From 238042d6e7b6a4d534516237f014ee836d3fec8b Mon Sep 17 00:00:00 2001
From: Tom Lane <tgl@sss.pgh.pa.us>
Date: Thu, 27 Mar 2025 21:09:00 -0400
Subject: [PATCH v3 4/6] Add cross-type comparisons for float types.

---
 contrib/btree_gin/btree_gin--1.3--1.4.sql |  20 ++
 contrib/btree_gin/btree_gin.c             |  44 ++++-
 contrib/btree_gin/expected/float4.out     | 227 ++++++++++++++++++++++
 contrib/btree_gin/expected/float8.out     |  50 +++++
 contrib/btree_gin/sql/float4.sql          |  37 ++++
 contrib/btree_gin/sql/float8.sql          |   9 +
 6 files changed, 381 insertions(+), 6 deletions(-)

diff --git a/contrib/btree_gin/btree_gin--1.3--1.4.sql b/contrib/btree_gin/btree_gin--1.3--1.4.sql
index 71e577de2d7..88ed5d5b3ea 100644
--- a/contrib/btree_gin/btree_gin--1.3--1.4.sql
+++ b/contrib/btree_gin/btree_gin--1.3--1.4.sql
@@ -61,3 +61,23 @@ ADD
     OPERATOR        20      >= (int8, int4),
     OPERATOR        21      > (int8, int4)
 ;
+
+ALTER OPERATOR FAMILY float4_ops USING gin
+ADD
+    -- Code 1: RHS is float8
+    OPERATOR        9       < (float4, float8),
+    OPERATOR        10      <= (float4, float8),
+    OPERATOR        11      = (float4, float8),
+    OPERATOR        12      >= (float4, float8),
+    OPERATOR        13      > (float4, float8)
+;
+
+ALTER OPERATOR FAMILY float8_ops USING gin
+ADD
+    -- Code 1: RHS is float4
+    OPERATOR        9       < (float8, float4),
+    OPERATOR        10      <= (float8, float4),
+    OPERATOR        11      = (float8, float4),
+    OPERATOR        12      >= (float8, float4),
+    OPERATOR        13      > (float8, float4)
+;
diff --git a/contrib/btree_gin/btree_gin.c b/contrib/btree_gin/btree_gin.c
index d0225c27561..ae6ffa07953 100644
--- a/contrib/btree_gin/btree_gin.c
+++ b/contrib/btree_gin/btree_gin.c
@@ -393,13 +393,34 @@ leftmostvalue_float4(void)
 	return Float4GetDatum(-get_float4_infinity());
 }
 
+static Datum
+cvt_float8_float4(Datum input)
+{
+	float8		val = DatumGetFloat8(input);
+	float4		result;
+
+	/*
+	 * Assume that ordinary C conversion will produce a usable result.
+	 * (Compare dtof(), which raises error conditions that we don't need.)
+	 * Note that for inputs that aren't exactly representable as float4, it
+	 * doesn't matter whether the conversion rounds up or down.  That might
+	 * cause us to scan a few index entries that we'll reject as not matching,
+	 * but we won't miss any that should match.
+	 */
+	result = (float4) val;
+	return Float4GetDatum(result);
+}
+
 static const bool float4_rhs_is_varlena[] =
-{false};
+{false, false};
+
+static const btree_gin_convert_function float4_cvt_fns[] =
+{NULL, cvt_float8_float4};
 
 static const PGFunction float4_cmp_fns[] =
-{btfloat4cmp};
+{btfloat4cmp, btfloat84cmp};
 
-GIN_SUPPORT(float4, leftmostvalue_float4, float4_rhs_is_varlena, NULL, float4_cmp_fns)
+GIN_SUPPORT(float4, leftmostvalue_float4, float4_rhs_is_varlena, float4_cvt_fns, float4_cmp_fns)
 
 static Datum
 leftmostvalue_float8(void)
@@ -407,13 +428,24 @@ leftmostvalue_float8(void)
 	return Float8GetDatum(-get_float8_infinity());
 }
 
+static Datum
+cvt_float4_float8(Datum input)
+{
+	float4		val = DatumGetFloat4(input);
+
+	return Float8GetDatum((float8) val);
+}
+
 static const bool float8_rhs_is_varlena[] =
