When formatting the output of an Interval, we call abs() on the hours
field of the Interval. Calling abs(INT_MIN) returns back INT_MIN
causing the output to contain two '-' characters. The attached patch
fixes that issue by special casing INT_MIN hours.
Here is an example of the issue:
postgres=# SELECT INTERVAL '-2147483648 hrs';
interval
--------------------
--2147483648:00:00
(1 row)
Cheers,
Joe Koshakow
From 720f0903c71a428695672aa866fc8fb0146a472e Mon Sep 17 00:00:00 2001
From: Joseph Koshakow <[email protected]>
Date: Thu, 17 Feb 2022 21:57:48 -0500
Subject: [PATCH] Fix Interval formatting with INT_MIN hours
When formatting the output of an Interval, we call abs() on the hours
field of the Interval. Calling abs(INT_MIN) returns back INT_MIN
causing the output to contain two '-' characters. This commit fixes
that issue by special casing INT_MIN hours.
---
src/backend/utils/adt/datetime.c | 7 +++++--
src/test/regress/expected/interval.out | 8 ++++++++
src/test/regress/sql/interval.sql | 4 ++++
3 files changed, 17 insertions(+), 2 deletions(-)
diff --git a/src/backend/utils/adt/datetime.c b/src/backend/utils/adt/datetime.c
index 7926258c06..368df715e6 100644
--- a/src/backend/utils/adt/datetime.c
+++ b/src/backend/utils/adt/datetime.c
@@ -4376,11 +4376,14 @@ EncodeInterval(struct pg_tm *tm, fsec_t fsec, int style, char *str)
if (is_zero || hour != 0 || min != 0 || sec != 0 || fsec != 0)
{
bool minus = (hour < 0 || min < 0 || sec < 0 || fsec < 0);
+ // need to special case hours being INT_MIN because you can't call abs(INT_MIN)
+ bool int_min_hours = (hour == INT_MIN);
sprintf(cp, "%s%s%02d:%02d:",
is_zero ? "" : " ",
- (minus ? "-" : (is_before ? "+" : "")),
- abs(hour), abs(min));
+ (minus ? (int_min_hours ? "" : "-") : (is_before ? "+" : "")),
+ (int_min_hours ? hour : abs(hour)),
+ abs(min));
cp += strlen(cp);
cp = AppendSeconds(cp, sec, fsec, MAX_INTERVAL_PRECISION, true);
*cp = '\0';
diff --git a/src/test/regress/expected/interval.out b/src/test/regress/expected/interval.out
index accd4a7d90..bb0d59ccfa 100644
--- a/src/test/regress/expected/interval.out
+++ b/src/test/regress/expected/interval.out
@@ -1043,3 +1043,11 @@ SELECT extract(epoch from interval '1000000000 days');
86400000000000.000000
(1 row)
+-- test that INT_MIN number of hours is formatted properly
+SET IntervalStyle to postgres;
+SELECT INTERVAL '-2147483648 hrs';
+ interval
+-------------------
+ -2147483648:00:00
+(1 row)
+
diff --git a/src/test/regress/sql/interval.sql b/src/test/regress/sql/interval.sql
index 6d532398bd..f1fe0a5ea0 100644
--- a/src/test/regress/sql/interval.sql
+++ b/src/test/regress/sql/interval.sql
@@ -355,3 +355,7 @@ SELECT f1,
-- internal overflow test case
SELECT extract(epoch from interval '1000000000 days');
+
+-- test that INT_MIN number of hours is formatted properly
+SET IntervalStyle to postgres;
+SELECT INTERVAL '-2147483648 hrs';
--
2.25.1