Skip to content

Commit 0635fe0

Browse files
committed
Fix overflow when calculating timestamp distance in BRIN
When calculating distances for timestamp values for BRIN minmax-multi indexes, we need to be careful about overflows for extreme values. If the value overflows into a negative value, the index may be inefficient. The new regression test checks this for the timestamp type by adding a table with enough values to force range compaction/merging. The values are close to min/max, which means a risk of overflow. Fixed by converting the int64 values to double first, before calculating the distance. This prevents the overflow. We may lose some precision, of course, but that's good enough. In the worst case we build a slightly less efficient index, but for large distances this won't matter. This only affects minmax-multi indexes on timestamp columns, with ranges containing values sufficiently distant to cause an overflow. That seems like a fairly rare case in practice. 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 2bf99b4 commit 0635fe0

File tree

3 files changed

+37
-1
lines changed

3 files changed

+37
-1
lines changed

src/backend/access/brin/brin_minmax_multi.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2138,7 +2138,7 @@ brin_minmax_multi_distance_timestamp(PG_FUNCTION_ARGS)
21382138
if (TIMESTAMP_NOT_FINITE(dt1) || TIMESTAMP_NOT_FINITE(dt2))
21392139
PG_RETURN_FLOAT8(0);
21402140

2141-
delta = dt2 - dt1;
2141+
delta = (float8) dt2 - (float8) dt1;
21422142

21432143
Assert(delta >= 0);
21442144

src/test/regress/expected/brin_multi.out

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -466,3 +466,18 @@ EXPLAIN (COSTS OFF) SELECT * FROM brin_test_multi WHERE b = 1;
466466
Filter: (b = 1)
467467
(2 rows)
468468

469+
-- test overflows during CREATE INDEX with extreme timestamp values
470+
CREATE TABLE brin_timestamp_test(a TIMESTAMPTZ);
471+
SET datestyle TO iso;
472+
-- values close to timetamp minimum
473+
INSERT INTO brin_timestamp_test
474+
SELECT '4713-01-01 00:00:01 BC'::timestamptz + (i || ' seconds')::interval
475+
FROM generate_series(1,30) s(i);
476+
-- values close to timetamp maximum
477+
INSERT INTO brin_timestamp_test
478+
SELECT '294276-12-01 00:00:01'::timestamptz + (i || ' seconds')::interval
479+
FROM generate_series(1,30) s(i);
480+
CREATE INDEX ON brin_timestamp_test USING brin (a timestamptz_minmax_multi_ops) WITH (pages_per_range=1);
481+
DROP TABLE brin_timestamp_test;
482+
RESET enable_seqscan;
483+
RESET datestyle;

src/test/regress/sql/brin_multi.sql

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -421,3 +421,24 @@ VACUUM ANALYZE brin_test_multi;
421421
EXPLAIN (COSTS OFF) SELECT * FROM brin_test_multi WHERE a = 1;
422422
-- Ensure brin index is not used when values are not correlated
423423
EXPLAIN (COSTS OFF) SELECT * FROM brin_test_multi WHERE b = 1;
424+
425+
-- test overflows during CREATE INDEX with extreme timestamp values
426+
CREATE TABLE brin_timestamp_test(a TIMESTAMPTZ);
427+
428+
SET datestyle TO iso;
429+
430+
-- values close to timetamp minimum
431+
INSERT INTO brin_timestamp_test
432+
SELECT '4713-01-01 00:00:01 BC'::timestamptz + (i || ' seconds')::interval
433+
FROM generate_series(1,30) s(i);
434+
435+
-- values close to timetamp maximum
436+
INSERT INTO brin_timestamp_test
437+
SELECT '294276-12-01 00:00:01'::timestamptz + (i || ' seconds')::interval
438+
FROM generate_series(1,30) s(i);
439+
440+
CREATE INDEX ON brin_timestamp_test USING brin (a timestamptz_minmax_multi_ops) WITH (pages_per_range=1);
441+
DROP TABLE brin_timestamp_test;
442+
443+
RESET enable_seqscan;
444+
RESET datestyle;

0 commit comments

Comments
 (0)