public inbox for [email protected]  
help / color / mirror / Atom feed
BUG #19501: btree_gist: use float4/float8 comparison functions to handle NaN correctly
2+ messages / 2 participants
[nested] [flat]

* BUG #19501: btree_gist: use float4/float8 comparison functions to handle NaN correctly
@ 2026-05-29 15:35 PG Bug reporting form <[email protected]>
  2026-06-18 16:43 ` Re:BUG #19501: btree_gist: use float4/float8 comparison functions to handle NaN correctly =?utf-8?B?eWx3YW5ndGp1?= <[email protected]>
  0 siblings, 1 reply; 2+ messages in thread

From: PG Bug reporting form @ 2026-05-29 15:35 UTC (permalink / raw)
  To: [email protected]; +Cc: [email protected]

The following bug has been logged on the website:

Bug reference:      19501
Logged by:          Man Zeng
Email address:      [email protected]
PostgreSQL version: 18.4
Operating system:   24.04.1-Ubuntu
Description:        

Hi all,

Here's an interesting case where btree_gist returns inconsistent results
between index scans and sequential scans when NaN values are involved.

```
postgres@zxm-VMware-Virtual-Platform:~/code/zengine$ psql
psql (19devel)
Type "help" for help.

test=# CREATE EXTENSION btree_gist;
CREATE EXTENSION
test=# CREATE TABLE test_nan_bug (v float8);
CREATE TABLE
test=# INSERT INTO test_nan_bug VALUES (1.0), (2.0), ('NaN'::float8), (3.0);
INSERT 0 4
test=# CREATE INDEX idx_nan_bug ON test_nan_bug USING gist(v);
CREATE INDEX
test=# SET enable_indexscan=off;
SET
test=# SELECT count(*) FROM test_nan_bug WHERE v = 'NaN'::float8;
 count
-------
     1
(1 row)

test=# EXPLAIN VERBOSE SELECT count(*) FROM test_nan_bug WHERE v =
'NaN'::float8;
                               QUERY PLAN
-------------------------------------------------------------------------
 Aggregate  (cost=1.05..1.06 rows=1 width=8)
   Output: count(*)
   ->  Seq Scan on public.test_nan_bug  (cost=0.00..1.05 rows=1 width=0)
         Output: v
         Filter: (test_nan_bug.v = 'NaN'::double precision)
(5 rows)

test=# SET enable_indexscan=on; SET enable_seqscan=off;
SET
SET
test=# SELECT count(*) FROM test_nan_bug WHERE v = 'NaN'::float8;
 count
-------
     0
(1 row)

test=# EXPLAIN VERBOSE SELECT count(*) FROM test_nan_bug WHERE v =
'NaN'::float8;
                                            QUERY PLAN
--------------------------------------------------------------------------------------------------
 Aggregate  (cost=8.15..8.16 rows=1 width=8)
   Output: count(*)
   ->  Index Only Scan using idx_nan_bug on public.test_nan_bug
(cost=0.13..8.15 rows=1 width=0)
         Output: v
         Index Cond: (test_nan_bug.v = 'NaN'::double precision)
(5 rows)

```

Steps to reproduce:
```sql
CREATE EXTENSION btree_gist;
CREATE TABLE test_nan_bug (v float8);
INSERT INTO test_nan_bug VALUES (1.0), (2.0), ('NaN'::float8), (3.0);
CREATE INDEX idx_nan_bug ON test_nan_bug USING gist(v);

SET enable_indexscan=off;
SELECT count(*) FROM test_nan_bug WHERE v = 'NaN'::float8;

SET enable_indexscan=on; SET enable_seqscan=off;
SELECT count(*) FROM test_nan_bug WHERE v = 'NaN'::float8;
```

My fix replaces the C comparison operators with PostgreSQL's float
comparison functions (float8_gt, float8_ge, float8_eq, etc.) and
float8_cmp_internal, which handle NaN consistently.

```
diff --git a/contrib/btree_gist/btree_float4.c
b/contrib/btree_gist/btree_float4.c
index c076918fd48..eaecdac0980 100644
--- a/contrib/btree_gist/btree_float4.c
+++ b/contrib/btree_gist/btree_float4.c
@@ -29,27 +29,27 @@ PG_FUNCTION_INFO_V1(gbt_float4_sortsupport);
 static bool
 gbt_float4gt(const void *a, const void *b, FmgrInfo *flinfo)
 {
-       return (*((const float4 *) a) > *((const float4 *) b));
+       return float4_gt(*(const float4 *) a, *(const float4 *) b);
 }
 static bool
 gbt_float4ge(const void *a, const void *b, FmgrInfo *flinfo)
 {
-       return (*((const float4 *) a) >= *((const float4 *) b));
+       return float4_ge(*(const float4 *) a, *(const float4 *) b);
 }
 static bool
 gbt_float4eq(const void *a, const void *b, FmgrInfo *flinfo)
 {
-       return (*((const float4 *) a) == *((const float4 *) b));
+       return float4_eq(*(const float4 *) a, *(const float4 *) b);
 }
 static bool
 gbt_float4le(const void *a, const void *b, FmgrInfo *flinfo)
 {
-       return (*((const float4 *) a) <= *((const float4 *) b));
+       return float4_le(*(const float4 *) a, *(const float4 *) b);
 }
 static bool
 gbt_float4lt(const void *a, const void *b, FmgrInfo *flinfo)
 {
-       return (*((const float4 *) a) < *((const float4 *) b));
+       return float4_lt(*(const float4 *) a, *(const float4 *) b);
 }
 
 static int
