Fix a bug in input processing for the "interval" type. Previously,
authorNeil Conway <neilc@samurai.com>
Tue, 29 May 2007 04:59:44 +0000 (04:59 +0000)
committerNeil Conway <neilc@samurai.com>
Tue, 29 May 2007 04:59:44 +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 c3436933316cb7220162934613ef8c0cef55157b..ed6269217f30dcd2ed4894014bdda009d23c6503 100644 (file)
@@ -1880,7 +1880,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 b0f2abfe8082101ecc70f36b1e0297f84bf3256b..68dee2362bd38d1d882cde058072e4c430ea1ec7 100644 (file)
@@ -924,6 +924,7 @@ DecodeDateTime(char **field, int *ftype, int nf,
 #else
                                                                *fsec = frac;
 #endif
+                                                               tmask = DTK_ALL_SECS_M;
                                                        }
                                                        break;
 
@@ -1699,6 +1700,7 @@ DecodeTimeOnly(char **field, int *ftype, int nf,
 #else
                                                                *fsec = frac;
 #endif
+                                                               tmask = DTK_ALL_SECS_M;
                                                        }
                                                        break;
 
@@ -2805,6 +2807,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:
@@ -2813,6 +2816,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:
@@ -2822,7 +2826,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 750fe53507a626fffb330e6c8c5bda6213294ee6..3e9f59b2984b9faa694f948aeb204eef88db0cee 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