From: Andrey Rachitskiy <pl0h0yp1@gmail.com>
Date: Mon, 3 Aug 2026 02:15:00 +0500
Subject: [PATCH v2] Fix reachable "impossible case" in SP-GiST quad tree

The fuzzy point comparisons used by getQuadrant() are not mutually
exhaustive near some power-of-two boundaries.  Fall back to exact
comparisons when none of the existing quadrant tests match, keeping
the documented axis tie-breaking.  Leave the elog for NaN.

Author: Ayush Tiwari <ayushtiwari.slg01@gmail.com>
Reviewed-by: Andrey Rachitskiy <pl0h0yp1@gmail.com>
Reported-by: Michael Malis <malis@pgrust.com>
Discussion: https://postgr.es/m/19597-39c532e61d78dff6@postgresql.org
Backpatch-through: 14
---
diff --git a/src/backend/access/spgist/spgquadtreeproc.c b/src/backend/access/spgist/spgquadtreeproc.c
index 946dabc4527..2e8610f5740 100644
--- a/src/backend/access/spgist/spgquadtreeproc.c
+++ b/src/backend/access/spgist/spgquadtreeproc.c
@@ -52,6 +52,11 @@ spg_quad_config(PG_FUNCTION_ARGS)
  *
  * Points on one of the axes are taken to lie in the lowest-numbered
  * adjacent quadrant.
+ *
+ * We normally use the fuzzy point_* operators, but those are not always
+ * decisive (see FPeq/FPlt/FPgt).  If no arm matches, fall back to exact
+ * comparisons with the same axis rule as above.  NaN still reaches the
+ * error below; cleaning that up is a separate matter.
  */
 static int16
 getQuadrant(Point *centroid, Point *tst)
@@ -76,6 +81,17 @@ getQuadrant(Point *centroid, Point *tst)
 		SPTEST(point_left, tst, centroid))
 		return 4;
 
+	/*
+	 * Fuzzy comparisons can leave gaps for finite values.  Fall back to
+	 * exact comparisons with the same axis tie-breaking as above.
+	 */
+	if (tst->y > centroid->y)
+		return (tst->x >= centroid->x) ? 1 : 4;
+	if (tst->y < centroid->y)
+		return (tst->x >= centroid->x) ? 2 : 3;
+	if (tst->y == centroid->y)
+		return (tst->x >= centroid->x) ? 1 : 3;
+
 	elog(ERROR, "getQuadrant: impossible case");
 	return 0;
 }
diff --git a/src/test/regress/expected/spgist.out b/src/test/regress/expected/spgist.out
index 2e911285600..1ec57562484 100644
--- a/src/test/regress/expected/spgist.out
+++ b/src/test/regress/expected/spgist.out
@@ -94,3 +94,18 @@ select box(point(i,j))
   from generate_series(1,100,5) i,
        generate_series(1,10,5) j;
 -- leave this table around, to help in testing dump/restore
+-- Check getQuadrant with large coords near a power-of-two boundary (bug #19597)
+create table spgist_quad_fp_tbl(p point);
+insert into spgist_quad_fp_tbl
+  select point(17179869183.999998, 17179869183.999998)
+  from generate_series(1, 4000);
+create index spgist_quad_fp_idx on spgist_quad_fp_tbl using spgist(p);
+insert into spgist_quad_fp_tbl
+  select point(17179869184.0, 17179869184.0)
+  from generate_series(1, 400);
+select count(*) from spgist_quad_fp_tbl where p ~= point(17179869184.0, 17179869184.0);
+ count 
+-------
+   400
+(1 row)
+
diff --git a/src/test/regress/sql/spgist.sql b/src/test/regress/sql/spgist.sql
index 4828ede68c3..f89f879e3a9 100644
--- a/src/test/regress/sql/spgist.sql
+++ b/src/test/regress/sql/spgist.sql
@@ -89,3 +89,13 @@ select box(point(i,j))
   from generate_series(1,100,5) i,
        generate_series(1,10,5) j;
 -- leave this table around, to help in testing dump/restore
+-- Check getQuadrant with large coords near a power-of-two boundary (bug #19597)
+create table spgist_quad_fp_tbl(p point);
+insert into spgist_quad_fp_tbl
+  select point(17179869183.999998, 17179869183.999998)
+  from generate_series(1, 4000);
+create index spgist_quad_fp_idx on spgist_quad_fp_tbl using spgist(p);
+insert into spgist_quad_fp_tbl
+  select point(17179869184.0, 17179869184.0)
+  from generate_series(1, 400);
+select count(*) from spgist_quad_fp_tbl where p ~= point(17179869184.0, 17179869184.0);
--
2.43.0