@@ -57,16 +57,12 @@ gbt_float4key_cmp(const void *a, const void *b, FmgrInfo
*flinfo)
 {
        float4KEY  *ia = (float4KEY *) (((const Nsrt *) a)->t);
        float4KEY  *ib = (float4KEY *) (((const Nsrt *) b)->t);
+       int                     cmp;
 
-       if (ia->lower == ib->lower)
-       {
-               if (ia->upper == ib->upper)
-                       return 0;
-
-               return (ia->upper > ib->upper) ? 1 : -1;
-       }
-
-       return (ia->lower > ib->lower) ? 1 : -1;
+       cmp = float4_cmp_internal(ia->lower, ib->lower);
+       if (cmp != 0)
+               return cmp;
+       return float4_cmp_internal(ia->upper, ib->upper);
 }
 
 static float8
diff --git a/contrib/btree_gist/btree_float8.c
b/contrib/btree_gist/btree_float8.c
index d7386e885a2..132065a648c 100644
--- a/contrib/btree_gist/btree_float8.c
+++ b/contrib/btree_gist/btree_float8.c
@@ -30,27 +30,27 @@ PG_FUNCTION_INFO_V1(gbt_float8_sortsupport);
 static bool
 gbt_float8gt(const void *a, const void *b, FmgrInfo *flinfo)
 {
-       return (*((const float8 *) a) > *((const float8 *) b));
+       return float8_gt(*(const float8 *) a, *(const float8 *) b);
 }
 static bool
 gbt_float8ge(const void *a, const void *b, FmgrInfo *flinfo)
 {
-       return (*((const float8 *) a) >= *((const float8 *) b));
+       return float8_ge(*(const float8 *) a, *(const float8 *) b);
 }
 static bool
 gbt_float8eq(const void *a, const void *b, FmgrInfo *flinfo)
 {
-       return (*((const float8 *) a) == *((const float8 *) b));
+       return float8_eq(*(const float8 *) a, *(const float8 *) b);
 }
 static bool
 gbt_float8le(const void *a, const void *b, FmgrInfo *flinfo)
 {
-       return (*((const float8 *) a) <= *((const float8 *) b));
+       return float8_le(*(const float8 *) a, *(const float8 *) b);
 }
 static bool
 gbt_float8lt(const void *a, const void *b, FmgrInfo *flinfo)
 {
-       return (*((const float8 *) a) < *((const float8 *) b));
+       return float8_lt(*(const float8 *) a, *(const float8 *) b);
 }
 
 static int
@@ -58,16 +58,12 @@ gbt_float8key_cmp(const void *a, const void *b, FmgrInfo
*flinfo)
 {
        float8KEY  *ia = (float8KEY *) (((const Nsrt *) a)->t);
        float8KEY  *ib = (float8KEY *) (((const Nsrt *) b)->t);
+       int                     cmp;
 
-       if (ia->lower == ib->lower)
-       {
-               if (ia->upper == ib->upper)
-                       return 0;
-
-               return (ia->upper > ib->upper) ? 1 : -1;
-       }
-
-       return (ia->lower > ib->lower) ? 1 : -1;
+       cmp = float8_cmp_internal(ia->lower, ib->lower);
+       if (cmp != 0)
+               return cmp;
+       return float8_cmp_internal(ia->upper, ib->upper);
 }
 
 static float8
diff --git a/contrib/btree_gist/expected/float4.out
b/contrib/btree_gist/expected/float4.out
index dfe732049e6..99ed6ae4668 100644
--- a/contrib/btree_gist/expected/float4.out
+++ b/contrib/btree_gist/expected/float4.out
@@ -89,3 +89,12 @@ SELECT a, a <-> '-179.0' FROM float4tmp ORDER BY a <->
'-179.0' LIMIT 3;
  -158.17741 | 20.822586
 (3 rows)
 
+INSERT INTO float4tmp VALUES ('NaN'), ('NaN');
+SET enable_seqscan=off;
+SELECT count(*) FROM float4tmp WHERE a = 'NaN';
+ count
+-------
+     2
+(1 row)
+
+RESET enable_seqscan;
diff --git a/contrib/btree_gist/expected/float8.out
b/contrib/btree_gist/expected/float8.out
index ebd0ef3d689..e463b8869d6 100644
--- a/contrib/btree_gist/expected/float8.out
+++ b/contrib/btree_gist/expected/float8.out
@@ -89,3 +89,12 @@ SELECT a, a <-> '-1890.0' FROM float8tmp ORDER BY a <->
'-1890.0' LIMIT 3;
   -1769.73634 | 120.26366000000007
 (3 rows)
 
