Fix a bug in input processing for the "interval" type. Previously,
authorNeil Conway <neilc@samurai.com>
Tue, 29 May 2007 04:59:15 +0000 (04:59 +0000)
committerNeil Conway <neilc@samurai.com>
Tue, 29 May 2007 04:59:15 +0000 (04:59 +0000)
"microsecond" and "millisecond" units were not considered valid input
by themselves, which caused inputs like "1 millisecond" to be rejected
erroneously.

Update the docs, add regression tests, and backport to 8.2 and 8.1

doc/src/sgml/datatype.sgml
src/backend/utils/adt/datetime.c
src/include/utils/datetime.h
src/test/regress/expected/interval.out
src/test/regress/sql/interval.sql

index 73a1431cd6da5acac00082b3e448ed8d10b17f78..ff9953a5470c08959fc765bd66233d2ec6684427 100644 (file)
@@ -1822,7 +1822,8 @@ January 8 04:05:06 1999 PST
 </programlisting>
 
       Where: <replaceable>quantity</> is a number (possibly signed);
-      <replaceable>unit</> is <literal>second</literal>,
+      <replaceable>unit</> is <literal>microsecond</literal>,
+      <literal>millisecond</literal>, <literal>second</literal>,
       <literal>minute</literal>, <literal>hour</literal>, <literal>day</literal>,
       <literal>week</literal>, <literal>month</literal>, <literal>year</literal>,
       <literal>decade</literal>, <literal>century</literal>, <literal>millennium</literal>,
index b4a21059041de62ade4c760bc37bb1b05ed3352e..ec5b4727aca116bd22966afe36dc62c68c7cdac5 100644 (file)
@@ -922,6 +922,7 @@ DecodeDateTime(char **field, int *ftype, int nf,
 #else
                                                                *fsec = frac;
 #endif
+                                                               tmask = DTK_ALL_SECS_M;
                                                        }
                                                        break;
 
@@ -1697,6 +1698,7 @@ DecodeTimeOnly(char **field, int *ftype, int nf,
 #else
                                                                *fsec = frac;
 #endif
+                                                               tmask = DTK_ALL_SECS_M;
                                                        }
                                                        break;
 
