]> granicus.if.org Git - postgresql/commitdiff
Fix bugs with parsing signed hh:mm and hh:mm:ss fields in interval input.
authorTom Lane <tgl@sss.pgh.pa.us>
Fri, 3 Aug 2012 21:39:39 +0000 (17:39 -0400)
committerTom Lane <tgl@sss.pgh.pa.us>
Fri, 3 Aug 2012 21:40:43 +0000 (17:40 -0400)
DecodeInterval() failed to honor the "range" parameter (the special SQL
syntax for indicating which fields appear in the literal string) if the
time was signed.  This seems inappropriate, so make it work like the
not-signed case.  The inconsistency was introduced in my commit
f867339c0148381eb1d01f93ab5c79f9d10211de, which as noted in its log message
was only really focused on making SQL-compliant literals work per spec.
Including a sign here is not per spec, but if we're going to allow it
then it's reasonable to expect it to work like the not-signed case.

Also, remove bogus setting of tmask, which caused subsequent processing to
think that what had been given was a timezone and not an hh:mm(:ss) field,
thus confusing checks for redundant fields.  This seems to be an aboriginal
mistake in Lockhart's commit 2cf1642461536d0d8f3a1cf124ead0eac04eb760.

Add regression test cases to illustrate the changed behaviors.

Back-patch as far as 8.4, where support for spec-compliant interval
literals was added.

Range problem reported and diagnosed by Amit Kapila, tmask problem by me.

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

index 1c2c39b2e272ca54506e6c220ced07918aacaaf5..26c6742e8b10d8476589873cbcb38f25633079c9 100644 (file)
@@ -2873,19 +2873,18 @@ DecodeInterval(char **field, int *ftype, int nf, int range,
                        case DTK_TZ:
 
                                /*
-                                * Timezone is a token with a leading sign character and at
+                                * Timezone means a token with a leading sign character and at
                                 * least one digit; there could be ':', '.', '-' embedded in
                                 * it as well.
                                 */
                                Assert(*field[i] == '-' || *field[i] == '+');
 
                                /*
-                                * 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.
+                                * Check for signed hh:mm or hh:mm:ss.  If so, process exactly
+                                * like DTK_TIME case above, plus handling the sign.
                                 */
                                if (strchr(field[i] + 1, ':') != NULL &&
-                                       DecodeTime(field[i] + 1, fmask, INTERVAL_FULL_RANGE,
+                                       DecodeTime(field[i] + 1, fmask, range,
                                                           &tmask, tm, fsec) == 0)
                                {
                                        if (*field[i] == '-')
@@ -2903,9 +2902,14 @@ DecodeInterval(char **field, int *ftype, int nf, int range,
                                         * are reading right to left.
                                         */
                                        type = DTK_DAY;
-                                       tmask = DTK_M(TZ);
                                        break;
                                }
+
+                               /*
+                                * Otherwise, fall through to DTK_NUMBER case, which can
+                                * handle signed float numbers and signed year-month values.
+                                */
+
                                /* FALL THROUGH */
 
                        case DTK_DATE:
index e7e21813332148d7b2b72f979c3bfd7d1796e855..3bf221187bc49d75ed4c10e42e8313456a6b94d7 100644 (file)
@@ -545,6 +545,30 @@ SELECT interval '1 2:03:04' minute to second;
  1 day 02:03:04
 (1 row)
 
+SELECT interval '1 +2:03' minute to second;
+    interval    
+----------------
+ 1 day 00:02:03
+(1 row)
+
+SELECT interval '1 +2:03:04' minute to second;
+    interval    
+----------------
+ 1 day 02:03:04
+(1 row)
+
+SELECT interval '1 -2:03' minute to second;
+    interval     
+-----------------
+ 1 day -00:02:03
+(1 row)
+
+SELECT interval '1 -2:03:04' minute to second;
+    interval     
+-----------------
+ 1 day -02:03:04
+(1 row)
+
 SELECT interval '123 11' day to hour; -- ok
      interval      
 -------------------
@@ -559,6 +583,10 @@ SELECT interval '123 11'; -- not ok, too ambiguous
 ERROR:  invalid input syntax for type interval: "123 11"
 LINE 1: SELECT interval '123 11';
                         ^
+SELECT interval '123 2:03 -2:04'; -- not ok, redundant hh:mm fields
+ERROR:  invalid input syntax for type interval: "123 2:03 -2:04"
+LINE 1: SELECT interval '123 2:03 -2:04';
+                        ^
 -- test syntaxes for restricted precision
 SELECT interval(0) '1 day 01:23:45.6789';
     interval    
index 9da2dc63e8712176da29f53fa7c38eeb9ed84f8f..f1da4c2911775212300bf394a0d8b9992264af39 100644 (file)
@@ -165,9 +165,14 @@ SELECT interval '1 2:03:04' hour to second;
 SELECT interval '1 2' minute to second;
 SELECT interval '1 2:03' minute to second;
 SELECT interval '1 2:03:04' minute to second;
+SELECT interval '1 +2:03' minute to second;
+SELECT interval '1 +2:03:04' minute to second;
+SELECT interval '1 -2:03' minute to second;
+SELECT interval '1 -2:03:04' minute to second;
 SELECT interval '123 11' day to hour; -- ok
 SELECT interval '123 11' day; -- not ok
 SELECT interval '123 11'; -- not ok, too ambiguous
+SELECT interval '123 2:03 -2:04'; -- not ok, redundant hh:mm fields
 
 -- test syntaxes for restricted precision
 SELECT interval(0) '1 day 01:23:45.6789';