aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNeil Conway <neilc@samurai.com>2007-05-29 04:59:15 +0000
committerNeil Conway <neilc@samurai.com>2007-05-29 04:59:15 +0000
commit75103cc2189d8455dfb6904b01c4a96449518ee9 (patch)
tree2072e15fca5bfedbaefe0eaec127a31ec53c72b7
parent7b6b5ba9b289510b71313cc250db45c3fd38d8cd (diff)
downloadpostgresql-75103cc2189d8455dfb6904b01c4a96449518ee9.tar.gz
postgresql-75103cc2189d8455dfb6904b01c4a96449518ee9.zip
Fix a bug in input processing for the "interval" type. Previously,
"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
-rw-r--r--doc/src/sgml/datatype.sgml5
-rw-r--r--src/backend/utils/adt/datetime.c16
-rw-r--r--src/include/utils/datetime.h26
-rw-r--r--src/test/regress/expected/interval.out23
-rw-r--r--src/test/regress/sql/interval.sql11
5 files changed, 66 insertions, 15 deletions
diff --git a/doc/src/sgml/datatype.sgml b/doc/src/sgml/datatype.sgml
index 029dec3dcd4..bef7ea083c2 100644
--- a/doc/src/sgml/datatype.sgml
+++ b/doc/src/sgml/datatype.sgml
@@ -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>,
diff --git a/src/backend/utils/adt/datetime.c b/src/backend/utils/adt/datetime.c
index f72d1c6b403..5a1613ef7d0 100644
--- a/src/backend/utils/adt/datetime.c
+++ b/src/backend/utils/adt/datetime.c
@@ -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:
diff --git a/src/include/utils/datetime.h b/src/include/utils/datetime.h
index 60a7c159a94..5b3a601c111 100644
--- a/src/include/utils/datetime.h
+++ b/src/include/utils/datetime.h
@@ -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 $
*
*-------------------------------------------------------------------------
*/
@@ -100,17 +100,19 @@
#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
@@ -173,8 +175,10 @@
#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) */
diff --git a/src/test/regress/expected/interval.out b/src/test/regress/expected/interval.out
index f7c35deca1a..72a031df5f1 100644
--- a/src/test/regress/expected/interval.out
+++ b/src/test/regress/expected/interval.out
@@ -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"
diff --git a/src/test/regress/sql/interval.sql b/src/test/regress/sql/interval.sql
index 9b2e62514db..d081bf1ffed 100644
--- a/src/test/regress/sql/interval.sql
+++ b/src/test/regress/sql/interval.sql
@@ -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