-{false};
+{false, false};
+
+static const btree_gin_convert_function float8_cvt_fns[] =
+{NULL, cvt_float4_float8};
 
 static const PGFunction float8_cmp_fns[] =
-{btfloat8cmp};
+{btfloat8cmp, btfloat48cmp};
 
-GIN_SUPPORT(float8, leftmostvalue_float8, float8_rhs_is_varlena, NULL, float8_cmp_fns)
+GIN_SUPPORT(float8, leftmostvalue_float8, float8_rhs_is_varlena, float8_cvt_fns, float8_cmp_fns)
 
 static Datum
 leftmostvalue_money(void)
diff --git a/contrib/btree_gin/expected/float4.out b/contrib/btree_gin/expected/float4.out
index 7b9134fcd4b..02c88e4007d 100644
--- a/contrib/btree_gin/expected/float4.out
+++ b/contrib/btree_gin/expected/float4.out
@@ -42,3 +42,230 @@ SELECT * FROM test_float4 WHERE i>1::float4 ORDER BY i;
  3
 (2 rows)
 
+explain (costs off)
+SELECT * FROM test_float4 WHERE i<1::float8 ORDER BY i;
+                      QUERY PLAN                       
+-------------------------------------------------------
+ Sort
+   Sort Key: i
+   ->  Bitmap Heap Scan on test_float4
+         Recheck Cond: (i < '1'::double precision)
+         ->  Bitmap Index Scan on idx_float4
+               Index Cond: (i < '1'::double precision)
+(6 rows)
+
+SELECT * FROM test_float4 WHERE i<1::float8 ORDER BY i;
+ i  
+----
+ -2
+ -1
+  0
+(3 rows)
+
+SELECT * FROM test_float4 WHERE i<=1::float8 ORDER BY i;
+ i  
+----
+ -2
+ -1
+  0
+  1
+(4 rows)
+
+SELECT * FROM test_float4 WHERE i=1::float8 ORDER BY i;
+ i 
+---
+ 1
+(1 row)
+
+SELECT * FROM test_float4 WHERE i>=1::float8 ORDER BY i;
+ i 
+---
+ 1
+ 2
+ 3
+(3 rows)
+
+SELECT * FROM test_float4 WHERE i>1::float8 ORDER BY i;
+ i 
+---
+ 2
+ 3
+(2 rows)
+
+-- Check endpoint and out-of-range cases
+INSERT INTO test_float4 VALUES ('NaN'), ('Inf'), ('-Inf');
+SELECT * FROM test_float4 WHERE i<'-Inf'::float8 ORDER BY i;
+ i 
+---
+(0 rows)
+
+SELECT * FROM test_float4 WHERE i<='-Inf'::float8 ORDER BY i;
+     i     
+-----------
+ -Infinity
+(1 row)
+
+SELECT * FROM test_float4 WHERE i='-Inf'::float8 ORDER BY i;
+     i     
+-----------
+ -Infinity
+(1 row)
+
+SELECT * FROM test_float4 WHERE i>='-Inf'::float8 ORDER BY i;
+     i     
+-----------
+ -Infinity
+        -2
+        -1
+         0
+         1
+         2
+         3
+  Infinity
+       NaN
+(9 rows)
+
+SELECT * FROM test_float4 WHERE i>'-Inf'::float8 ORDER BY i;
+    i     
+----------
+       -2
+       -1
+        0
+        1
+        2
+        3
+ Infinity
+      NaN
+(8 rows)
+
+SELECT * FROM test_float4 WHERE i<'Inf'::float8 ORDER BY i;
+     i     
+-----------
+ -Infinity
+        -2
+        -1
+         0
+         1
+         2
+         3
+(7 rows)
+
+SELECT * FROM test_float4 WHERE i<='Inf'::float8 ORDER BY i;
+     i     
+-----------
+ -Infinity
+        -2
+        -1
+         0
+         1
+         2
+         3
+  Infinity
+(8 rows)
+
+SELECT * FROM test_float4 WHERE i='Inf'::float8 ORDER BY i;
+    i     
+----------
+ Infinity
+(1 row)
+
+SELECT * FROM test_float4 WHERE i>='Inf'::float8 ORDER BY i;
+    i     
+----------
+ Infinity
+      NaN
+(2 rows)
+
+SELECT * FROM test_float4 WHERE i>'Inf'::float8 ORDER BY i;
+  i  
+-----
+ NaN
+(1 row)
+
+SELECT * FROM test_float4 WHERE i<'1e300'::float8 ORDER BY i;
+     i     
+-----------
+ -Infinity
+        -2
+        -1
+         0
+         1
+         2
+         3
+(7 rows)
+
+SELECT * FROM test_float4 WHERE i<='1e300'::float8 ORDER BY i;
+     i     
+-----------
+ -Infinity
+        -2
+        -1
+         0
+         1
+         2
+         3
+(7 rows)
+
+SELECT * FROM test_float4 WHERE i='1e300'::float8 ORDER BY i;
+ i 
+---
+(0 rows)
+
+SELECT * FROM test_float4 WHERE i>='1e300'::float8 ORDER BY i;
+    i     
+----------
+ Infinity
+      NaN
+(2 rows)
+
+SELECT * FROM test_float4 WHERE i>'1e300'::float8 ORDER BY i;
+    i     
+----------
+ Infinity
+      NaN
+(2 rows)
+
+SELECT * FROM test_float4 WHERE i<'NaN'::float8 ORDER BY i;
+     i     
+-----------
+ -Infinity
+        -2
+        -1
+         0
+         1
+         2
+         3
+  Infinity
+(8 rows)
+
+SELECT * FROM test_float4 WHERE i<='NaN'::float8 ORDER BY i;
+     i     
+-----------
+ -Infinity
+        -2
+        -1
+         0
+         1
+         2
+         3
+  Infinity
+       NaN
+(9 rows)
+
+SELECT * FROM test_float4 WHERE i='NaN'::float8 ORDER BY i;
+  i  
+-----
+ NaN
+(1 row)
+
+SELECT * FROM test_float4 WHERE i>='NaN'::float8 ORDER BY i;
+  i  
+-----
+ NaN
+(1 row)
+
+SELECT * FROM test_float4 WHERE i>'NaN'::float8 ORDER BY i;
+ i 
+---
+(0 rows)
+
diff --git a/contrib/btree_gin/expected/float8.out b/contrib/btree_gin/expected/float8.out
index a41d4f9f6bb..b2877dfa3c1 100644
--- a/contrib/btree_gin/expected/float8.out
+++ b/contrib/btree_gin/expected/float8.out
@@ -42,3 +42,53 @@ SELECT * FROM test_float8 WHERE i>1::float8 ORDER BY i;
  3
 (2 rows)
 