+INSERT INTO float8tmp VALUES ('NaN'), ('NaN');
+SET enable_seqscan=off;
+SELECT count(*) FROM float8tmp WHERE a = 'NaN';
+ count
+-------
+     2
+(1 row)
+
+RESET enable_seqscan;
diff --git a/contrib/btree_gist/sql/float4.sql
b/contrib/btree_gist/sql/float4.sql
index 3da1ce953c8..0cacee08276 100644
--- a/contrib/btree_gist/sql/float4.sql
+++ b/contrib/btree_gist/sql/float4.sql
@@ -35,3 +35,11 @@ SELECT count(*) FROM float4tmp WHERE a >  -179.0::float4;
 EXPLAIN (COSTS OFF)
 SELECT a, a <-> '-179.0' FROM float4tmp ORDER BY a <-> '-179.0' LIMIT 3;
 SELECT a, a <-> '-179.0' FROM float4tmp ORDER BY a <-> '-179.0' LIMIT 3;
+
+INSERT INTO float4tmp VALUES ('NaN'), ('NaN');
+
+SET enable_seqscan=off;
+
+SELECT count(*) FROM float4tmp WHERE a = 'NaN';
+
+RESET enable_seqscan;
diff --git a/contrib/btree_gist/sql/float8.sql
b/contrib/btree_gist/sql/float8.sql
index e1e819b37f9..2d6ef9d95e7 100644
--- a/contrib/btree_gist/sql/float8.sql
+++ b/contrib/btree_gist/sql/float8.sql
@@ -35,3 +35,11 @@ SELECT count(*) FROM float8tmp WHERE a >
-1890.0::float8;
 EXPLAIN (COSTS OFF)
 SELECT a, a <-> '-1890.0' FROM float8tmp ORDER BY a <-> '-1890.0' LIMIT 3;
 SELECT a, a <-> '-1890.0' FROM float8tmp ORDER BY a <-> '-1890.0' LIMIT 3;
+
+INSERT INTO float8tmp VALUES ('NaN'), ('NaN');
+
+SET enable_seqscan=off;
+
+SELECT count(*) FROM float8tmp WHERE a = 'NaN';
+
+RESET enable_seqscan;
```
Any thoughts?

Regards,
Man Zeng








^ permalink  raw  reply  [nested|flat] 2+ messages in thread

* Re:BUG #19501: btree_gist: use float4/float8 comparison functions to handle NaN correctly
  2026-05-29 15:35 BUG #19501: btree_gist: use float4/float8 comparison functions to handle NaN correctly PG Bug reporting form <[email protected]>
@ 2026-06-18 16:43 ` =?utf-8?B?eWx3YW5ndGp1?= <[email protected]>
  0 siblings, 0 replies; 2+ messages in thread

From: =?utf-8?B?eWx3YW5ndGp1?= @ 2026-06-18 16:43 UTC (permalink / raw)
  To: =?utf-8?B?emVuZ21hbg==?= <[email protected]>; =?utf-8?B?cGdzcWwtYnVncw==?= <[email protected]>; +Cc: =?utf-8?B?emVuZ21hbg==?= <[email protected]>

In addition to the index scan inconsistency you described, I found two further security-relevant effects from the same root cause.

Effect 1: EXCLUDE constraint bypass (float4)

CREATE TABLE reservations (

room float4,

during tsrange,

EXCLUDE USING gist (room WITH =, during WITH &amp;&amp;)

);

INSERT INTO reservations VALUES ('NaN'::float4, '[2025-01-01,2025-01-02)');

INSERT INTO reservations VALUES ('NaN'::float4, '[2025-01-01,2025-01-02)');

SELECT COUNT(*) FROM reservations;

Expected: 1 (second insert blocked). Actual: 2. The EXCLUDE constraint is silently bypassed for NaN values, allowing duplicate conflicting rows to be inserted.

Effect 2: RLS bypass (float8)

CREATE TABLE measurements (id int, val float8);

CREATE INDEX ON measurements USING gist (val);

INSERT INTO measurements VALUES (1, 'NaN'), (2, 1.5);

ALTER TABLE measurements ENABLE ROW LEVEL SECURITY;

ALTER TABLE measurements FORCE ROW LEVEL SECURITY;

CREATE POLICY hide_nan ON measurements FOR SELECT USING (val != 'NaN'::float8);

CREATE ROLE lowpriv LOGIN;

GRANT SELECT ON measurements TO lowpriv;

SET ROLE lowpriv;

SET enable_seqscan = off;

SET enable_bitmapscan = off;

SELECT * FROM measurements ORDER BY id;

Expected: only (2, 1.5). Actual: both rows including (1, NaN). The RLS policy is bypassed when the GiST index is used because gbt_float8_consistent() sets *recheck = false unconditionally, so the heap-level RLS filter is never applied.
The fix is right, and I just followed up some other impacts.


         Original
         
       
