From 705fc9741e32da1ec4238c91a2f7d7aa0784679e Mon Sep 17 00:00:00 2001 From: Ayush Tiwari Date: Wed, 4 Nov 2026 19:05:19 +0530 Subject: [PATCH v2 2/2] Fix NaN ordering in cube GiST searches An internal GiST key containing NaN can cover finite values. Returning NaN as its distance does not give a lower bound on the distances of its descendants, so nearest-neighbor scans can return rows out of order. Return negative infinity when an internal distance evaluates to NaN. Leaf entries still return the exact value of the ordering operator. Also use float8_min() and float8_max() when selecting coordinate bounds, as the ~> operator does. Bug: #19603 Reported-by: Aleksander Alekseev Author: Ayush Tiwari Discussion: https://postgr.es/m/19603-7b1f783d5bbfe791@postgresql.org --- contrib/cube/cube.c | 13 +++++++++---- contrib/cube/expected/cube.out | 11 +++++++++++ contrib/cube/sql/cube.sql | 6 ++++++ 3 files changed, 26 insertions(+), 4 deletions(-) diff --git a/contrib/cube/cube.c b/contrib/cube/cube.c index 7576c0a9514..24134ff7ca1 100644 --- a/contrib/cube/cube.c +++ b/contrib/cube/cube.c @@ -1450,9 +1450,9 @@ g_cube_distance(PG_FUNCTION_ARGS) { /* For leaf just return required upper/lower bound */ if (upper) - retval = Max(cube->x[index], cube->x[index + DIM(cube)]); + retval = float8_max(cube->x[index], cube->x[index + DIM(cube)]); else - retval = Min(cube->x[index], cube->x[index + DIM(cube)]); + retval = float8_min(cube->x[index], cube->x[index + DIM(cube)]); } else { @@ -1464,9 +1464,9 @@ g_cube_distance(PG_FUNCTION_ARGS) * inversed value. */ if (!inverse) - retval = Min(cube->x[index], cube->x[index + DIM(cube)]); + retval = float8_min(cube->x[index], cube->x[index + DIM(cube)]); else - retval = Max(cube->x[index], cube->x[index + DIM(cube)]); + retval = float8_max(cube->x[index], cube->x[index + DIM(cube)]); } } } @@ -1503,6 +1503,11 @@ g_cube_distance(PG_FUNCTION_ARGS) break; } } + + /* NaN does not bound the subtree's distances from below */ + if (!GIST_LEAF(entry) && isnan(retval)) + retval = -get_float8_infinity(); + PG_RETURN_FLOAT8(retval); } diff --git a/contrib/cube/expected/cube.out b/contrib/cube/expected/cube.out index 51e50d90e55..17281408a06 100644 --- a/contrib/cube/expected/cube.out +++ b/contrib/cube/expected/cube.out @@ -1988,3 +1988,14 @@ SELECT c~>(-4), c FROM test_cube ORDER BY c~>(-4) LIMIT 15; -- descending by upp (15 rows) RESET enable_indexscan; +-- Test kNN with NaN coordinates +INSERT INTO test_cube VALUES ('(200000, 25000),(NaN, 25000)'); +SET enable_seqscan = false; +SELECT c~>(-1), c FROM test_cube ORDER BY c~>(-1) LIMIT 2; + ?column? | c +----------+------------------------------ + -200000 | (200000, 25000),(NaN, 25000) + -100000 | (100000) +(2 rows) + +RESET enable_seqscan; diff --git a/contrib/cube/sql/cube.sql b/contrib/cube/sql/cube.sql index 6a77075f0da..25d389114e8 100644 --- a/contrib/cube/sql/cube.sql +++ b/contrib/cube/sql/cube.sql @@ -443,3 +443,9 @@ SELECT c~>(-2), c FROM test_cube ORDER BY c~>(-2) LIMIT 15; -- descending by rig SELECT c~>(-3), c FROM test_cube ORDER BY c~>(-3) LIMIT 15; -- descending by lower bound SELECT c~>(-4), c FROM test_cube ORDER BY c~>(-4) LIMIT 15; -- descending by upper bound RESET enable_indexscan; + +-- Test kNN with NaN coordinates +INSERT INTO test_cube VALUES ('(200000, 25000),(NaN, 25000)'); +SET enable_seqscan = false; +SELECT c~>(-1), c FROM test_cube ORDER BY c~>(-1) LIMIT 2; +RESET enable_seqscan; -- 2.34.1