PostGIS 3.7.0dev-r@@SVN_REVISION@@
Loading...
Searching...
No Matches
postgis/cunit/cu_tester.c
Go to the documentation of this file.
1/**********************************************************************
2 *
3 * PostGIS - Spatial Types for PostgreSQL
4 * http://postgis.net
5 *
6 * This file is part of PostGIS
7 *
8 * PostGIS is free software: you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation, either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * PostGIS is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with PostGIS. If not, see <http://www.gnu.org/licenses/>.
20 *
21 **********************************************************************
22 *
23 * Copyright 2025 (C) Darafei Praliaskouski <me@komzpa.net>
24 *
25 **********************************************************************/
26
27#include "postgres.h"
28
29#include <CUnit/Basic.h>
30#include <limits.h>
31#include <math.h>
32#include <string.h>
33
34#include "../gserialized_estimate_support.h"
35
36static ND_BOX
37make_box(float minx, float miny, float minz, float minm, float maxx, float maxy, float maxz, float maxm)
38{
39 ND_BOX box;
40
41 memset(&box, 0, sizeof(box));
42 box.min[0] = minx;
43 box.min[1] = miny;
44 box.min[2] = minz;
45 box.min[3] = minm;
46 box.max[0] = maxx;
47 box.max[1] = maxy;
48 box.max[2] = maxz;
49 box.max[3] = maxm;
50 return box;
51}
52
53static void
55{
56 /* Zero or negative row counts disable histogram construction. */
57 CU_ASSERT_EQUAL(histogram_cell_budget(0.0, 2, 100), 0);
58 CU_ASSERT_EQUAL(histogram_cell_budget(-1.0, 4, 100), 0);
59
60 /* Degenerate dimensionality cannot allocate histogram space. */
61 CU_ASSERT_EQUAL(histogram_cell_budget(1000.0, 0, 100), 0);
62
63 /* Matches the classic pow(attstattarget, ndims) path. */
64 CU_ASSERT_EQUAL(histogram_cell_budget(1e6, 2, 100), 10000);
65 CU_ASSERT_EQUAL(histogram_cell_budget(1e6, 3, 50), 125000);
66
67 /* attstattarget^ndims exceeds ndims * 100000 and must be clamped. */
68 CU_ASSERT_EQUAL(histogram_cell_budget(1e6, 4, 50), 400000);
69
70 /* attstattarget<=0 is normalised to the smallest viable target. */
71 CU_ASSERT_EQUAL(histogram_cell_budget(1e6, 2, 0), 1);
72
73 /* Row clamp shrinks the grid for small relations. */
74 CU_ASSERT_EQUAL(histogram_cell_budget(1.0, 2, 100), 20);
75
76 /* Large tables now preserve the dimensional cap instead of overflowing. */
77 CU_ASSERT_EQUAL(histogram_cell_budget(1.5e8, 2, 100), 10000);
78
79 /* Regression for #5984: huge attstat targets stabilise instead of wrapping. */
80 CU_ASSERT_EQUAL(histogram_cell_budget(5e6, 2, 10000), 200000);
81
82 /* Trigger the INT_MAX guard once both other caps exceed it. */
83 CU_ASSERT_EQUAL(histogram_cell_budget((double)INT_MAX, 50000, INT_MAX), INT_MAX);
84}
85
86static void
88{
89 /* Baseline: evenly split a 10k target over two varying dimensions. */
90 CU_ASSERT_EQUAL(histogram_axis_cells(10000, 2, 0.5), 100);
91
92 /* Skewed axis ratios that collapse to tiny powers still return one cell. */
93 CU_ASSERT_EQUAL(histogram_axis_cells(10000, 2, 1e-9), 1);
94
95 /* Denormals, NaNs and negative ratios should not leak to the histogram. */
96 CU_ASSERT_EQUAL(histogram_axis_cells(10000, 2, NAN), 1);
97 CU_ASSERT_EQUAL(histogram_axis_cells(10000, 2, -0.5), 1);
98
99 /* Extremely aggressive ratios remain bounded by the square root of the budget. */
100 CU_ASSERT_EQUAL(histogram_axis_cells(INT_MAX, 2, 1.0), (int)sqrt((double)INT_MAX * 2.0));
101}
102
103static void
105{
106 ND_STATS stats;
107 const int good_index[ND_DIMS] = {1, 2, 0, 0};
108 const int bad_index[ND_DIMS] = {1, 5, 0, 0};
109
110 memset(&stats, 0, sizeof(stats));
111 stats.ndims = 3;
112 stats.size[0] = 4.0f;
113 stats.size[1] = 5.0f;
114 stats.size[2] = 3.0f;
115
116 /* Three-dimensional index (x=1, y=2, z=0) collapses into 1 + 2 * 4. */
117 CU_ASSERT_EQUAL(nd_stats_value_index(&stats, good_index), 1 + 2 * 4);
118 /* Any request outside the histogram bounds triggers a guard. */
119 CU_ASSERT_EQUAL(nd_stats_value_index(&stats, bad_index), -1);
120
121 /* Regression for #5959: ndims higher than populated sizes still honours guards. */
122 stats.ndims = 4;
123 CU_ASSERT_EQUAL(nd_stats_value_index(&stats, good_index), -1);
124}
125
126static void
128{
129 ND_BOX covering = make_box(0.0f, 0.0f, 0.0f, 0.0f, 2.0f, 2.0f, 2.0f, 0.0f);
130 ND_BOX interior = make_box(0.5f, 0.5f, 0.5f, 0.0f, 1.5f, 1.5f, 1.5f, 0.0f);
131 ND_BOX partial = make_box(0.0f, 0.0f, 0.0f, 0.0f, 0.5f, 0.5f, 0.5f, 0.0f);
132 ND_BOX target = make_box(0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f);
133 ND_BOX flat = make_box(0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f);
134 ND_BOX touch = make_box(2.0f, 0.0f, 0.0f, 0.0f, 3.0f, 1.0f, 1.0f, 0.0f);
135
136 /* Full coverage should evaluate to one regardless of the extra extent. */
137 CU_ASSERT_DOUBLE_EQUAL(nd_box_ratio(&covering, &interior, 3), 1.0, 1e-12);
138 /* A shared octant carries one eighth of the reference volume. */
139 CU_ASSERT_DOUBLE_EQUAL(nd_box_ratio(&partial, &target, 3), 0.125, 1e-12);
140 /* Degenerate slabs have zero volume in three dimensions. */
141 CU_ASSERT_DOUBLE_EQUAL(nd_box_ratio(&covering, &flat, 3), 0.0, 1e-12);
142 /* Boxes that only touch along a face should not count as overlap. */
143 CU_ASSERT_DOUBLE_EQUAL(nd_box_ratio(&covering, &touch, 3), 0.0, 1e-12);
144}
145
146int
147main(void)
148{
149 CU_pSuite suite;
150 unsigned int failures = 0;
151 if (CU_initialize_registry() != CUE_SUCCESS)
152 return CU_get_error();
153
154 suite = CU_add_suite("gserialized_histogram_helpers", NULL, NULL);
155 if (!suite)
156 goto cleanup;
157
158 if (!CU_add_test(suite, "histogram budget clamps", histogram_budget_clamps) ||
159 !CU_add_test(suite, "histogram axis guards", histogram_axis_allocation_guards) ||
160 !CU_add_test(suite, "nd_stats value index guards", nd_stats_indexing_behaviour) ||
161 !CU_add_test(suite, "nd_box ratio edge cases", nd_box_ratio_cases))
162 {
163 goto cleanup;
164 }
165
166 CU_basic_set_mode(CU_BRM_VERBOSE);
167 CU_basic_run_tests();
168
169cleanup:
170 failures = CU_get_number_of_tests_failed();
171 CU_cleanup_registry();
172 return failures == 0 ? CUE_SUCCESS : 1;
173}
static int histogram_axis_cells(int histo_cells_target, int histo_ndims, double edge_ratio)
static double nd_box_ratio(const ND_BOX *cover, const ND_BOX *target, int ndims)
static int histogram_cell_budget(double total_rows, int ndims, int attstattarget)
static int nd_stats_value_index(const ND_STATS *stats, const int *indexes)
#define NAN
Definition lwgeodetic.h:37
static void nd_stats_indexing_behaviour(void)
static void nd_box_ratio_cases(void)
int main(void)
static void histogram_budget_clamps(void)
static ND_BOX make_box(float minx, float miny, float minz, float minm, float maxx, float maxy, float maxz, float maxm)
static void histogram_axis_allocation_guards(void)