Skip to content

Commit 8028e29

Browse files
committed
Detect overflow in timestamp[tz] subtraction.
It's possible to overflow the int64 microseconds field of the output interval when subtracting two timestamps. Detect that instead of silently returning a bogus result. Nick Babadzhanian Discussion: https://postgr.es/m/CABw73Uq2oJ3E+kYvvDuY04EkhhkChim2e-PaghBDjOmgUAMWGw@mail.gmail.com
1 parent f0d0394 commit 8028e29

File tree

5 files changed

+30
-1
lines changed

5 files changed

+30
-1
lines changed

src/backend/utils/adt/timestamp.c

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2713,7 +2713,10 @@ timestamp_mi(PG_FUNCTION_ARGS)
27132713
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
27142714
errmsg("cannot subtract infinite timestamps")));
27152715

2716-
result->time = dt1 - dt2;
2716+
if (unlikely(pg_sub_s64_overflow(dt1, dt2, &result->time)))
2717+
ereport(ERROR,
2718+
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
2719+
errmsg("interval out of range")));
27172720

27182721
result->month = 0;
27192722
result->day = 0;

src/test/regress/expected/timestamp.out

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1207,6 +1207,15 @@ SELECT extract(epoch from '5000-01-01 00:00:00'::timestamp);
12071207
95617584000.000000
12081208
(1 row)
12091209

1210+
-- test edge-case overflow in timestamp subtraction
1211+
SELECT timestamp '294276-12-31 23:59:59' - timestamp '1999-12-23 19:59:04.224193' AS ok;
1212+
ok
1213+
-----------------------------------------
1214+
@ 106751991 days 4 hours 54.775807 secs
1215+
(1 row)
1216+
1217+
SELECT timestamp '294276-12-31 23:59:59' - timestamp '1999-12-23 19:59:04.224192' AS overflows;
1218+
ERROR: interval out of range
12101219
-- TO_CHAR()
12111220
SELECT to_char(d1, 'DAY Day day DY Dy dy MONTH Month month RM MON Mon mon')
12121221
FROM TIMESTAMP_TBL;

src/test/regress/expected/timestamptz.out

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1331,6 +1331,15 @@ SELECT extract(epoch from '5000-01-01 00:00:00+00'::timestamptz);
13311331
95617584000.000000
13321332
(1 row)
13331333

1334+
-- test edge-case overflow in timestamp subtraction
1335+
SELECT timestamptz '294276-12-31 23:59:59 UTC' - timestamptz '1999-12-23 19:59:04.224193 UTC' AS ok;
1336+
ok
1337+
-----------------------------------------
1338+
@ 106751991 days 4 hours 54.775807 secs
1339+
(1 row)
1340+
1341+
SELECT timestamptz '294276-12-31 23:59:59 UTC' - timestamptz '1999-12-23 19:59:04.224192 UTC' AS overflows;
1342+
ERROR: interval out of range
13341343
-- TO_CHAR()
13351344
SELECT to_char(d1, 'DAY Day day DY Dy dy MONTH Month month RM MON Mon mon')
13361345
FROM TIMESTAMPTZ_TBL;

src/test/regress/sql/timestamp.sql

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -326,6 +326,10 @@ SELECT extract(epoch from '294270-01-01 00:00:00'::timestamp);
326326
-- another internal overflow test case
327327
SELECT extract(epoch from '5000-01-01 00:00:00'::timestamp);
328328

329+
-- test edge-case overflow in timestamp subtraction
330+
SELECT timestamp '294276-12-31 23:59:59' - timestamp '1999-12-23 19:59:04.224193' AS ok;
331+
SELECT timestamp '294276-12-31 23:59:59' - timestamp '1999-12-23 19:59:04.224192' AS overflows;
332+
329333
-- TO_CHAR()
330334
SELECT to_char(d1, 'DAY Day day DY Dy dy MONTH Month month RM MON Mon mon')
331335
FROM TIMESTAMP_TBL;

src/test/regress/sql/timestamptz.sql

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -306,6 +306,10 @@ SELECT extract(epoch from '294270-01-01 00:00:00+00'::timestamptz);
306306
-- another internal overflow test case
307307
SELECT extract(epoch from '5000-01-01 00:00:00+00'::timestamptz);
308308

309+
-- test edge-case overflow in timestamp subtraction
310+
SELECT timestamptz '294276-12-31 23:59:59 UTC' - timestamptz '1999-12-23 19:59:04.224193 UTC' AS ok;
311+
SELECT timestamptz '294276-12-31 23:59:59 UTC' - timestamptz '1999-12-23 19:59:04.224192 UTC' AS overflows;
312+
309313
-- TO_CHAR()
310314
SELECT to_char(d1, 'DAY Day day DY Dy dy MONTH Month month RM MON Mon mon')
311315
FROM TIMESTAMPTZ_TBL;

0 commit comments

Comments
 (0)