Fix a bug in input processing for the "interval" type. Previously,
authorNeil Conway <neilc@samurai.com>
Tue, 29 May 2007 04:58:43 +0000 (04:58 +0000)
committerNeil Conway <neilc@samurai.com>
Tue, 29 May 2007 04:58:43 +0000 (04:58 +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 6b72e80e569905de6a8d455989334695c55bc9b3..17357d8408850c10bf8e9861ed5bce92c2c7a337 100644 (file)
@@ -1,4 +1,4 @@
-<!-- $PostgreSQL: pgsql/doc/src/sgml/datatype.sgml,v 1.201 2007/05/21 17:10:28 petere Exp $ -->
+<!-- $PostgreSQL: pgsql/doc/src/sgml/datatype.sgml,v 1.202 2007/05/29 04:58:43 neilc Exp $ -->
 
  <chapter id="datatype">
   <title id="datatype-title">Data Types</title>
@@ -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 9d1fdd5f40c571e321da4d6648bfeafe659c1b01..b5cc2461589c98b1a8aeb64db0666a059e6a09e1 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/utils/adt/datetime.c,v 1.179 2007/05/27 20:32:16 neilc Exp $
+ *   $PostgreSQL: pgsql/src/backend/utils/adt/datetime.c,v 1.180 2007/05/29 04:58:43 neilc Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -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 df6cf5e4f308f92cb78620a5e90d6e3baf133439..34b88ad5c7a9e111f60c1fd1a871acc3c05daaba 100644 (file)
@@ -9,7 +9,7 @@
  * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/utils/datetime.h,v 1.65 2007/02/19 17:41:39 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/utils/datetime.h,v 1.66 2007/05/29 04:58:43 neilc Exp $
  *
  *-------------------------------------------------------------------------
  */
 #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