Handle integer overflow in interval justification functions.
justify_interval, justify_hours, and justify_days didn't check for overflow when promoting hours to days or days to months; but that's possible when the upper field's value is already large. Detect and report any such overflow. Also, we can avoid unnecessary overflow in some cases in justify_interval by pre-justifying the days field. (Thanks to Nathan Bossart for this idea.) Joe Koshakow Discussion: https://postgr.es/m/CAAvxfHeNqsJ2xYFbPUf_8nNQUiJqkag04NW6aBQQ0dbZsxfWHA@mail.gmail.com
This commit is contained in:
parent
a59c79564b
commit
54bd1e43ca
@ -2717,12 +2717,33 @@ interval_justify_interval(PG_FUNCTION_ARGS)
|
|||||||
result->day = span->day;
|
result->day = span->day;
|
||||||
result->time = span->time;
|
result->time = span->time;
|
||||||
|
|
||||||
|
/* pre-justify days if it might prevent overflow */
|
||||||
|
if ((result->day > 0 && result->time > 0) ||
|
||||||
|
(result->day < 0 && result->time < 0))
|
||||||
|
{
|
||||||
|
wholemonth = result->day / DAYS_PER_MONTH;
|
||||||
|
result->day -= wholemonth * DAYS_PER_MONTH;
|
||||||
|
if (pg_add_s32_overflow(result->month, wholemonth, &result->month))
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
|
||||||
|
errmsg("interval out of range")));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Since TimeOffset is int64, abs(wholeday) can't exceed about 1.07e8. If
|
||||||
|
* we pre-justified then abs(result->day) is less than DAYS_PER_MONTH, so
|
||||||
|
* this addition can't overflow. If we didn't pre-justify, then day and
|
||||||
|
* time are of different signs, so it still can't overflow.
|
||||||
|
*/
|
||||||
TMODULO(result->time, wholeday, USECS_PER_DAY);
|
TMODULO(result->time, wholeday, USECS_PER_DAY);
|
||||||
result->day += wholeday; /* could overflow... */
|
result->day += wholeday;
|
||||||
|
|
||||||
wholemonth = result->day / DAYS_PER_MONTH;
|
wholemonth = result->day / DAYS_PER_MONTH;
|
||||||
result->day -= wholemonth * DAYS_PER_MONTH;
|
result->day -= wholemonth * DAYS_PER_MONTH;
|
||||||
result->month += wholemonth;
|
if (pg_add_s32_overflow(result->month, wholemonth, &result->month))
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
|
||||||
|
errmsg("interval out of range")));
|
||||||
|
|
||||||
if (result->month > 0 &&
|
if (result->month > 0 &&
|
||||||
(result->day < 0 || (result->day == 0 && result->time < 0)))
|
(result->day < 0 || (result->day == 0 && result->time < 0)))
|
||||||
@ -2772,7 +2793,10 @@ interval_justify_hours(PG_FUNCTION_ARGS)
|
|||||||
result->time = span->time;
|
result->time = span->time;
|
||||||
|
|
||||||
TMODULO(result->time, wholeday, USECS_PER_DAY);
|
TMODULO(result->time, wholeday, USECS_PER_DAY);
|
||||||
result->day += wholeday; /* could overflow... */
|
if (pg_add_s32_overflow(result->day, wholeday, &result->day))
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
|
||||||
|
errmsg("interval out of range")));
|
||||||
|
|
||||||
if (result->day > 0 && result->time < 0)
|
if (result->day > 0 && result->time < 0)
|
||||||
{
|
{
|
||||||
@ -2808,7 +2832,10 @@ interval_justify_days(PG_FUNCTION_ARGS)
|
|||||||
|
|
||||||
wholemonth = result->day / DAYS_PER_MONTH;
|
wholemonth = result->day / DAYS_PER_MONTH;
|
||||||
result->day -= wholemonth * DAYS_PER_MONTH;
|
result->day -= wholemonth * DAYS_PER_MONTH;
|
||||||
result->month += wholemonth;
|
if (pg_add_s32_overflow(result->month, wholemonth, &result->month))
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
|
||||||
|
errmsg("interval out of range")));
|
||||||
|
|
||||||
if (result->month > 0 && result->day < 0)
|
if (result->month > 0 && result->day < 0)
|
||||||
{
|
{
|
||||||
|
@ -396,6 +396,10 @@ SELECT justify_days(interval '6 months 36 days 5 hours 4 minutes 3 seconds') as
|
|||||||
@ 7 mons 6 days 5 hours 4 mins 3 secs
|
@ 7 mons 6 days 5 hours 4 mins 3 secs
|
||||||
(1 row)
|
(1 row)
|
||||||
|
|
||||||
|
SELECT justify_hours(interval '2147483647 days 24 hrs');
|
||||||
|
ERROR: interval out of range
|
||||||
|
SELECT justify_days(interval '2147483647 months 30 days');
|
||||||
|
ERROR: interval out of range
|
||||||
-- test justify_interval()
|
-- test justify_interval()
|
||||||
SELECT justify_interval(interval '1 month -1 hour') as "1 month -1 hour";
|
SELECT justify_interval(interval '1 month -1 hour') as "1 month -1 hour";
|
||||||
1 month -1 hour
|
1 month -1 hour
|
||||||
@ -403,6 +407,38 @@ SELECT justify_interval(interval '1 month -1 hour') as "1 month -1 hour";
|
|||||||
@ 29 days 23 hours
|
@ 29 days 23 hours
|
||||||
(1 row)
|
(1 row)
|
||||||
|
|
||||||
|
SELECT justify_interval(interval '2147483647 days 24 hrs');
|
||||||
|
justify_interval
|
||||||
|
-------------------------------
|
||||||
|
@ 5965232 years 4 mons 8 days
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT justify_interval(interval '-2147483648 days -24 hrs');
|
||||||
|
justify_interval
|
||||||
|
-----------------------------------
|
||||||
|
@ 5965232 years 4 mons 9 days ago
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT justify_interval(interval '2147483647 months 30 days');
|
||||||
|
ERROR: interval out of range
|
||||||
|
SELECT justify_interval(interval '-2147483648 months -30 days');
|
||||||
|
ERROR: interval out of range
|
||||||
|
SELECT justify_interval(interval '2147483647 months 30 days -24 hrs');
|
||||||
|
justify_interval
|
||||||
|
----------------------------------
|
||||||
|
@ 178956970 years 7 mons 29 days
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT justify_interval(interval '-2147483648 months -30 days 24 hrs');
|
||||||
|
justify_interval
|
||||||
|
--------------------------------------
|
||||||
|
@ 178956970 years 8 mons 29 days ago
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT justify_interval(interval '2147483647 months -30 days 1440 hrs');
|
||||||
|
ERROR: interval out of range
|
||||||
|
SELECT justify_interval(interval '-2147483648 months 30 days -1440 hrs');
|
||||||
|
ERROR: interval out of range
|
||||||
-- test fractional second input, and detection of duplicate units
|
-- test fractional second input, and detection of duplicate units
|
||||||
SET DATESTYLE = 'ISO';
|
SET DATESTYLE = 'ISO';
|
||||||
SET IntervalStyle TO postgres;
|
SET IntervalStyle TO postgres;
|
||||||
|
@ -149,10 +149,22 @@ select '100000000y 10mon -1000000000d -100000h -10min -10.000001s ago'::interval
|
|||||||
SELECT justify_hours(interval '6 months 3 days 52 hours 3 minutes 2 seconds') as "6 mons 5 days 4 hours 3 mins 2 seconds";
|
SELECT justify_hours(interval '6 months 3 days 52 hours 3 minutes 2 seconds') as "6 mons 5 days 4 hours 3 mins 2 seconds";
|
||||||
SELECT justify_days(interval '6 months 36 days 5 hours 4 minutes 3 seconds') as "7 mons 6 days 5 hours 4 mins 3 seconds";
|
SELECT justify_days(interval '6 months 36 days 5 hours 4 minutes 3 seconds') as "7 mons 6 days 5 hours 4 mins 3 seconds";
|
||||||
|
|
||||||
|
SELECT justify_hours(interval '2147483647 days 24 hrs');
|
||||||
|
SELECT justify_days(interval '2147483647 months 30 days');
|
||||||
|
|
||||||
-- test justify_interval()
|
-- test justify_interval()
|
||||||
|
|
||||||
SELECT justify_interval(interval '1 month -1 hour') as "1 month -1 hour";
|
SELECT justify_interval(interval '1 month -1 hour') as "1 month -1 hour";
|
||||||
|
|
||||||
|
SELECT justify_interval(interval '2147483647 days 24 hrs');
|
||||||
|
SELECT justify_interval(interval '-2147483648 days -24 hrs');
|
||||||
|
SELECT justify_interval(interval '2147483647 months 30 days');
|
||||||
|
SELECT justify_interval(interval '-2147483648 months -30 days');
|
||||||
|
SELECT justify_interval(interval '2147483647 months 30 days -24 hrs');
|
||||||
|
SELECT justify_interval(interval '-2147483648 months -30 days 24 hrs');
|
||||||
|
SELECT justify_interval(interval '2147483647 months -30 days 1440 hrs');
|
||||||
|
SELECT justify_interval(interval '-2147483648 months 30 days -1440 hrs');
|
||||||
|
|
||||||
-- test fractional second input, and detection of duplicate units
|
-- test fractional second input, and detection of duplicate units
|
||||||
SET DATESTYLE = 'ISO';
|
SET DATESTYLE = 'ISO';
|
||||||
SET IntervalStyle TO postgres;
|
SET IntervalStyle TO postgres;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user