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 <kosh...@gmail.com> 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