diff options
Diffstat (limited to 'src/backend/utils/adt')
-rw-r--r-- | src/backend/utils/adt/datetime.c | 85 | ||||
-rw-r--r-- | src/backend/utils/adt/nabstime.c | 5 | ||||
-rw-r--r-- | src/backend/utils/adt/timestamp.c | 12 |
3 files changed, 85 insertions, 17 deletions
diff --git a/src/backend/utils/adt/datetime.c b/src/backend/utils/adt/datetime.c index a92583ca19e..6712d6d8d44 100644 --- a/src/backend/utils/adt/datetime.c +++ b/src/backend/utils/adt/datetime.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/adt/datetime.c,v 1.190 2008/06/09 19:34:02 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/utils/adt/datetime.c,v 1.191 2008/09/10 18:29:41 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -35,8 +35,8 @@ static int DecodeNumber(int flen, char *field, bool haveTextMonth, static int DecodeNumberField(int len, char *str, int fmask, int *tmask, struct pg_tm * tm, fsec_t *fsec, bool *is2digits); -static int DecodeTime(char *str, int fmask, int *tmask, - struct pg_tm * tm, fsec_t *fsec); +static int DecodeTime(char *str, int fmask, int range, + int *tmask, struct pg_tm * tm, fsec_t *fsec); static int DecodeTimezone(char *str, int *tzp); static const datetkn *datebsearch(const char *key, const datetkn *base, int nel); static int DecodeDate(char *str, int fmask, int *tmask, bool *is2digits, @@ -832,7 +832,8 @@ DecodeDateTime(char **field, int *ftype, int nf, break; case DTK_TIME: - dterr = DecodeTime(field[i], fmask, &tmask, tm, fsec); + dterr = DecodeTime(field[i], fmask, INTERVAL_FULL_RANGE, + &tmask, tm, fsec); if (dterr) return dterr; @@ -1563,6 +1564,7 @@ DecodeTimeOnly(char **field, int *ftype, int nf, case DTK_TIME: dterr = DecodeTime(field[i], (fmask | DTK_DATE_M), + INTERVAL_FULL_RANGE, &tmask, tm, fsec); if (dterr) return dterr; @@ -2224,7 +2226,8 @@ ValidateDate(int fmask, bool is2digits, bool bc, struct pg_tm * tm) * used to represent time spans. */ static int -DecodeTime(char *str, int fmask, int *tmask, struct pg_tm * tm, fsec_t *fsec) +DecodeTime(char *str, int fmask, int range, + int *tmask, struct pg_tm * tm, fsec_t *fsec) { char *cp; @@ -2245,6 +2248,13 @@ DecodeTime(char *str, int fmask, int *tmask, struct pg_tm * tm, fsec_t *fsec) { tm->tm_sec = 0; *fsec = 0; + /* If it's a MINUTE TO SECOND interval, take 2 fields as being mm:ss */ + if (range == (INTERVAL_MASK(MINUTE) | INTERVAL_MASK(SECOND))) + { + tm->tm_sec = tm->tm_min; + tm->tm_min = tm->tm_hour; + tm->tm_hour = 0; + } } else if (*cp != ':') return DTERR_BAD_FORMAT; @@ -2705,7 +2715,8 @@ DecodeSpecial(int field, char *lowtoken, int *val) * preceding an hh:mm:ss field. - thomas 1998-04-30 */ int -DecodeInterval(char **field, int *ftype, int nf, int *dtype, struct pg_tm * tm, fsec_t *fsec) +DecodeInterval(char **field, int *ftype, int nf, int range, + int *dtype, struct pg_tm * tm, fsec_t *fsec) { bool is_before = FALSE; char *cp; @@ -2734,7 +2745,8 @@ DecodeInterval(char **field, int *ftype, int nf, int *dtype, struct pg_tm * tm, switch (ftype[i]) { case DTK_TIME: - dterr = DecodeTime(field[i], fmask, &tmask, tm, fsec); + dterr = DecodeTime(field[i], fmask, range, + &tmask, tm, fsec); if (dterr) return dterr; type = DTK_DAY; @@ -2757,7 +2769,8 @@ DecodeInterval(char **field, int *ftype, int nf, int *dtype, struct pg_tm * tm, while (*cp != '\0' && *cp != ':' && *cp != '.') cp++; if (*cp == ':' && - DecodeTime(field[i] + 1, fmask, &tmask, tm, fsec) == 0) + DecodeTime(field[i] + 1, fmask, INTERVAL_FULL_RANGE, + &tmask, tm, fsec) == 0) { if (*field[i] == '-') { @@ -2796,19 +2809,66 @@ DecodeInterval(char **field, int *ftype, int nf, int *dtype, struct pg_tm * tm, type = DTK_HOUR; } } - /* DROP THROUGH */ + /* FALL THROUGH */ case DTK_DATE: case DTK_NUMBER: + if (type == IGNORE_DTF) + { + /* use typmod to decide what rightmost integer field is */ + switch (range) + { + case INTERVAL_MASK(YEAR): + type = DTK_YEAR; + break; + case INTERVAL_MASK(MONTH): + case INTERVAL_MASK(YEAR) | INTERVAL_MASK(MONTH): + type = DTK_MONTH; + break; + case INTERVAL_MASK(DAY): + type = DTK_DAY; + break; + case INTERVAL_MASK(HOUR): + case INTERVAL_MASK(DAY) | INTERVAL_MASK(HOUR): + case INTERVAL_MASK(DAY) | INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE): + case INTERVAL_MASK(DAY) | INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE) | INTERVAL_MASK(SECOND): + type = DTK_HOUR; + break; + case INTERVAL_MASK(MINUTE): + case INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE): + type = DTK_MINUTE; + break; + case INTERVAL_MASK(SECOND): + case INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE) | INTERVAL_MASK(SECOND): + case INTERVAL_MASK(MINUTE) | INTERVAL_MASK(SECOND): + type = DTK_SECOND; + break; + default: + type = DTK_SECOND; + break; + } + } + errno = 0; val = strtoi(field[i], &cp, 10); if (errno == ERANGE) return DTERR_FIELD_OVERFLOW; - if (type == IGNORE_DTF) - type = DTK_SECOND; + if (*cp == '-') + { + /* SQL "years-months" syntax */ + int val2; - if (*cp == '.') + val2 = strtoi(cp + 1, &cp, 10); + if (errno == ERANGE || val2 < 0 || val2 >= MONTHS_PER_YEAR) + return DTERR_FIELD_OVERFLOW; + if (*cp != '\0') + return DTERR_BAD_FORMAT; + type = DTK_MONTH; + val = val * MONTHS_PER_YEAR + val2; + fval = 0; + } + else if (*cp == '.') { fval = strtod(cp, &cp); if (*cp != '\0') @@ -2896,6 +2956,7 @@ DecodeInterval(char **field, int *ftype, int nf, int *dtype, struct pg_tm * tm, #endif } tmask = DTK_M(HOUR); + type = DTK_DAY; break; case DTK_DAY: diff --git a/src/backend/utils/adt/nabstime.c b/src/backend/utils/adt/nabstime.c index a40ca5edd05..4a505c341e9 100644 --- a/src/backend/utils/adt/nabstime.c +++ b/src/backend/utils/adt/nabstime.c @@ -10,7 +10,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/adt/nabstime.c,v 1.155 2008/03/25 22:42:44 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/utils/adt/nabstime.c,v 1.156 2008/09/10 18:29:41 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -632,7 +632,8 @@ reltimein(PG_FUNCTION_ARGS) dterr = ParseDateTime(str, workbuf, sizeof(workbuf), field, ftype, MAXDATEFIELDS, &nf); if (dterr == 0) - dterr = DecodeInterval(field, ftype, nf, &dtype, tm, &fsec); + dterr = DecodeInterval(field, ftype, nf, INTERVAL_FULL_RANGE, + &dtype, tm, &fsec); if (dterr != 0) { if (dterr == DTERR_FIELD_OVERFLOW) diff --git a/src/backend/utils/adt/timestamp.c b/src/backend/utils/adt/timestamp.c index d6a5dee0842..9060b989f9c 100644 --- a/src/backend/utils/adt/timestamp.c +++ b/src/backend/utils/adt/timestamp.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/adt/timestamp.c,v 1.190 2008/07/07 18:09:46 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/utils/adt/timestamp.c,v 1.191 2008/09/10 18:29:41 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -604,6 +604,7 @@ interval_in(PG_FUNCTION_ARGS) *tm = &tt; int dtype; int nf; + int range; int dterr; char *field[MAXDATEFIELDS]; int ftype[MAXDATEFIELDS]; @@ -617,10 +618,15 @@ interval_in(PG_FUNCTION_ARGS) tm->tm_sec = 0; fsec = 0; + if (typmod >= 0) + range = INTERVAL_RANGE(typmod); + else + range = INTERVAL_FULL_RANGE; + dterr = ParseDateTime(str, workbuf, sizeof(workbuf), field, ftype, MAXDATEFIELDS, &nf); if (dterr == 0) - dterr = DecodeInterval(field, ftype, nf, &dtype, tm, &fsec); + dterr = DecodeInterval(field, ftype, nf, range, &dtype, tm, &fsec); if (dterr != 0) { if (dterr == DTERR_FIELD_OVERFLOW) @@ -945,7 +951,7 @@ AdjustIntervalForTypmod(Interval *interval, int32 typmod) * Unspecified range and precision? Then not necessary to adjust. Setting * typmod to -1 is the convention for all types. */ - if (typmod != -1) + if (typmod >= 0) { int range = INTERVAL_RANGE(typmod); int precision = INTERVAL_PRECISION(typmod); |