]> 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: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 8e353aaf7025c05ec8429298ac0528b8b64ddd65..072c96d44f4e3a133cf2dcad91039653b6416897 100644 (file)
@@ -1,5 +1,5 @@
 <!--
-$PostgreSQL: pgsql/doc/src/sgml/datatype.sgml,v 1.163.2.3 2006/08/21 16:24:02 tgl Exp $
+$PostgreSQL: pgsql/doc/src/sgml/datatype.sgml,v 1.163.2.4 2007/05/29 04:59:44 neilc Exp $
 -->
 
  <chapter id="datatype">
@@ -1823,7 +1823,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 63bc43debf9f2db07d7b0468f91b8517489e2e0d..bfb9564397f73a850cc86bb08eab534d4e575e47 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/utils/adt/datetime.c,v 1.160.2.2 2005/12/01 17:56:43 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/utils/adt/datetime.c,v 1.160.2.3 2007/05/29 04:59:44 neilc Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1240,6 +1240,7 @@ DecodeDateTime(char **field, int *ftype, int nf,
 #else
                                                                *fsec = frac;
 #endif
+                                                               tmask = DTK_ALL_SECS_M;
                                                        }
                                                        break;
 
@@ -2000,6 +2001,7 @@ DecodeTimeOnly(char **field, int *ftype, int nf,
 #else
                                                                *fsec = frac;
 #endif
+                                                               tmask = DTK_ALL_SECS_M;
                                                        }
                                                        break;
 
@@ -3117,6 +3119,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:
@@ -3125,6 +3128,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:
@@ -3134,7 +3138,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 48b158e050b1bcda7952b75bc8c4687cb72de1a9..e8e26533766ddedf95d4bf4ec1a9d18e0b8f0b12 100644 (file)
@@ -9,7 +9,7 @@
  * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/utils/datetime.h,v 1.57 2005/10/15 02:49:46 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/utils/datetime.h,v 1.57.2.1 2007/05/29 04:59:44 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             51              /* maximum possible length of an input date
                                                                 * string (not counting tr. null) */
index d07dd3013dc4ffa746e58ea4695a0033c1d39b26..17b18a1a0f9f7cde07e42ee6467beaa480411a58 100644 (file)
@@ -241,3 +241,26 @@ SELECT justify_days(interval '6 months 36 days 5 hours 4 minutes 3 seconds') as
  @ 7 mons 6 days 5 hours 4 mins 3 secs
 (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 25e58659e7dd5f6f0b43f7db5ff5346c30495f7f..920ce8a09a49b716b0d801a5ccf2048ac2c8f4c6 100644 (file)
@@ -76,3 +76,13 @@ select '4 millenniums 5 centuries 4 decades 1 year 4 months 4 days 17 minutes 31
 SELECT justify_hours(interval '6 months 3 days 52 hours 3 minutes 2 seconds') as "6 mons 5 days 4 hours 3 mins 2 seconds";
 SELECT justify_days(interval '6 months 36 days 5 hours 4 minutes 3 seconds') as "7 mons 6 days 5 hours 4 mins 3 seconds";
 
+-- 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