[SCM] PostGIS branch master updated. 3.6.0rc2-164-g59d2f3dc7

git at osgeo.org git at osgeo.org
Wed Oct 29 16:46:43 PDT 2025


This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "PostGIS".

The branch, master has been updated
       via  59d2f3dc7f7bc15f1bb924c71610fbdee61a6ef3 (commit)
      from  5faa572a734cce40195709a14db60301c1bf5a0c (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
commit 59d2f3dc7f7bc15f1bb924c71610fbdee61a6ef3
Author: Darafei Praliaskouski <me at komzpa.net>
Date:   Thu Oct 30 03:46:26 2025 +0400

    Guard against histogram axis dimension underflow
    
    References #5959
    References #5984

diff --git a/postgis/cunit/cu_tester.c b/postgis/cunit/cu_tester.c
index b4dd46aa7..eb6ba5b0d 100644
--- a/postgis/cunit/cu_tester.c
+++ b/postgis/cunit/cu_tester.c
@@ -28,6 +28,7 @@
 
 #include <CUnit/Basic.h>
 #include <limits.h>
+#include <math.h>
 #include <string.h>
 
 #include "../gserialized_estimate_support.h"
@@ -82,6 +83,23 @@ histogram_budget_clamps(void)
 	CU_ASSERT_EQUAL(histogram_cell_budget((double)INT_MAX, 50000, INT_MAX), INT_MAX);
 }
 
+static void
+histogram_axis_allocation_guards(void)
+{
+	/* Baseline: evenly split a 10k target over two varying dimensions. */
+	CU_ASSERT_EQUAL(histogram_axis_cells(10000, 2, 0.5), 100);
+
+	/* Skewed axis ratios that collapse to tiny powers still return one cell. */
+	CU_ASSERT_EQUAL(histogram_axis_cells(10000, 2, 1e-9), 1);
+
+	/* Denormals, NaNs and negative ratios should not leak to the histogram. */
+	CU_ASSERT_EQUAL(histogram_axis_cells(10000, 2, NAN), 1);
+	CU_ASSERT_EQUAL(histogram_axis_cells(10000, 2, -0.5), 1);
+
+	/* Extremely aggressive ratios remain bounded by the square root of the budget. */
+	CU_ASSERT_EQUAL(histogram_axis_cells(INT_MAX, 2, 1.0), (int)sqrt((double)INT_MAX * 2.0));
+}
+
 static void
 nd_stats_indexing_behaviour(void)
 {
@@ -138,6 +156,7 @@ main(void)
 		goto cleanup;
 
 	if (!CU_add_test(suite, "histogram budget clamps", histogram_budget_clamps) ||
+	    !CU_add_test(suite, "histogram axis guards", histogram_axis_allocation_guards) ||
 	    !CU_add_test(suite, "nd_stats value index guards", nd_stats_indexing_behaviour) ||
 	    !CU_add_test(suite, "nd_box ratio edge cases", nd_box_ratio_cases))
 	{
diff --git a/postgis/gserialized_estimate.c b/postgis/gserialized_estimate.c
index 5c4b4387b..d616cbeba 100644
--- a/postgis/gserialized_estimate.c
+++ b/postgis/gserialized_estimate.c
@@ -1516,11 +1516,10 @@ compute_gserialized_stats_mode(VacAttrStats *stats, AnalyzeAttrFetchFunc fetchfu
 				 * Scale the target cells number by the # of dims and ratio,
 				 * then take the appropriate root to get the estimated number of cells
 				 * on this axis (eg, pow(0.5) for 2d, pow(0.333) for 3d, pow(0.25) for 4d)
-				*/
-				histo_size[d] = (int)pow((double)histo_cells_target * histo_ndims * edge_ratio, 1/(double)histo_ndims);
-				/* If something goes awry, just give this dim one slot */
-				if ( ! histo_size[d] )
-					histo_size[d] = 1;
+				 * The dedicated helper clamps pathological floating point inputs so we
+				 * do not resurrect the NaN propagation reported in #5959 on amd64.
+				 */
+				histo_size[d] = histogram_axis_cells(histo_cells_target, histo_ndims, edge_ratio);
 			}
 			histo_cells_new *= histo_size[d];
 		}
diff --git a/postgis/gserialized_estimate_support.h b/postgis/gserialized_estimate_support.h
index 0d3a23d75..6b372a43e 100644
--- a/postgis/gserialized_estimate_support.h
+++ b/postgis/gserialized_estimate_support.h
@@ -151,6 +151,46 @@ histogram_cell_budget(double total_rows, int ndims, int attstattarget)
 	return (int)budget;
 }
 
+/*
+ * Allocate histogram buckets along a single axis in proportion to the observed
+ * density variation.  The caller passes in the global histogram target along
+ * with the number of axes that exhibited variation in the sampled data and the
+ * relative contribution of the current axis (edge_ratio).  Earlier versions
+ * evaluated the pow() call directly in the caller, which exposed the planner to
+ * NaN propagation on some amd64 builds when the ratio was denormal or negative
+ * (see #5959).  Keeping the calculation in one place allows us to clamp the
+ * inputs and provide a predictable fallback for problematic floating point
+ * combinations.
+ */
+static inline int
+histogram_axis_cells(int histo_cells_target, int histo_ndims, double edge_ratio)
+{
+	double scaled;
+	double axis_cells;
+
+	if (histo_cells_target <= 0 || histo_ndims <= 0)
+		return 1;
+
+	if (!(edge_ratio > 0.0) || !isfinite(edge_ratio))
+		return 1;
+
+	scaled = (double)histo_cells_target * (double)histo_ndims * edge_ratio;
+	if (!(scaled > 0.0) || !isfinite(scaled))
+		return 1;
+
+	axis_cells = pow(scaled, 1.0 / (double)histo_ndims);
+	if (!(axis_cells > 0.0) || !isfinite(axis_cells))
+		return 1;
+
+	if (axis_cells >= (double)INT_MAX)
+		return INT_MAX;
+
+	if (axis_cells <= 1.0)
+		return 1;
+
+	return (int)axis_cells;
+}
+
 /*
  * Compute the portion of 'target' covered by 'cover'.  The caller supplies the
  * dimensionality because ND_BOX always carries four slots.  Degenerate volumes

-----------------------------------------------------------------------

Summary of changes:
 postgis/cunit/cu_tester.c              | 19 ++++++++++++++++
 postgis/gserialized_estimate.c         |  9 ++++----
 postgis/gserialized_estimate_support.h | 40 ++++++++++++++++++++++++++++++++++
 3 files changed, 63 insertions(+), 5 deletions(-)


hooks/post-receive
-- 
PostGIS


More information about the postgis-tickets mailing list