]> granicus.if.org Git - postgresql/commitdiff
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 029dec3dcd48a9225c0d3db3acb3a82dd3407b77..bef7ea083c237b0ae357cf4b583e18de2608cf5f 100644 (file)
@@ -1,4 +1,4 @@
-<!-- $PostgreSQL: pgsql/doc/src/sgml/datatype.sgml,v 1.181.2.2 2007/03/14 17:38:15 tgl Exp $ -->
+<!-- $PostgreSQL: pgsql/doc/src/sgml/datatype.sgml,v 1.181.2.3 2007/05/29 04:59:12 neilc Exp $ -->
 
  <chapter id="datatype">
   <title id="datatype-title">Data Types</title>
@@ -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 f72d1c6b403a3d0856a238d9526fcc7ac6f68822..5a1613ef7d0c88ef41a4032831a266a809fb3658 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/utils/adt/datetime.c,v 1.174 2006/10/18 16:43:13 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/utils/adt/datetime.c,v 1.174.2.1 2007/05/29 04:59:13 neilc Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -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 60a7c159a94f8b0e8a1940d641aa6585c806bba0..5b3a601c11115b93f29cd79b803dac1a9dd0733f 100644 (file)
@@ -9,7 +9,7 @@
  * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/utils/datetime.h,v 1.62 2006/10/18 16:43:14 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/utils/datetime.h,v 1.62.2.1 2007/05/29 04:59:14 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