Skip to content

Commit 394d517

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 b5489b7 commit 394d517

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
@@ -2081,13 +2081,18 @@ brin_minmax_multi_distance_uuid(PG_FUNCTION_ARGS)
20812081
Datum
20822082
brin_minmax_multi_distance_date(PG_FUNCTION_ARGS)
20832083
{
2084+
float8 delta = 0;
20842085
DateADT dateVal1 = PG_GETARG_DATEADT(0);
20852086
DateADT dateVal2 = PG_GETARG_DATEADT(1);
20862087

20872088
if (DATE_NOT_FINITE(dateVal1) || DATE_NOT_FINITE(dateVal2))
20882089
PG_RETURN_FLOAT8(0);
20892090

2090-
PG_RETURN_FLOAT8(dateVal1 - dateVal2);
2091+
delta = (float8) dateVal2 - (float8) dateVal1;
2092+
2093+
Assert(delta >= 0);
2094+
2095+
PG_RETURN_FLOAT8(delta);
20912096
}
20922097

20932098
/*

src/test/regress/expected/brin_multi.out

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -836,5 +836,25 @@ SELECT '294276-12-01 00:00:01'::timestamptz + (i || ' seconds')::interval
836836
FROM generate_series(1,30) s(i);
837837
CREATE INDEX ON brin_timestamp_test USING brin (a timestamptz_minmax_multi_ops) WITH (pages_per_range=1);
838838
DROP TABLE brin_timestamp_test;
839+
-- test overflows during CREATE INDEX with extreme date values
840+
CREATE TABLE brin_date_test(a DATE);
841+
-- insert values close to date minimum
842+
INSERT INTO brin_date_test SELECT '4713-01-01 BC'::date + i FROM generate_series(1, 30) s(i);
843+
-- insert values close to date minimum
844+
INSERT INTO brin_date_test SELECT '5874897-12-01'::date + i FROM generate_series(1, 30) s(i);
845+
CREATE INDEX ON brin_date_test USING brin (a date_minmax_multi_ops) WITH (pages_per_range=1);
846+
SET enable_seqscan = off;
847+
-- make sure the ranges were built correctly and 2023-01-01 eliminates all
848+
EXPLAIN (ANALYZE, TIMING OFF, COSTS OFF, SUMMARY OFF)
849+
SELECT * FROM brin_date_test WHERE a = '2023-01-01'::date;
850+
QUERY PLAN
851+
-------------------------------------------------------------------------
852+
Bitmap Heap Scan on brin_date_test (actual rows=0 loops=1)
853+
Recheck Cond: (a = '2023-01-01'::date)
854+
-> Bitmap Index Scan on brin_date_test_a_idx (actual rows=0 loops=1)
855+
Index Cond: (a = '2023-01-01'::date)
856+
(4 rows)
857+
858+
DROP TABLE brin_date_test;
839859
RESET enable_seqscan;
840860
RESET datestyle;

src/test/regress/sql/brin_multi.sql

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

608+
-- test overflows during CREATE INDEX with extreme date values
609+
CREATE TABLE brin_date_test(a DATE);
610+
611+
-- insert values close to date minimum
612+
INSERT INTO brin_date_test SELECT '4713-01-01 BC'::date + i FROM generate_series(1, 30) s(i);
613+
614+
-- insert values close to date minimum
615+
INSERT INTO brin_date_test SELECT '5874897-12-01'::date + i FROM generate_series(1, 30) s(i);
616+
617+
CREATE INDEX ON brin_date_test USING brin (a date_minmax_multi_ops) WITH (pages_per_range=1);
618+
619+
SET enable_seqscan = off;
620+
621+
-- make sure the ranges were built correctly and 2023-01-01 eliminates all
622+
EXPLAIN (ANALYZE, TIMING OFF, COSTS OFF, SUMMARY OFF)
623+
SELECT * FROM brin_date_test WHERE a = '2023-01-01'::date;
624+
625+
DROP TABLE brin_date_test;
608626
RESET enable_seqscan;
609627
RESET datestyle;

0 commit comments

Comments
 (0)