@@ -2803,6 +2805,7 @@ DecodeInterval(char **field, int *ftype, int nf, int *dtype, struct pg_tm * tm,
 #else
                                                *fsec += (val + fval) * 1e-6;
 #endif
+                                               tmask = DTK_M(MICROSECOND);
                                                break;
 
                                        case DTK_MILLISEC:
@@ -2811,6 +2814,7 @@ DecodeInterval(char **field, int *ftype, int nf, int *dtype, struct pg_tm * tm,
 #else
                                                *fsec += (val + fval) * 1e-3;
 #endif
+                                               tmask = DTK_M(MILLISECOND);
                                                break;
 
                                        case DTK_SECOND:
@@ -2820,7 +2824,15 @@ DecodeInterval(char **field, int *ftype, int nf, int *dtype, struct pg_tm * tm,
 #else
                                                *fsec += fval;
 #endif
-                                               tmask = DTK_M(SECOND);
+                                               /*
+                                                * If any subseconds were specified, consider
+                                                * this microsecond and millisecond input as
+                                                * well.
+                                                */
+                                               if (fval == 0)
+                                                       tmask = DTK_M(SECOND);
+                                               else
+                                                       tmask = DTK_ALL_SECS_M;
                                                break;
 
                                        case DTK_MINUTE:
index b1cb7819a3ae22661e0a8d1285f75eba206f84d0..137a5aa91f1f693fdd8e49eb4c1f0ff1a524648c 100644 (file)
 #define HOUR   10
 #define MINUTE 11
 #define SECOND 12
-#define DOY            13
-#define DOW            14
-#define UNITS  15
-#define ADBC   16
+#define MILLISECOND 13
+#define MICROSECOND 14
+#define DOY            15
+#define DOW            16
+#define UNITS  17
+#define ADBC   18
 /* these are only for relative dates */
-#define AGO            17
-#define ABS_BEFORE             18
-#define ABS_AFTER              19
+#define AGO            19
+#define ABS_BEFORE             20
+#define ABS_AFTER              21
 /* generic fields to help with parsing */
-#define ISODATE 20
-#define ISOTIME 21
+#define ISODATE 22
+#define ISOTIME 23
 /* reserved for unrecognized string values */
 #define UNKNOWN_FIELD  31
 
 
 #define DTK_M(t)               (0x01 << (t))
 
+/* Convenvience: a second, plus any fractional component */
+#define DTK_ALL_SECS_M (DTK_M(SECOND) | DTK_M(MILLISECOND) | DTK_M(MICROSECOND))
 #define DTK_DATE_M             (DTK_M(YEAR) | DTK_M(MONTH) | DTK_M(DAY))
-#define DTK_TIME_M             (DTK_M(HOUR) | DTK_M(MINUTE) | DTK_M(SECOND))
+#define DTK_TIME_M             (DTK_M(HOUR) | DTK_M(MINUTE) | DTK_ALL_SECS_M)
 
 #define MAXDATELEN             63              /* maximum possible length of an input date
                                                                 * string (not counting tr. null) */
index f7c35deca1a6d557e537998d3db50abfdfd31c7c..72a031df5f1d9f1d08e1b186fa0749690e6ad544 100644 (file)
@@ -326,3 +326,26 @@ SELECT justify_interval(interval '1 month -1 hour') as "1 month -1 hour";
  @ 29 days 23 hours
 (1 row)
 
+-- test fractional second input, and detection of duplicate units
+SET DATESTYLE = 'ISO';
+SELECT '1 millisecond'::interval, '1 microsecond'::interval,
+       '500 seconds 99 milliseconds 51 microseconds'::interval;
+   interval   |    interval     |    interval     
+--------------+-----------------+-----------------
+ 00:00:00.001 | 00:00:00.000001 | 00:08:20.099051
+(1 row)
+
+SELECT '3 days 5 milliseconds'::interval;
+      interval       
+---------------------
+ 3 days 00:00:00.005
+(1 row)
+
+SELECT '1 second 2 seconds'::interval;              -- error
+ERROR:  invalid input syntax for type interval: "1 second 2 seconds"
+SELECT '10 milliseconds 20 milliseconds'::interval; -- error
+ERROR:  invalid input syntax for type interval: "10 milliseconds 20 milliseconds"
+SELECT '5.5 seconds 3 milliseconds'::interval;      -- error
+ERROR:  invalid input syntax for type interval: "5.5 seconds 3 milliseconds"
+SELECT '1:20:05 5 microseconds'::interval;          -- error
+ERROR:  invalid input syntax for type interval: "1:20:05 5 microseconds"
index 9b2e62514dbbc5bb40b43c5d43574727aafe46ee..d081bf1ffedf2d504f2c422e49abd9a2a6a48045 100644 (file)
@@ -116,3 +116,14 @@ SELECT justify_days(interval '6 months 36 days 5 hours 4 minutes 3 seconds') as
 -- test justify_interval()
 
 SELECT justify_interval(interval '1 month -1 hour') as "1 month -1 hour";
+
+-- test fractional second input, and detection of duplicate units
+SET DATESTYLE = 'ISO';
+SELECT '1 millisecond'::interval, '1 microsecond'::interval,
+       '500 seconds 99 milliseconds 51 microseconds'::interval;
+SELECT '3 days 5 milliseconds'::interval;
+
+SELECT '1 second 2 seconds'::interval;              -- error
+SELECT '10 milliseconds 20 milliseconds'::interval; -- error
+SELECT '5.5 seconds 3 milliseconds'::interval;      -- error
+SELECT '1:20:05 5 microseconds'::interval;          -- error
\ No newline at end of file