+explain (costs off)
+SELECT * FROM test_float8 WHERE i<1::float4 ORDER BY i;
+                 QUERY PLAN                  
+---------------------------------------------
+ Sort
+   Sort Key: i
+   ->  Bitmap Heap Scan on test_float8
+         Recheck Cond: (i < '1'::real)
+         ->  Bitmap Index Scan on idx_float8
+               Index Cond: (i < '1'::real)
+(6 rows)
+
+SELECT * FROM test_float8 WHERE i<1::float4 ORDER BY i;
+ i  
+----
+ -2
+ -1
+  0
+(3 rows)
+
+SELECT * FROM test_float8 WHERE i<=1::float4 ORDER BY i;
+ i  
+----
+ -2
+ -1
+  0
+  1
+(4 rows)
+
+SELECT * FROM test_float8 WHERE i=1::float4 ORDER BY i;
+ i 
+---
+ 1
+(1 row)
+
+SELECT * FROM test_float8 WHERE i>=1::float4 ORDER BY i;
+ i 
+---
+ 1
+ 2
+ 3
+(3 rows)
+
+SELECT * FROM test_float8 WHERE i>1::float4 ORDER BY i;
+ i 
+---
+ 2
+ 3
+(2 rows)
+
diff --git a/contrib/btree_gin/sql/float4.sql b/contrib/btree_gin/sql/float4.sql
index 759778ad3c3..e850e1a0588 100644
--- a/contrib/btree_gin/sql/float4.sql
+++ b/contrib/btree_gin/sql/float4.sql
@@ -13,3 +13,40 @@ SELECT * FROM test_float4 WHERE i<=1::float4 ORDER BY i;
 SELECT * FROM test_float4 WHERE i=1::float4 ORDER BY i;
 SELECT * FROM test_float4 WHERE i>=1::float4 ORDER BY i;
 SELECT * FROM test_float4 WHERE i>1::float4 ORDER BY i;
