This is an automated email from the ASF dual-hosted git repository.

tuhaihe pushed a commit to branch REL_2_STABLE
in repository https://gitbox.apache.org/repos/asf/cloudberry.git

commit e84c0966214e4c25b3876bc0eb0ee29aef1a436c
Author: Tomas Vondra <[email protected]>
AuthorDate: Fri Oct 27 17:57:44 2023 +0200

    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/[email protected]
    (cherry picked from commit 1487acb539573afcb5d3566c2381443f39ab36f3)
---
 src/backend/access/brin/brin_minmax_multi.c | 33 ++++-------------------------
 src/test/regress/expected/brin_multi.out    | 29 +++++++++++++++++++++++++
 src/test/regress/sql/brin_multi.sql         | 21 ++++++++++++++++++
 3 files changed, 54 insertions(+), 29 deletions(-)

diff --git a/src/backend/access/brin/brin_minmax_multi.c 
b/src/backend/access/brin/brin_minmax_multi.c
index 9f7d6448a7c..1e1159d6382 100644
--- a/src/backend/access/brin/brin_minmax_multi.c
+++ b/src/backend/access/brin/brin_minmax_multi.c
@@ -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;
diff --git a/src/test/regress/expected/brin_multi.out 
b/src/test/regress/expected/brin_multi.out
index 5c3b46235e4..987f010273b 100644
--- a/src/test/regress/expected/brin_multi.out
+++ b/src/test/regress/expected/brin_multi.out
@@ -569,3 +569,32 @@ SELECT * FROM brin_date_test WHERE a = '1900-01-01'::date;
 DROP TABLE brin_date_test;
 RESET enable_seqscan;
 RESET datestyle;
+-- test handling of overflow for interval values
+CREATE TABLE brin_interval_test(a INTERVAL);
+INSERT INTO brin_interval_test SELECT (i || ' years')::interval FROM 
generate_series(-178000000, -177999980) s(i);
+INSERT INTO brin_interval_test SELECT (i || ' years')::interval FROM 
generate_series( 177999980,  178000000) s(i);
+CREATE INDEX ON brin_interval_test USING brin (a interval_minmax_multi_ops) 
WITH (pages_per_range=1);
+SET enable_seqscan = off;
+EXPLAIN (ANALYZE, TIMING OFF, COSTS OFF, SUMMARY OFF)
+SELECT * FROM brin_interval_test WHERE a = '-30 years'::interval;
+                                 QUERY PLAN                                  
+-----------------------------------------------------------------------------
+ Bitmap Heap Scan on brin_interval_test (actual rows=0 loops=1)
+   Recheck Cond: (a = '@ 30 years ago'::interval)
+   ->  Bitmap Index Scan on brin_interval_test_a_idx (actual rows=0 loops=1)
+         Index Cond: (a = '@ 30 years ago'::interval)
+(4 rows)
+
+EXPLAIN (ANALYZE, TIMING OFF, COSTS OFF, SUMMARY OFF)
+SELECT * FROM brin_interval_test WHERE a = '30 years'::interval;
+                                 QUERY PLAN                                  
+-----------------------------------------------------------------------------
+ Bitmap Heap Scan on brin_interval_test (actual rows=0 loops=1)
+   Recheck Cond: (a = '@ 30 years'::interval)
+   ->  Bitmap Index Scan on brin_interval_test_a_idx (actual rows=0 loops=1)
+         Index Cond: (a = '@ 30 years'::interval)
+(4 rows)
+
+DROP TABLE brin_interval_test;
+RESET enable_seqscan;
+RESET datestyle;
diff --git a/src/test/regress/sql/brin_multi.sql 
b/src/test/regress/sql/brin_multi.sql
index 3028d6f2238..8051ec997e4 100644
--- a/src/test/regress/sql/brin_multi.sql
+++ b/src/test/regress/sql/brin_multi.sql
@@ -502,3 +502,24 @@ SELECT * FROM brin_date_test WHERE a = '1900-01-01'::date;
 DROP TABLE brin_date_test;
 RESET enable_seqscan;
 RESET datestyle;
+
+-- test handling of overflow for interval values
+CREATE TABLE brin_interval_test(a INTERVAL);
+
+INSERT INTO brin_interval_test SELECT (i || ' years')::interval FROM 
generate_series(-178000000, -177999980) s(i);
+
+INSERT INTO brin_interval_test SELECT (i || ' years')::interval FROM 
generate_series( 177999980,  178000000) s(i);
+
+CREATE INDEX ON brin_interval_test USING brin (a interval_minmax_multi_ops) 
WITH (pages_per_range=1);
+
+SET enable_seqscan = off;
+
+EXPLAIN (ANALYZE, TIMING OFF, COSTS OFF, SUMMARY OFF)
+SELECT * FROM brin_interval_test WHERE a = '-30 years'::interval;
+
+EXPLAIN (ANALYZE, TIMING OFF, COSTS OFF, SUMMARY OFF)
+SELECT * FROM brin_interval_test WHERE a = '30 years'::interval;
+
+DROP TABLE brin_interval_test;
+RESET enable_seqscan;
+RESET datestyle;


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to