Skip to content

Commit e796522

Browse files
committed
Fix calculation in brin_minmax_multi_distance_date
When calculating the distance between date values, make sure to subtract them in the right order, i.e. (larger - smaller). The distance is used to determine which values to merge, and is expected to be a positive value. The code unfortunately did the subtraction in the opposite order, i.e. (smaller - larger), thus producing negative values and merging values the most distant values first. The resulting index is correct (i.e. produces correct results), but may be significantly less efficient. This affects all minmax-multi indexes on date columns. Backpatch to 14, where minmax-multi indexes were introduced. Reported-by: Ashutosh Bapat Reviewed-by: Ashutosh Bapat, Dean Rasheed Backpatch-through: 14 Discussion: https://postgr.es/m/eef0ea8c-4aaa-8d0d-027f-58b1f35dd170@enterprisedb.com
1 parent 0635fe0 commit e796522

File tree

3 files changed

+44
-1
lines changed

3 files changed

+44
-1
lines changed

src/backend/access/brin/brin_minmax_multi.c

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2075,13 +2075,18 @@ brin_minmax_multi_distance_uuid(PG_FUNCTION_ARGS)
20752075
Datum
20762076
brin_minmax_multi_distance_date(PG_FUNCTION_ARGS)
20772077
{
2078+
float8 delta = 0;
20782079
DateADT dateVal1 = PG_GETARG_DATEADT(0);
20792080
DateADT dateVal2 = PG_GETARG_DATEADT(1);
20802081

20812082
if (DATE_NOT_FINITE(dateVal1) || DATE_NOT_FINITE(dateVal2))
20822083
PG_RETURN_FLOAT8(0);
20832084

2084-
PG_RETURN_FLOAT8(dateVal1 - dateVal2);
2085+
delta = (float8) dateVal2 - (float8) dateVal1;
2086+
2087+
Assert(delta >= 0);
2088+
2089+
PG_RETURN_FLOAT8(delta);
20852090
}
20862091

20872092
/*

src/test/regress/expected/brin_multi.out

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -479,5 +479,25 @@ SELECT '294276-12-01 00:00:01'::timestamptz + (i || ' seconds')::interval
479479
FROM generate_series(1,30) s(i);
480480
CREATE INDEX ON brin_timestamp_test USING brin (a timestamptz_minmax_multi_ops) WITH (pages_per_range=1);
481481
DROP TABLE brin_timestamp_test;
482+
-- test overflows during CREATE INDEX with extreme date values
483+
CREATE TABLE brin_date_test(a DATE);
484+
-- insert values close to date minimum
485+
INSERT INTO brin_date_test SELECT '4713-01-01 BC'::date + i FROM generate_series(1, 30) s(i);
486+
-- insert values close to date minimum
487+
INSERT INTO brin_date_test SELECT '5874897-12-01'::date + i FROM generate_series(1, 30) s(i);
488+
CREATE INDEX ON brin_date_test USING brin (a date_minmax_multi_ops) WITH (pages_per_range=1);
489+
SET enable_seqscan = off;
490+
-- make sure the ranges were built correctly and 2023-01-01 eliminates all
491+
EXPLAIN (ANALYZE, TIMING OFF, COSTS OFF, SUMMARY OFF)
492+
SELECT * FROM brin_date_test WHERE a = '2023-01-01'::date;
493+
QUERY PLAN
494+
-------------------------------------------------------------------------
495+
Bitmap Heap Scan on brin_date_test (actual rows=0 loops=1)
496+
Recheck Cond: (a = '2023-01-01'::date)
497+
-> Bitmap Index Scan on brin_date_test_a_idx (actual rows=0 loops=1)
498+
Index Cond: (a = '2023-01-01'::date)
499+
(4 rows)
500+
501+
DROP TABLE brin_date_test;
482502
RESET enable_seqscan;
483503
RESET datestyle;

src/test/regress/sql/brin_multi.sql

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -440,5 +440,23 @@ SELECT '294276-12-01 00:00:01'::timestamptz + (i || ' seconds')::interval
440440
CREATE INDEX ON brin_timestamp_test USING brin (a timestamptz_minmax_multi_ops) WITH (pages_per_range=1);
441441
DROP TABLE brin_timestamp_test;
442442

443+
-- test overflows during CREATE INDEX with extreme date values
444+
CREATE TABLE brin_date_test(a DATE);
445+
446+
-- insert values close to date minimum
447+
INSERT INTO brin_date_test SELECT '4713-01-01 BC'::date + i FROM generate_series(1, 30) s(i);
448+
449+
-- insert values close to date minimum
450+
INSERT INTO brin_date_test SELECT '5874897-12-01'::date + i FROM generate_series(1, 30) s(i);
451+
452+
CREATE INDEX ON brin_date_test USING brin (a date_minmax_multi_ops) WITH (pages_per_range=1);
453+
454+
SET enable_seqscan = off;
455+
456+
-- make sure the ranges were built correctly and 2023-01-01 eliminates all
457+
EXPLAIN (ANALYZE, TIMING OFF, COSTS OFF, SUMMARY OFF)
458+
SELECT * FROM brin_date_test WHERE a = '2023-01-01'::date;
459+
460+
DROP TABLE brin_date_test;
443461
RESET enable_seqscan;
444462
RESET datestyle;

0 commit comments

Comments
 (0)