+
+explain (costs off)
+SELECT * FROM test_float4 WHERE i<1::float8 ORDER BY i;
+
+SELECT * FROM test_float4 WHERE i<1::float8 ORDER BY i;
+SELECT * FROM test_float4 WHERE i<=1::float8 ORDER BY i;
+SELECT * FROM test_float4 WHERE i=1::float8 ORDER BY i;
+SELECT * FROM test_float4 WHERE i>=1::float8 ORDER BY i;
+SELECT * FROM test_float4 WHERE i>1::float8 ORDER BY i;
+
+-- Check endpoint and out-of-range cases
+
+INSERT INTO test_float4 VALUES ('NaN'), ('Inf'), ('-Inf');
+
+SELECT * FROM test_float4 WHERE i<'-Inf'::float8 ORDER BY i;
+SELECT * FROM test_float4 WHERE i<='-Inf'::float8 ORDER BY i;
+SELECT * FROM test_float4 WHERE i='-Inf'::float8 ORDER BY i;
+SELECT * FROM test_float4 WHERE i>='-Inf'::float8 ORDER BY i;
+SELECT * FROM test_float4 WHERE i>'-Inf'::float8 ORDER BY i;
+
+SELECT * FROM test_float4 WHERE i<'Inf'::float8 ORDER BY i;
+SELECT * FROM test_float4 WHERE i<='Inf'::float8 ORDER BY i;
+SELECT * FROM test_float4 WHERE i='Inf'::float8 ORDER BY i;
+SELECT * FROM test_float4 WHERE i>='Inf'::float8 ORDER BY i;
+SELECT * FROM test_float4 WHERE i>'Inf'::float8 ORDER BY i;
+
+SELECT * FROM test_float4 WHERE i<'1e300'::float8 ORDER BY i;
+SELECT * FROM test_float4 WHERE i<='1e300'::float8 ORDER BY i;
+SELECT * FROM test_float4 WHERE i='1e300'::float8 ORDER BY i;
+SELECT * FROM test_float4 WHERE i>='1e300'::float8 ORDER BY i;
+SELECT * FROM test_float4 WHERE i>'1e300'::float8 ORDER BY i;
+
+SELECT * FROM test_float4 WHERE i<'NaN'::float8 ORDER BY i;
+SELECT * FROM test_float4 WHERE i<='NaN'::float8 ORDER BY i;
+SELECT * FROM test_float4 WHERE i='NaN'::float8 ORDER BY i;
+SELECT * FROM test_float4 WHERE i>='NaN'::float8 ORDER BY i;
+SELECT * FROM test_float4 WHERE i>'NaN'::float8 ORDER BY i;
diff --git a/contrib/btree_gin/sql/float8.sql b/contrib/btree_gin/sql/float8.sql
index b046ac4e6c4..5f393147082 100644
--- a/contrib/btree_gin/sql/float8.sql
+++ b/contrib/btree_gin/sql/float8.sql
@@ -13,3 +13,12 @@ SELECT * FROM test_float8 WHERE i<=1::float8 ORDER BY i;
 SELECT * FROM test_float8 WHERE i=1::float8 ORDER BY i;
 SELECT * FROM test_float8 WHERE i>=1::float8 ORDER BY i;
 SELECT * FROM test_float8 WHERE i>1::float8 ORDER BY i;
+
+explain (costs off)
+SELECT * FROM test_float8 WHERE i<1::float4 ORDER BY i;
+
+SELECT * FROM test_float8 WHERE i<1::float4 ORDER BY i;
+SELECT * FROM test_float8 WHERE i<=1::float4 ORDER BY i;
+SELECT * FROM test_float8 WHERE i=1::float4 ORDER BY i;
+SELECT * FROM test_float8 WHERE i>=1::float4 ORDER BY i;
+SELECT * FROM test_float8 WHERE i>1::float4 ORDER BY i;
-- 
2.43.5

