Skip to content

Commit 805fc21

Browse files
committed
Fix bugs with parsing signed hh:mm and hh:mm:ss fields in interval input.
DecodeInterval() failed to honor the "range" parameter (the special SQL syntax for indicating which fields appear in the literal string) if the time was signed. This seems inappropriate, so make it work like the not-signed case. The inconsistency was introduced in my commit f867339, which as noted in its log message was only really focused on making SQL-compliant literals work per spec. Including a sign here is not per spec, but if we're going to allow it then it's reasonable to expect it to work like the not-signed case. Also, remove bogus setting of tmask, which caused subsequent processing to think that what had been given was a timezone and not an hh:mm(:ss) field, thus confusing checks for redundant fields. This seems to be an aboriginal mistake in Lockhart's commit 2cf1642. Add regression test cases to illustrate the changed behaviors. Back-patch as far as 8.4, where support for spec-compliant interval literals was added. Range problem reported and diagnosed by Amit Kapila, tmask problem by me.
1 parent d06dfc1 commit 805fc21

File tree

3 files changed

+43
-6
lines changed

3 files changed

+43
-6
lines changed

src/backend/utils/adt/datetime.c

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2877,19 +2877,18 @@ DecodeInterval(char **field, int *ftype, int nf, int range,
28772877
case DTK_TZ:
28782878

28792879
/*
2880-
* Timezone is a token with a leading sign character and at
2880+
* Timezone means a token with a leading sign character and at
28812881
* least one digit; there could be ':', '.', '-' embedded in
28822882
* it as well.
28832883
*/
28842884
Assert(*field[i] == '-' || *field[i] == '+');
28852885

28862886
/*
2887-
* Try for hh:mm or hh:mm:ss. If not, fall through to
2888-
* DTK_NUMBER case, which can handle signed float numbers and
2889-
* signed year-month values.
2887+
* Check for signed hh:mm or hh:mm:ss. If so, process exactly
2888+
* like DTK_TIME case above, plus handling the sign.
28902889
*/
28912890
if (strchr(field[i] + 1, ':') != NULL &&
2892-
DecodeTime(field[i] + 1, fmask, INTERVAL_FULL_RANGE,
2891+
DecodeTime(field[i] + 1, fmask, range,
28932892
&tmask, tm, fsec) == 0)
28942893
{
28952894
if (*field[i] == '-')
@@ -2907,9 +2906,14 @@ DecodeInterval(char **field, int *ftype, int nf, int range,
29072906
* are reading right to left.
29082907
*/
29092908
type = DTK_DAY;
2910-
tmask = DTK_M(TZ);
29112909
break;
29122910
}
2911+
2912+
/*
2913+
* Otherwise, fall through to DTK_NUMBER case, which can
2914+
* handle signed float numbers and signed year-month values.
2915+
*/
2916+
29132917
/* FALL THROUGH */
29142918

29152919
case DTK_DATE:

src/test/regress/expected/interval.out

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -545,6 +545,30 @@ SELECT interval '1 2:03:04' minute to second;
545545
1 day 02:03:04
546546
(1 row)
547547

548+
SELECT interval '1 +2:03' minute to second;
549+
interval
550+
----------------
551+
1 day 00:02:03
552+
(1 row)
553+
554+
SELECT interval '1 +2:03:04' minute to second;
555+
interval
556+
----------------
557+
1 day 02:03:04
558+
(1 row)
559+
560+
SELECT interval '1 -2:03' minute to second;
561+
interval
562+
-----------------
563+
1 day -00:02:03
564+
(1 row)
565+
566+
SELECT interval '1 -2:03:04' minute to second;
567+
interval
568+
-----------------
569+
1 day -02:03:04
570+
(1 row)
571+
548572
SELECT interval '123 11' day to hour; -- ok
549573
interval
550574
-------------------
@@ -559,6 +583,10 @@ SELECT interval '123 11'; -- not ok, too ambiguous
559583
ERROR: invalid input syntax for type interval: "123 11"
560584
LINE 1: SELECT interval '123 11';
561585
^
586+
SELECT interval '123 2:03 -2:04'; -- not ok, redundant hh:mm fields
587+
ERROR: invalid input syntax for type interval: "123 2:03 -2:04"
588+
LINE 1: SELECT interval '123 2:03 -2:04';
589+
^
562590
-- test syntaxes for restricted precision
563591
SELECT interval(0) '1 day 01:23:45.6789';
564592
interval

src/test/regress/sql/interval.sql

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,9 +165,14 @@ SELECT interval '1 2:03:04' hour to second;
165165
SELECT interval '1 2' minute to second;
166166
SELECT interval '1 2:03' minute to second;
167167
SELECT interval '1 2:03:04' minute to second;
168+
SELECT interval '1 +2:03' minute to second;
169+
SELECT interval '1 +2:03:04' minute to second;
170+
SELECT interval '1 -2:03' minute to second;
171+
SELECT interval '1 -2:03:04' minute to second;
168172
SELECT interval '123 11' day to hour; -- ok
169173
SELECT interval '123 11' day; -- not ok
170174
SELECT interval '123 11'; -- not ok, too ambiguous
175+
SELECT interval '123 2:03 -2:04'; -- not ok, redundant hh:mm fields
171176

172177
-- test syntaxes for restricted precision
173178
SELECT interval(0) '1 day 01:23:45.6789';

0 commit comments

Comments
 (0)