From:PG Bug reporting form <[email protected]&gt;
Sent Time:May 29, 2026 23:35
To:pgsql-bugs <[email protected]&gt;
Cc:zengman <[email protected]&gt;
Subject:BUG #19501: btree_gist: use float4/float8 comparison functions to handle NaN correctly



       The&nbsp;following&nbsp;bug&nbsp;has&nbsp;been&nbsp;logged&nbsp;on&nbsp;the&nbsp;website:

Bug&nbsp;reference:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;19501
Logged&nbsp;by:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Man&nbsp;Zeng
Email&nbsp;address:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[email protected]
PostgreSQL&nbsp;version:&nbsp;18.4
Operating&nbsp;system:&nbsp;&nbsp;&nbsp;24.04.1-Ubuntu
Description:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;

Hi&nbsp;all,

Here's&nbsp;an&nbsp;interesting&nbsp;case&nbsp;where&nbsp;btree_gist&nbsp;returns&nbsp;inconsistent&nbsp;results
between&nbsp;index&nbsp;scans&nbsp;and&nbsp;sequential&nbsp;scans&nbsp;when&nbsp;NaN&nbsp;values&nbsp;are&nbsp;involved.

```
postgres@zxm-VMware-Virtual-Platform:~/code/zengine$&nbsp;psql
psql&nbsp;(19devel)
Type&nbsp;"help"&nbsp;for&nbsp;help.

test=#&nbsp;CREATE&nbsp;EXTENSION&nbsp;btree_gist;
CREATE&nbsp;EXTENSION
test=#&nbsp;CREATE&nbsp;TABLE&nbsp;test_nan_bug&nbsp;(v&nbsp;float8);
CREATE&nbsp;TABLE
test=#&nbsp;INSERT&nbsp;INTO&nbsp;test_nan_bug&nbsp;VALUES&nbsp;(1.0),&nbsp;(2.0),&nbsp;('NaN'::float8),&nbsp;(3.0);
INSERT&nbsp;0&nbsp;4
test=#&nbsp;CREATE&nbsp;INDEX&nbsp;idx_nan_bug&nbsp;ON&nbsp;test_nan_bug&nbsp;USING&nbsp;gist(v);
CREATE&nbsp;INDEX
test=#&nbsp;SET&nbsp;enable_indexscan=off;
SET
test=#&nbsp;SELECT&nbsp;count(*)&nbsp;FROM&nbsp;test_nan_bug&nbsp;WHERE&nbsp;v&nbsp;=&nbsp;'NaN'::float8;
&nbsp;count
-------
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1
(1&nbsp;row)

test=#&nbsp;EXPLAIN&nbsp;VERBOSE&nbsp;SELECT&nbsp;count(*)&nbsp;FROM&nbsp;test_nan_bug&nbsp;WHERE&nbsp;v&nbsp;=
'NaN'::float8;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;QUERY&nbsp;PLAN
-------------------------------------------------------------------------
&nbsp;Aggregate&nbsp;&nbsp;(cost=1.05..1.06&nbsp;rows=1&nbsp;width=8)
&nbsp;&nbsp;&nbsp;Output:&nbsp;count(*)
&nbsp;&nbsp;&nbsp;-&gt;&nbsp;&nbsp;Seq&nbsp;Scan&nbsp;on&nbsp;public.test_nan_bug&nbsp;&nbsp;(cost=0.00..1.05&nbsp;rows=1&nbsp;width=0)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Output:&nbsp;v
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Filter:&nbsp;(test_nan_bug.v&nbsp;=&nbsp;'NaN'::double&nbsp;precision)
(5&nbsp;rows)

test=#&nbsp;SET&nbsp;enable_indexscan=on;&nbsp;SET&nbsp;enable_seqscan=off;
SET
SET
test=#&nbsp;SELECT&nbsp;count(*)&nbsp;FROM&nbsp;test_nan_bug&nbsp;WHERE&nbsp;v&nbsp;=&nbsp;'NaN'::float8;
&nbsp;count
-------
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;0
(1&nbsp;row)

test=#&nbsp;EXPLAIN&nbsp;VERBOSE&nbsp;SELECT&nbsp;count(*)&nbsp;FROM&nbsp;test_nan_bug&nbsp;WHERE&nbsp;v&nbsp;=
'NaN'::float8;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;QUERY&nbsp;PLAN
--------------------------------------------------------------------------------------------------
&nbsp;Aggregate&nbsp;&nbsp;(cost=8.15..8.16&nbsp;rows=1&nbsp;width=8)
&nbsp;&nbsp;&nbsp;Output:&nbsp;count(*)
&nbsp;&nbsp;&nbsp;-&gt;&nbsp;&nbsp;Index&nbsp;Only&nbsp;Scan&nbsp;using&nbsp;idx_nan_bug&nbsp;on&nbsp;public.test_nan_bug
(cost=0.13..8.15&nbsp;rows=1&nbsp;width=0)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Output:&nbsp;v
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Index&nbsp;Cond:&nbsp;(test_nan_bug.v&nbsp;=&nbsp;'NaN'::double&nbsp;precision)
(5&nbsp;rows)

```

