]> git.ipfire.org Git - thirdparty/postgresql.git/commitdiff
Fix overflow when calculating timestamp distance in BRIN
authorTomas Vondra <tomas.vondra@postgresql.org>
Fri, 27 Oct 2023 15:56:27 +0000 (17:56 +0200)
committerTomas Vondra <tomas.vondra@postgresql.org>
Fri, 27 Oct 2023 16:37:56 +0000 (18:37 +0200)
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

src/backend/access/brin/brin_minmax_multi.c
src/test/regress/expected/brin_multi.out
src/test/regress/sql/brin_multi.sql

index 0713fd6588af9076eafe2656a03ba172c44e27bd..760f90d71c7a45ef7dcd8fdd045e0e716f5dacb7 100644 (file)
@@ -2138,7 +2138,7 @@ brin_minmax_multi_distance_timestamp(PG_FUNCTION_ARGS)
        if (TIMESTAMP_NOT_FINITE(dt1) || TIMESTAMP_NOT_FINITE(dt2))
                PG_RETURN_FLOAT8(0);
 
-       delta = dt2 - dt1;
+       delta = (float8) dt2 - (float8) dt1;
 
        Assert(delta >= 0);
 
index 84c233c22ace161d3afef5253d6084ee6367d46f..804467b494311b7fbd112b0788755a8db09d2433 100644 (file)
@@ -466,3 +466,18 @@ EXPLAIN (COSTS OFF) SELECT * FROM brin_test_multi WHERE b = 1;
    Filter: (b = 1)
 (2 rows)
 
+-- test overflows during CREATE INDEX with extreme timestamp values
+CREATE TABLE brin_timestamp_test(a TIMESTAMPTZ);
+SET datestyle TO iso;
+-- values close to timetamp minimum
+INSERT INTO brin_timestamp_test
+SELECT '4713-01-01 00:00:01 BC'::timestamptz + (i || ' seconds')::interval
+  FROM generate_series(1,30) s(i);
+-- values close to timetamp maximum
+INSERT INTO brin_timestamp_test
+SELECT '294276-12-01 00:00:01'::timestamptz + (i || ' seconds')::interval
+  FROM generate_series(1,30) s(i);
+CREATE INDEX ON brin_timestamp_test USING brin (a timestamptz_minmax_multi_ops) WITH (pages_per_range=1);
+DROP TABLE brin_timestamp_test;
+RESET enable_seqscan;
+RESET datestyle;
index 9e8923f0b7acb678fcf4dd5769f9b56f5854e20e..d87839506b61e38ab591a4f1aca0299e2e0f8a2d 100644 (file)
@@ -421,3 +421,24 @@ VACUUM ANALYZE brin_test_multi;
 EXPLAIN (COSTS OFF) SELECT * FROM brin_test_multi WHERE a = 1;
 -- Ensure brin index is not used when values are not correlated
 EXPLAIN (COSTS OFF) SELECT * FROM brin_test_multi WHERE b = 1;
+
+-- test overflows during CREATE INDEX with extreme timestamp values
+CREATE TABLE brin_timestamp_test(a TIMESTAMPTZ);
+
+SET datestyle TO iso;
+
+-- values close to timetamp minimum
+INSERT INTO brin_timestamp_test
+SELECT '4713-01-01 00:00:01 BC'::timestamptz + (i || ' seconds')::interval
+  FROM generate_series(1,30) s(i);
+
+-- values close to timetamp maximum
+INSERT INTO brin_timestamp_test
+SELECT '294276-12-01 00:00:01'::timestamptz + (i || ' seconds')::interval
+  FROM generate_series(1,30) s(i);
+
+CREATE INDEX ON brin_timestamp_test USING brin (a timestamptz_minmax_multi_ops) WITH (pages_per_range=1);
+DROP TABLE brin_timestamp_test;
+
+RESET enable_seqscan;
+RESET datestyle;