1
0
mirror of https://github.com/postgres/postgres.git synced 2025-11-03 09:13:20 +03:00

Fix minmax-multi distance for extreme interval values

When calculating distance for interval values, the code mostly mimicked
interval_mi, i.e. it built a new interval value for the difference.
That however does not work for sufficiently distant interval values,
when the difference overflows the interval range.

Instead, we can calculate the distance directly, without constructing
the intermediate (and unnecessary) interval value.

Backpatch to 14, where minmax-multi indexes were introduced.

Reported-by: Dean Rasheed
Reviewed-by: Ashutosh Bapat, Dean Rasheed
Backpatch-through: 14
Discussion: https://postgr.es/m/eef0ea8c-4aaa-8d0d-027f-58b1f35dd170@enterprisedb.com
This commit is contained in:
Tomas Vondra
2023-10-27 17:57:44 +02:00
parent d04a9283b7
commit 2fbb2fcb0c
3 changed files with 54 additions and 29 deletions

View File

@@ -2154,45 +2154,20 @@ brin_minmax_multi_distance_interval(PG_FUNCTION_ARGS)
Interval *ia = PG_GETARG_INTERVAL_P(0);
Interval *ib = PG_GETARG_INTERVAL_P(1);
Interval *result;
int64 dayfraction;
int64 days;
result = (Interval *) palloc(sizeof(Interval));
result->month = ib->month - ia->month;
/* overflow check copied from int4mi */
if (!SAMESIGN(ib->month, ia->month) &&
!SAMESIGN(result->month, ib->month))
ereport(ERROR,
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
errmsg("interval out of range")));
result->day = ib->day - ia->day;
if (!SAMESIGN(ib->day, ia->day) &&
!SAMESIGN(result->day, ib->day))
ereport(ERROR,
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
errmsg("interval out of range")));
result->time = ib->time - ia->time;
if (!SAMESIGN(ib->time, ia->time) &&
!SAMESIGN(result->time, ib->time))
ereport(ERROR,
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
errmsg("interval out of range")));
/*
* Delta is (fractional) number of days between the intervals. Assume
* months have 30 days for consistency with interval_cmp_internal. We
* don't need to be exact, in the worst case we'll build a bit less
* efficient ranges. But we should not contradict interval_cmp.
*/
dayfraction = result->time % USECS_PER_DAY;
days = result->time / USECS_PER_DAY;
days += result->month * INT64CONST(30);
days += result->day;
dayfraction = (ib->time % USECS_PER_DAY) - (ia->time % USECS_PER_DAY);
days = (ib->time / USECS_PER_DAY) - (ia->time / USECS_PER_DAY);
days += (int64) ib->day - (int64) ia->day;
days += ((int64) ib->month - (int64) ia->month) * INT64CONST(30);
/* convert to double precision */
delta = (double) days + dayfraction / (double) USECS_PER_DAY;