Steps&nbsp;to&nbsp;reproduce:
```sql
CREATE&nbsp;EXTENSION&nbsp;btree_gist;
CREATE&nbsp;TABLE&nbsp;test_nan_bug&nbsp;(v&nbsp;float8);
INSERT&nbsp;INTO&nbsp;test_nan_bug&nbsp;VALUES&nbsp;(1.0),&nbsp;(2.0),&nbsp;('NaN'::float8),&nbsp;(3.0);
CREATE&nbsp;INDEX&nbsp;idx_nan_bug&nbsp;ON&nbsp;test_nan_bug&nbsp;USING&nbsp;gist(v);

SET&nbsp;enable_indexscan=off;
SELECT&nbsp;count(*)&nbsp;FROM&nbsp;test_nan_bug&nbsp;WHERE&nbsp;v&nbsp;=&nbsp;'NaN'::float8;

SET&nbsp;enable_indexscan=on;&nbsp;SET&nbsp;enable_seqscan=off;
SELECT&nbsp;count(*)&nbsp;FROM&nbsp;test_nan_bug&nbsp;WHERE&nbsp;v&nbsp;=&nbsp;'NaN'::float8;
```

My&nbsp;fix&nbsp;replaces&nbsp;the&nbsp;C&nbsp;comparison&nbsp;operators&nbsp;with&nbsp;PostgreSQL's&nbsp;float
comparison&nbsp;functions&nbsp;(float8_gt,&nbsp;float8_ge,&nbsp;float8_eq,&nbsp;etc.)&nbsp;and
float8_cmp_internal,&nbsp;which&nbsp;handle&nbsp;NaN&nbsp;consistently.

