Skip to content

Commit bb49e35

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 ae47eb1 commit bb49e35

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
@@ -2874,19 +2874,18 @@ DecodeInterval(char **field, int *ftype, int nf, int range,
28742874
case DTK_TZ:
28752875

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

28832883
/*
2884-
* Try for hh:mm or hh:mm:ss. If not, fall through to
2885-
* DTK_NUMBER case, which can handle signed float numbers and
2886-
* signed year-month values.
2884+
* Check for signed hh:mm or hh:mm:ss. If so, process exactly
2885+
* like DTK_TIME case above, plus handling the sign.
28872886
*/
28882887
if (strchr(field[i] + 1, ':') != NULL &&
2889-
DecodeTime(field[i] + 1, fmask, INTERVAL_FULL_RANGE,
2888+
DecodeTime(field[i] + 1, fmask, range,
28902889
&tmask, tm, fsec) == 0)
28912890
{
28922891
if (*field[i] == '-')
@@ -2904,9 +2903,14 @@ DecodeInterval(char **field, int *ftype, int nf, int range,
29042903
* are reading right to left.
29052904
*/
29062905
type = DTK_DAY;
2907-
tmask = DTK_M(TZ);
29082906
break;
29092907
}
2908+
2909+
/*
2910+
* Otherwise, fall through to DTK_NUMBER case, which can
2911+
* handle signed float numbers and signed year-month values.
2912+
*/
2913+
29102914
/* FALL THROUGH */
29112915

29122916
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)