OSDN Git Service

Clean up a couple of weird corner cases in interval parsing: make -yyyy-mm be
authorTom Lane <tgl@sss.pgh.pa.us>
Tue, 16 Sep 2008 22:31:21 +0000 (22:31 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Tue, 16 Sep 2008 22:31:21 +0000 (22:31 +0000)
interpreted as expected (the sign should affect months too), and get rid of
hard-wired assumption that unmarked signed values must be hours (if integers)
or seconds (if floats).  The former was just a bug in my previous patch,
while the latter may have made sense at one time but seems illogical now
that we support determination of the units from typmod information.
Ron Mayer and myself.

src/backend/utils/adt/datetime.c
src/test/regress/expected/interval.out
src/test/regress/sql/interval.sql

index a1cf2b4..9640684 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/utils/adt/datetime.c,v 1.192 2008/09/11 15:27:30 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/utils/adt/datetime.c,v 1.193 2008/09/16 22:31:21 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -448,7 +448,7 @@ TrimTrailingZeros(char *str)
  *     DTK_TIME - digits, colon delimiters, and possibly a decimal point
  *     DTK_STRING - text (no digits or punctuation)
  *     DTK_SPECIAL - leading "+" or "-" followed by text
- *     DTK_TZ - leading "+" or "-" followed by digits (also eats ':' or '.')
+ *     DTK_TZ - leading "+" or "-" followed by digits (also eats ':', '.', '-')
  *
  * Note that some field types can hold unexpected items:
  *     DTK_NUMBER can hold date fields (yy.ddd)
@@ -610,12 +610,13 @@ ParseDateTime(const char *timestr, char *workbuf, size_t buflen,
                        while (isspace((unsigned char) *cp))
                                cp++;
                        /* numeric timezone? */
+                       /* note that "DTK_TZ" could also be a signed float or yyyy-mm */
                        if (isdigit((unsigned char) *cp))
                        {
                                ftype[nf] = DTK_TZ;
                                APPEND_CHAR(bufp, bufend, *cp++);
                                while (isdigit((unsigned char) *cp) ||
-                                          *cp == ':' || *cp == '.')
+                                          *cp == ':' || *cp == '.' || *cp == '-')
                                        APPEND_CHAR(bufp, bufend, *cp++);
                        }
                        /* special? */
@@ -2774,19 +2775,17 @@ DecodeInterval(char **field, int *ftype, int nf, int range,
 
                                /*
                                 * Timezone is a token with a leading sign character and
-                                * otherwise the same as a non-signed time field
+                                * at least one digit; there could be ':', '.', '-'
+                                * embedded in it as well.
                                 */
                                Assert(*field[i] == '-' || *field[i] == '+');
 
                                /*
-                                * A single signed number ends up here, but will be rejected
-                                * by DecodeTime(). So, work this out to drop through to
-                                * DTK_NUMBER, which *can* tolerate this.
+                                * Try for hh:mm or hh:mm:ss.  If not, fall through to
+                                * DTK_NUMBER case, which can handle signed float numbers
+                                * and signed year-month values.
                                 */
-                               cp = field[i] + 1;
-                               while (*cp != '\0' && *cp != ':' && *cp != '.')
-                                       cp++;
-                               if (*cp == ':' &&
+                               if (strchr(field[i] + 1, ':') != NULL &&
                                        DecodeTime(field[i] + 1, fmask, INTERVAL_FULL_RANGE,
                                                           &tmask, tm, fsec) == 0)
                                {
@@ -2808,32 +2807,13 @@ DecodeInterval(char **field, int *ftype, int nf, int range,
                                        tmask = DTK_M(TZ);
                                        break;
                                }
-                               else if (type == IGNORE_DTF)
-                               {
-                                       if (*cp == '.')
-                                       {
-                                               /*
-                                                * Got a decimal point? Then assume some sort of
-                                                * seconds specification
-                                                */
-                                               type = DTK_SECOND;
-                                       }
-                                       else if (*cp == '\0')
-                                       {
-                                               /*
-                                                * Only a signed integer? Then must assume a
-                                                * timezone-like usage
-                                                */
-                                               type = DTK_HOUR;
-                                       }
-                               }
                                /* FALL THROUGH */
 
                        case DTK_DATE:
                        case DTK_NUMBER:
                                if (type == IGNORE_DTF)
                                {
-                                       /* use typmod to decide what rightmost integer field is */
+                                       /* use typmod to decide what rightmost field is */
                                        switch (range)
                                        {
                                                case INTERVAL_MASK(YEAR):
@@ -2883,6 +2863,8 @@ DecodeInterval(char **field, int *ftype, int nf, int range,
                                        if (*cp != '\0')
                                                return DTERR_BAD_FORMAT;
                                        type = DTK_MONTH;
+                                       if (val < 0)
+                                               val2 = -val2;
                                        val = val * MONTHS_PER_YEAR + val2;
                                        fval = 0;
                                }
index a9b5766..8270516 100644 (file)
@@ -21,12 +21,6 @@ SELECT INTERVAL '-08:00' AS "Eight hours";
  -08:00:00
 (1 row)
 
-SELECT INTERVAL '-05' AS "Five hours";
- Five hours 
-------------
- -05:00:00
-(1 row)
-
 SELECT INTERVAL '-1 +02:03' AS "22 hours ago...";
   22 hours ago...  
 -------------------
index 06e4705..732ca02 100644 (file)
@@ -8,7 +8,6 @@ SET DATESTYLE = 'ISO';
 SELECT INTERVAL '01:00' AS "One hour";
 SELECT INTERVAL '+02:00' AS "Two hours";
 SELECT INTERVAL '-08:00' AS "Eight hours";
-SELECT INTERVAL '-05' AS "Five hours";
 SELECT INTERVAL '-1 +02:03' AS "22 hours ago...";
 SELECT INTERVAL '-1 days +02:03' AS "22 hours ago...";
 SELECT INTERVAL '1.5 weeks' AS "Ten days twelve hours";