```
diff&nbsp;--git&nbsp;a/contrib/btree_gist/btree_float4.c
b/contrib/btree_gist/btree_float4.c
index&nbsp;c076918fd48..eaecdac0980&nbsp;100644
---&nbsp;a/contrib/btree_gist/btree_float4.c
+++&nbsp;b/contrib/btree_gist/btree_float4.c
@@&nbsp;-29,27&nbsp;+29,27&nbsp;@@&nbsp;PG_FUNCTION_INFO_V1(gbt_float4_sortsupport);
&nbsp;static&nbsp;bool
&nbsp;gbt_float4gt(const&nbsp;void&nbsp;*a,&nbsp;const&nbsp;void&nbsp;*b,&nbsp;FmgrInfo&nbsp;*flinfo)
&nbsp;{
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;(*((const&nbsp;float4&nbsp;*)&nbsp;a)&nbsp;&gt;&nbsp;*((const&nbsp;float4&nbsp;*)&nbsp;b));
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;float4_gt(*(const&nbsp;float4&nbsp;*)&nbsp;a,&nbsp;*(const&nbsp;float4&nbsp;*)&nbsp;b);
&nbsp;}
&nbsp;static&nbsp;bool
&nbsp;gbt_float4ge(const&nbsp;void&nbsp;*a,&nbsp;const&nbsp;void&nbsp;*b,&nbsp;FmgrInfo&nbsp;*flinfo)
&nbsp;{
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;(*((const&nbsp;float4&nbsp;*)&nbsp;a)&nbsp;&gt;=&nbsp;*((const&nbsp;float4&nbsp;*)&nbsp;b));
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;float4_ge(*(const&nbsp;float4&nbsp;*)&nbsp;a,&nbsp;*(const&nbsp;float4&nbsp;*)&nbsp;b);
&nbsp;}
&nbsp;static&nbsp;bool
&nbsp;gbt_float4eq(const&nbsp;void&nbsp;*a,&nbsp;const&nbsp;void&nbsp;*b,&nbsp;FmgrInfo&nbsp;*flinfo)
&nbsp;{
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;(*((const&nbsp;float4&nbsp;*)&nbsp;a)&nbsp;==&nbsp;*((const&nbsp;float4&nbsp;*)&nbsp;b));
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;float4_eq(*(const&nbsp;float4&nbsp;*)&nbsp;a,&nbsp;*(const&nbsp;float4&nbsp;*)&nbsp;b);
&nbsp;}
&nbsp;static&nbsp;bool
&nbsp;gbt_float4le(const&nbsp;void&nbsp;*a,&nbsp;const&nbsp;void&nbsp;*b,&nbsp;FmgrInfo&nbsp;*flinfo)
&nbsp;{
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;(*((const&nbsp;float4&nbsp;*)&nbsp;a)&nbsp;<=&nbsp;*((const&nbsp;float4&nbsp;*)&nbsp;b));
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;float4_le(*(const&nbsp;float4&nbsp;*)&nbsp;a,&nbsp;*(const&nbsp;float4&nbsp;*)&nbsp;b);
&nbsp;}
&nbsp;static&nbsp;bool
&nbsp;gbt_float4lt(const&nbsp;void&nbsp;*a,&nbsp;const&nbsp;void&nbsp;*b,&nbsp;FmgrInfo&nbsp;*flinfo)
&nbsp;{
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;(*((const&nbsp;float4&nbsp;*)&nbsp;a)&nbsp;<&nbsp;*((const&nbsp;float4&nbsp;*)&nbsp;b));
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;float4_lt(*(const&nbsp;float4&nbsp;*)&nbsp;a,&nbsp;*(const&nbsp;float4&nbsp;*)&nbsp;b);
&nbsp;}
&nbsp;
&nbsp;static&nbsp;int
@@&nbsp;-57,16&nbsp;+57,12&nbsp;@@&nbsp;gbt_float4key_cmp(const&nbsp;void&nbsp;*a,&nbsp;const&nbsp;void&nbsp;*b,&nbsp;FmgrInfo
*flinfo)
&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;float4KEY&nbsp;&nbsp;*ia&nbsp;=&nbsp;(float4KEY&nbsp;*)&nbsp;(((const&nbsp;Nsrt&nbsp;*)&nbsp;a)-&gt;t);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;float4KEY&nbsp;&nbsp;*ib&nbsp;=&nbsp;(float4KEY&nbsp;*)&nbsp;(((const&nbsp;Nsrt&nbsp;*)&nbsp;b)-&gt;t);
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cmp;
&nbsp;
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(ia-&gt;lower&nbsp;==&nbsp;ib-&gt;lower)
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(ia-&gt;upper&nbsp;==&nbsp;ib-&gt;upper)
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;0;
-
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;(ia-&gt;upper&nbsp;&gt;&nbsp;ib-&gt;upper)&nbsp;?&nbsp;1&nbsp;:&nbsp;-1;
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
-
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;(ia-&gt;lower&nbsp;&gt;&nbsp;ib-&gt;lower)&nbsp;?&nbsp;1&nbsp;:&nbsp;-1;
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cmp&nbsp;=&nbsp;float4_cmp_internal(ia-&gt;lower,&nbsp;ib-&gt;lower);
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(cmp&nbsp;!=&nbsp;0)
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;cmp;
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;float4_cmp_internal(ia-&gt;upper,&nbsp;ib-&gt;upper);
&nbsp;}
&nbsp;
&nbsp;static&nbsp;float8
diff&nbsp;--git&nbsp;a/contrib/btree_gist/btree_float8.c
b/contrib/btree_gist/btree_float8.c
index&nbsp;d7386e885a2..132065a648c&nbsp;100644
---&nbsp;a/contrib/btree_gist/btree_float8.c
+++&nbsp;b/contrib/btree_gist/btree_float8.c
@@&nbsp;-30,27&nbsp;+30,27&nbsp;@@&nbsp;PG_FUNCTION_INFO_V1(gbt_float8_sortsupport);
&nbsp;static&nbsp;bool
&nbsp;gbt_float8gt(const&nbsp;void&nbsp;*a,&nbsp;const&nbsp;void&nbsp;*b,&nbsp;FmgrInfo&nbsp;*flinfo)
&nbsp;{
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;(*((const&nbsp;float8&nbsp;*)&nbsp;a)&nbsp;&gt;&nbsp;*((const&nbsp;float8&nbsp;*)&nbsp;b));
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;float8_gt(*(const&nbsp;float8&nbsp;*)&nbsp;a,&nbsp;*(const&nbsp;float8&nbsp;*)&nbsp;b);
&nbsp;}
&nbsp;static&nbsp;bool
&nbsp;gbt_float8ge(const&nbsp;void&nbsp;*a,&nbsp;const&nbsp;void&nbsp;*b,&nbsp;FmgrInfo&nbsp;*flinfo)
&nbsp;{
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;(*((const&nbsp;float8&nbsp;*)&nbsp;a)&nbsp;&gt;=&nbsp;*((const&nbsp;float8&nbsp;*)&nbsp;b));
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;float8_ge(*(const&nbsp;float8&nbsp;*)&nbsp;a,&nbsp;*(const&nbsp;float8&nbsp;*)&nbsp;b);
&nbsp;}
&nbsp;static&nbsp;bool
&nbsp;gbt_float8eq(const&nbsp;void&nbsp;*a,&nbsp;const&nbsp;void&nbsp;*b,&nbsp;FmgrInfo&nbsp;*flinfo)
&nbsp;{
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;(*((const&nbsp;float8&nbsp;*)&nbsp;a)&nbsp;==&nbsp;*((const&nbsp;float8&nbsp;*)&nbsp;b));
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;float8_eq(*(const&nbsp;float8&nbsp;*)&nbsp;a,&nbsp;*(const&nbsp;float8&nbsp;*)&nbsp;b);
&nbsp;}
&nbsp;static&nbsp;bool
&nbsp;gbt_float8le(const&nbsp;void&nbsp;*a,&nbsp;const&nbsp;void&nbsp;*b,&nbsp;FmgrInfo&nbsp;*flinfo)
&nbsp;{
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;(*((const&nbsp;float8&nbsp;*)&nbsp;a)&nbsp;<=&nbsp;*((const&nbsp;float8&nbsp;*)&nbsp;b));
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;float8_le(*(const&nbsp;float8&nbsp;*)&nbsp;a,&nbsp;*(const&nbsp;float8&nbsp;*)&nbsp;b);
&nbsp;}
&nbsp;static&nbsp;bool
&nbsp;gbt_float8lt(const&nbsp;void&nbsp;*a,&nbsp;const&nbsp;void&nbsp;*b,&nbsp;FmgrInfo&nbsp;*flinfo)
&nbsp;{
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;(*((const&nbsp;float8&nbsp;*)&nbsp;a)&nbsp;<&nbsp;*((const&nbsp;float8&nbsp;*)&nbsp;b));
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;float8_lt(*(const&nbsp;float8&nbsp;*)&nbsp;a,&nbsp;*(const&nbsp;float8&nbsp;*)&nbsp;b);
&nbsp;}
&nbsp;
&nbsp;static&nbsp;int
@@&nbsp;-58,16&nbsp;+58,12&nbsp;@@&nbsp;gbt_float8key_cmp(const&nbsp;void&nbsp;*a,&nbsp;const&nbsp;void&nbsp;*b,&nbsp;FmgrInfo
*flinfo)
&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;float8KEY&nbsp;&nbsp;*ia&nbsp;=&nbsp;(float8KEY&nbsp;*)&nbsp;(((const&nbsp;Nsrt&nbsp;*)&nbsp;a)-&gt;t);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;float8KEY&nbsp;&nbsp;*ib&nbsp;=&nbsp;(float8KEY&nbsp;*)&nbsp;(((const&nbsp;Nsrt&nbsp;*)&nbsp;b)-&gt;t);
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cmp;
&nbsp;
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(ia-&gt;lower&nbsp;==&nbsp;ib-&gt;lower)
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(ia-&gt;upper&nbsp;==&nbsp;ib-&gt;upper)
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;0;
-
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;(ia-&gt;upper&nbsp;&gt;&nbsp;ib-&gt;upper)&nbsp;?&nbsp;1&nbsp;:&nbsp;-1;
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
-
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;(ia-&gt;lower&nbsp;&gt;&nbsp;ib-&gt;lower)&nbsp;?&nbsp;1&nbsp;:&nbsp;-1;
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cmp&nbsp;=&nbsp;float8_cmp_internal(ia-&gt;lower,&nbsp;ib-&gt;lower);
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(cmp&nbsp;!=&nbsp;0)
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;cmp;
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;float8_cmp_internal(ia-&gt;upper,&nbsp;ib-&gt;upper);
&nbsp;}
&nbsp;
&nbsp;static&nbsp;float8
diff&nbsp;--git&nbsp;a/contrib/btree_gist/expected/float4.out
b/contrib/btree_gist/expected/float4.out
index&nbsp;dfe732049e6..99ed6ae4668&nbsp;100644
---&nbsp;a/contrib/btree_gist/expected/float4.out
+++&nbsp;b/contrib/btree_gist/expected/float4.out
@@&nbsp;-89,3&nbsp;+89,12&nbsp;@@&nbsp;SELECT&nbsp;a,&nbsp;a&nbsp;<-&gt;&nbsp;'-179.0'&nbsp;FROM&nbsp;float4tmp&nbsp;ORDER&nbsp;BY&nbsp;a&nbsp;<-&gt;
'-179.0'&nbsp;LIMIT&nbsp;3;
&nbsp;&nbsp;-158.17741&nbsp;|&nbsp;20.822586
&nbsp;(3&nbsp;rows)
&nbsp;
+INSERT&nbsp;INTO&nbsp;float4tmp&nbsp;VALUES&nbsp;('NaN'),&nbsp;('NaN');
+SET&nbsp;enable_seqscan=off;
+SELECT&nbsp;count(*)&nbsp;FROM&nbsp;float4tmp&nbsp;WHERE&nbsp;a&nbsp;=&nbsp;'NaN';
+&nbsp;count
+-------
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2
+(1&nbsp;row)
+
+RESET&nbsp;enable_seqscan;
diff&nbsp;--git&nbsp;a/contrib/btree_gist/expected/float8.out
b/contrib/btree_gist/expected/float8.out
index&nbsp;ebd0ef3d689..e463b8869d6&nbsp;100644
---&nbsp;a/contrib/btree_gist/expected/float8.out
+++&nbsp;b/contrib/btree_gist/expected/float8.out
@@&nbsp;-89,3&nbsp;+89,12&nbsp;@@&nbsp;SELECT&nbsp;a,&nbsp;a&nbsp;<-&gt;&nbsp;'-1890.0'&nbsp;FROM&nbsp;float8tmp&nbsp;ORDER&nbsp;BY&nbsp;a&nbsp;<-&gt;
'-1890.0'&nbsp;LIMIT&nbsp;3;
&nbsp;&nbsp;&nbsp;-1769.73634&nbsp;|&nbsp;120.26366000000007
&nbsp;(3&nbsp;rows)
&nbsp;
+INSERT&nbsp;INTO&nbsp;float8tmp&nbsp;VALUES&nbsp;('NaN'),&nbsp;('NaN');
+SET&nbsp;enable_seqscan=off;
+SELECT&nbsp;count(*)&nbsp;FROM&nbsp;float8tmp&nbsp;WHERE&nbsp;a&nbsp;=&nbsp;'NaN';
+&nbsp;count
+-------
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2
+(1&nbsp;row)
+
+RESET&nbsp;enable_seqscan;
diff&nbsp;--git&nbsp;a/contrib/btree_gist/sql/float4.sql
b/contrib/btree_gist/sql/float4.sql
index&nbsp;3da1ce953c8..0cacee08276&nbsp;100644
---&nbsp;a/contrib/btree_gist/sql/float4.sql
+++&nbsp;b/contrib/btree_gist/sql/float4.sql
@@&nbsp;-35,3&nbsp;+35,11&nbsp;@@&nbsp;SELECT&nbsp;count(*)&nbsp;FROM&nbsp;float4tmp&nbsp;WHERE&nbsp;a&nbsp;&gt;&nbsp;&nbsp;-179.0::float4;
&nbsp;EXPLAIN&nbsp;(COSTS&nbsp;OFF)
&nbsp;SELECT&nbsp;a,&nbsp;a&nbsp;<-&gt;&nbsp;'-179.0'&nbsp;FROM&nbsp;float4tmp&nbsp;ORDER&nbsp;BY&nbsp;a&nbsp;<-&gt;&nbsp;'-179.0'&nbsp;LIMIT&nbsp;3;
&nbsp;SELECT&nbsp;a,&nbsp;a&nbsp;<-&gt;&nbsp;'-179.0'&nbsp;FROM&nbsp;float4tmp&nbsp;ORDER&nbsp;BY&nbsp;a&nbsp;<-&gt;&nbsp;'-179.0'&nbsp;LIMIT&nbsp;3;
+
+INSERT&nbsp;INTO&nbsp;float4tmp&nbsp;VALUES&nbsp;('NaN'),&nbsp;('NaN');
+
+SET&nbsp;enable_seqscan=off;
+
+SELECT&nbsp;count(*)&nbsp;FROM&nbsp;float4tmp&nbsp;WHERE&nbsp;a&nbsp;=&nbsp;'NaN';
+
+RESET&nbsp;enable_seqscan;
diff&nbsp;--git&nbsp;a/contrib/btree_gist/sql/float8.sql
b/contrib/btree_gist/sql/float8.sql
index&nbsp;e1e819b37f9..2d6ef9d95e7&nbsp;100644
---&nbsp;a/contrib/btree_gist/sql/float8.sql
+++&nbsp;b/contrib/btree_gist/sql/float8.sql
@@&nbsp;-35,3&nbsp;+35,11&nbsp;@@&nbsp;SELECT&nbsp;count(*)&nbsp;FROM&nbsp;float8tmp&nbsp;WHERE&nbsp;a&nbsp;&gt;
-1890.0::float8;
&nbsp;EXPLAIN&nbsp;(COSTS&nbsp;OFF)
&nbsp;SELECT&nbsp;a,&nbsp;a&nbsp;<-&gt;&nbsp;'-1890.0'&nbsp;FROM&nbsp;float8tmp&nbsp;ORDER&nbsp;BY&nbsp;a&nbsp;<-&gt;&nbsp;'-1890.0'&nbsp;LIMIT&nbsp;3;
&nbsp;SELECT&nbsp;a,&nbsp;a&nbsp;<-&gt;&nbsp;'-1890.0'&nbsp;FROM&nbsp;float8tmp&nbsp;ORDER&nbsp;BY&nbsp;a&nbsp;<-&gt;&nbsp;'-1890.0'&nbsp;LIMIT&nbsp;3;
+
+INSERT&nbsp;INTO&nbsp;float8tmp&nbsp;VALUES&nbsp;('NaN'),&nbsp;('NaN');
+
+SET&nbsp;enable_seqscan=off;
+
+SELECT&nbsp;count(*)&nbsp;FROM&nbsp;float8tmp&nbsp;WHERE&nbsp;a&nbsp;=&nbsp;'NaN';
+
+RESET&nbsp;enable_seqscan;
```
Any&nbsp;thoughts?

Regards,
Man&nbsp;Zeng

^ permalink  raw  reply  [nested|flat] 2+ messages in thread


end of thread, other threads:[~2026-06-18 16:43 UTC | newest]

Thread overview: 2+ messages (download: mbox mbox.gz follow: Atom feed)
-- links below jump to the message on this page --
2026-05-29 15:35 BUG #19501: btree_gist: use float4/float8 comparison functions to handle NaN correctly PG Bug reporting form <[email protected]>
2026-06-18 16:43 ` =?utf-8?B?eWx3YW5ndGp1?= <[email protected]>

This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox