Age Owner Branch data TLA Line data Source code
1 : : /*-------------------------------------------------------------------------
2 : : *
3 : : * geo_spgist.c
4 : : * SP-GiST implementation of 4-dimensional quad tree over boxes
5 : : *
6 : : * This module provides SP-GiST implementation for boxes using quad tree
7 : : * analogy in 4-dimensional space. SP-GiST doesn't allow indexing of
8 : : * overlapping objects. We are making 2D objects never-overlapping in
9 : : * 4D space. This technique has some benefits compared to traditional
10 : : * R-Tree which is implemented as GiST. The performance tests reveal
11 : : * that this technique especially beneficial with too much overlapping
12 : : * objects, so called "spaghetti data".
13 : : *
14 : : * Unlike the original quad tree, we are splitting the tree into 16
15 : : * quadrants in 4D space. It is easier to imagine it as splitting space
16 : : * two times into 4:
17 : : *
18 : : * | |
19 : : * | |
20 : : * | -----+-----
21 : : * | |
22 : : * | |
23 : : * -------------+-------------
24 : : * |
25 : : * |
26 : : * |
27 : : * |
28 : : * |
29 : : *
30 : : * We are using box datatype as the prefix, but we are treating them
31 : : * as points in 4-dimensional space, because 2D boxes are not enough
32 : : * to represent the quadrant boundaries in 4D space. They however are
33 : : * sufficient to point out the additional boundaries of the next
34 : : * quadrant.
35 : : *
36 : : * We are using traversal values provided by SP-GiST to calculate and
37 : : * to store the bounds of the quadrants, while traversing into the tree.
38 : : * Traversal value has all the boundaries in the 4D space, and is capable
39 : : * of transferring the required boundaries to the following traversal
40 : : * values. In conclusion, three things are necessary to calculate the
41 : : * next traversal value:
42 : : *
43 : : * (1) the traversal value of the parent
44 : : * (2) the quadrant of the current node
45 : : * (3) the prefix of the current node
46 : : *
47 : : * If we visualize them on our simplified drawing (see the drawing above);
48 : : * transferred boundaries of (1) would be the outer axis, relevant part
49 : : * of (2) would be the up right part of the other axis, and (3) would be
50 : : * the inner axis.
51 : : *
52 : : * For example, consider the case of overlapping. When recursion
53 : : * descends deeper and deeper down the tree, all quadrants in
54 : : * the current node will be checked for overlapping. The boundaries
55 : : * will be re-calculated for all quadrants. Overlap check answers
56 : : * the question: can any box from this quadrant overlap with the given
57 : : * box? If yes, then this quadrant will be walked. If no, then this
58 : : * quadrant will be skipped.
59 : : *
60 : : * This method provides restrictions for minimum and maximum values of
61 : : * every dimension of every corner of the box on every level of the tree
62 : : * except the root. For the root node, we are setting the boundaries
63 : : * that we don't yet have as infinity.
64 : : *
65 : : * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
66 : : * Portions Copyright (c) 1994, Regents of the University of California
67 : : *
68 : : * IDENTIFICATION
69 : : * src/backend/utils/adt/geo_spgist.c
70 : : *
71 : : *-------------------------------------------------------------------------
72 : : */
73 : :
74 : : #include "postgres.h"
75 : :
76 : : #include "access/spgist.h"
77 : : #include "access/spgist_private.h"
78 : : #include "access/stratnum.h"
79 : : #include "catalog/pg_type.h"
80 : : #include "utils/float.h"
81 : : #include "utils/fmgroids.h"
82 : : #include "utils/fmgrprotos.h"
83 : : #include "utils/geo_decls.h"
84 : :
85 : : /*
86 : : * Comparator for qsort
87 : : *
88 : : * We don't need to use the floating point macros in here, because this
89 : : * is only going to be used in a place to effect the performance
90 : : * of the index, not the correctness.
91 : : */
92 : : static int
2937 teodor@sigaev.ru 93 :CBC 1022501 : compareDoubles(const void *a, const void *b)
94 : : {
2068 tomas.vondra@postgre 95 : 1022501 : float8 x = *(float8 *) a;
96 : 1022501 : float8 y = *(float8 *) b;
97 : :
2937 teodor@sigaev.ru 98 [ + + ]: 1022501 : if (x == y)
99 : 198930 : return 0;
100 [ + + ]: 823571 : return (x > y) ? 1 : -1;
101 : : }
102 : :
103 : : typedef struct
104 : : {
105 : : float8 low;
106 : : float8 high;
107 : : } Range;
108 : :
109 : : typedef struct
110 : : {
111 : : Range left;
112 : : Range right;
113 : : } RangeBox;
114 : :
115 : : typedef struct
116 : : {
117 : : RangeBox range_box_x;
118 : : RangeBox range_box_y;
119 : : } RectBox;
120 : :
121 : : /*
122 : : * Calculate the quadrant
123 : : *
124 : : * The quadrant is 8 bit unsigned integer with 4 least bits in use.
125 : : * This function accepts BOXes as input. They are not casted to
126 : : * RangeBoxes, yet. All 4 bits are set by comparing a corner of the box.
127 : : * This makes 16 quadrants in total.
128 : : */
129 : : static uint8
130 : 435819 : getQuadrant(BOX *centroid, BOX *inBox)
131 : : {
132 : 435819 : uint8 quadrant = 0;
133 : :
134 [ + + ]: 435819 : if (inBox->low.x > centroid->low.x)
135 : 367809 : quadrant |= 0x8;
136 : :
137 [ + + ]: 435819 : if (inBox->high.x > centroid->high.x)
138 : 374592 : quadrant |= 0x4;
139 : :
140 [ + + ]: 435819 : if (inBox->low.y > centroid->low.y)
141 : 235452 : quadrant |= 0x2;
142 : :
143 [ + + ]: 435819 : if (inBox->high.y > centroid->high.y)
144 : 237105 : quadrant |= 0x1;
145 : :
146 : 435819 : return quadrant;
147 : : }
148 : :
149 : : /*
150 : : * Get RangeBox using BOX
151 : : *
152 : : * We are turning the BOX to our structures to emphasize their function
153 : : * of representing points in 4D space. It also is more convenient to
154 : : * access the values with this structure.
155 : : */
156 : : static RangeBox *
157 : 8082 : getRangeBox(BOX *box)
158 : : {
159 : 8082 : RangeBox *range_box = (RangeBox *) palloc(sizeof(RangeBox));
160 : :
161 : 8082 : range_box->left.low = box->low.x;
162 : 8082 : range_box->left.high = box->high.x;
163 : :
164 : 8082 : range_box->right.low = box->low.y;
165 : 8082 : range_box->right.high = box->high.y;
166 : :
167 : 8082 : return range_box;
168 : : }
169 : :
170 : : /*
171 : : * Initialize the traversal value
172 : : *
173 : : * In the beginning, we don't have any restrictions. We have to
174 : : * initialize the struct to cover the whole 4D space.
175 : : */
176 : : static RectBox *
2913 bruce@momjian.us 177 : 403 : initRectBox(void)
178 : : {
2866 rhaas@postgresql.org 179 : 403 : RectBox *rect_box = (RectBox *) palloc(sizeof(RectBox));
2068 tomas.vondra@postgre 180 : 403 : float8 infinity = get_float8_infinity();
181 : :
2937 teodor@sigaev.ru 182 : 403 : rect_box->range_box_x.left.low = -infinity;
183 : 403 : rect_box->range_box_x.left.high = infinity;
184 : :
185 : 403 : rect_box->range_box_x.right.low = -infinity;
186 : 403 : rect_box->range_box_x.right.high = infinity;
187 : :
188 : 403 : rect_box->range_box_y.left.low = -infinity;
189 : 403 : rect_box->range_box_y.left.high = infinity;
190 : :
191 : 403 : rect_box->range_box_y.right.low = -infinity;
192 : 403 : rect_box->range_box_y.right.high = infinity;
193 : :
194 : 403 : return rect_box;
195 : : }
196 : :
197 : : /*
198 : : * Calculate the next traversal value
199 : : *
200 : : * All centroids are bounded by RectBox, but SP-GiST only keeps
201 : : * boxes. When we are traversing the tree, we must calculate RectBox,
202 : : * using centroid and quadrant.
203 : : */
204 : : static RectBox *
205 : 67008 : nextRectBox(RectBox *rect_box, RangeBox *centroid, uint8 quadrant)
206 : : {
2866 rhaas@postgresql.org 207 : 67008 : RectBox *next_rect_box = (RectBox *) palloc(sizeof(RectBox));
208 : :
2937 teodor@sigaev.ru 209 : 67008 : memcpy(next_rect_box, rect_box, sizeof(RectBox));
210 : :
211 [ + + ]: 67008 : if (quadrant & 0x8)
212 : 33504 : next_rect_box->range_box_x.left.low = centroid->left.low;
213 : : else
214 : 33504 : next_rect_box->range_box_x.left.high = centroid->left.low;
215 : :
216 [ + + ]: 67008 : if (quadrant & 0x4)
217 : 33504 : next_rect_box->range_box_x.right.low = centroid->left.high;
218 : : else
219 : 33504 : next_rect_box->range_box_x.right.high = centroid->left.high;
220 : :
221 [ + + ]: 67008 : if (quadrant & 0x2)
222 : 33504 : next_rect_box->range_box_y.left.low = centroid->right.low;
223 : : else
224 : 33504 : next_rect_box->range_box_y.left.high = centroid->right.low;
225 : :
226 [ + + ]: 67008 : if (quadrant & 0x1)
227 : 33504 : next_rect_box->range_box_y.right.low = centroid->right.high;
228 : : else
229 : 33504 : next_rect_box->range_box_y.right.high = centroid->right.high;
230 : :
231 : 67008 : return next_rect_box;
232 : : }
233 : :
234 : : /* Can any range from range_box overlap with this argument? */
235 : : static bool
236 : 5400 : overlap2D(RangeBox *range_box, Range *query)
237 : : {
238 [ + + + + ]: 10164 : return FPge(range_box->right.high, query->low) &&
2866 rhaas@postgresql.org 239 : 4764 : FPle(range_box->left.low, query->high);
240 : : }
241 : :
242 : : /* Can any rectangle from rect_box overlap with this argument? */
243 : : static bool
2937 teodor@sigaev.ru 244 : 3168 : overlap4D(RectBox *rect_box, RangeBox *query)
245 : : {
246 [ + + + + ]: 5400 : return overlap2D(&rect_box->range_box_x, &query->left) &&
2866 rhaas@postgresql.org 247 : 2232 : overlap2D(&rect_box->range_box_y, &query->right);
248 : : }
249 : :
250 : : /* Can any range from range_box contain this argument? */
251 : : static bool
2937 teodor@sigaev.ru 252 : 888 : contain2D(RangeBox *range_box, Range *query)
253 : : {
254 [ + + + + ]: 1488 : return FPge(range_box->right.high, query->high) &&
2866 rhaas@postgresql.org 255 : 600 : FPle(range_box->left.low, query->low);
256 : : }
257 : :
258 : : /* Can any rectangle from rect_box contain this argument? */
259 : : static bool
260 : 576 : contain4D(RectBox *rect_box, RangeBox *query)
261 : : {
2937 teodor@sigaev.ru 262 [ + + + + ]: 888 : return contain2D(&rect_box->range_box_x, &query->left) &&
2866 rhaas@postgresql.org 263 : 312 : contain2D(&rect_box->range_box_y, &query->right);
264 : : }
265 : :
266 : : /* Can any range from range_box be contained by this argument? */
267 : : static bool
2937 teodor@sigaev.ru 268 : 10500 : contained2D(RangeBox *range_box, Range *query)
269 : : {
270 [ + + ]: 20388 : return FPle(range_box->left.low, query->high) &&
2866 rhaas@postgresql.org 271 [ + + ]: 18252 : FPge(range_box->left.high, query->low) &&
272 [ + + + + ]: 28752 : FPle(range_box->right.low, query->high) &&
273 : 7950 : FPge(range_box->right.high, query->low);
274 : : }
275 : :
276 : : /* Can any rectangle from rect_box be contained by this argument? */
277 : : static bool
2937 teodor@sigaev.ru 278 : 6720 : contained4D(RectBox *rect_box, RangeBox *query)
279 : : {
280 [ + + + + ]: 10500 : return contained2D(&rect_box->range_box_x, &query->left) &&
2866 rhaas@postgresql.org 281 : 3780 : contained2D(&rect_box->range_box_y, &query->right);
282 : : }
283 : :
284 : : /* Can any range from range_box to be lower than this argument? */
285 : : static bool
2937 teodor@sigaev.ru 286 : 6816 : lower2D(RangeBox *range_box, Range *query)
287 : : {
288 [ + + + + ]: 12216 : return FPlt(range_box->left.low, query->low) &&
2866 rhaas@postgresql.org 289 : 5400 : FPlt(range_box->right.low, query->low);
290 : : }
291 : :
292 : : /* Can any range from range_box not extend to the right side of the query? */
293 : : static bool
2581 teodor@sigaev.ru 294 : 12144 : overLower2D(RangeBox *range_box, Range *query)
295 : : {
296 [ + + + + ]: 23352 : return FPle(range_box->left.low, query->high) &&
297 : 11208 : FPle(range_box->right.low, query->high);
298 : : }
299 : :
300 : : /* Can any range from range_box to be higher than this argument? */
301 : : static bool
2937 302 : 17424 : higher2D(RangeBox *range_box, Range *query)
303 : : {
304 [ + + + + ]: 30936 : return FPgt(range_box->left.high, query->high) &&
2866 rhaas@postgresql.org 305 : 13512 : FPgt(range_box->right.high, query->high);
306 : : }
307 : :
308 : : /* Can any range from range_box not extend to the left side of the query? */
309 : : static bool
2581 teodor@sigaev.ru 310 : 15456 : overHigher2D(RangeBox *range_box, Range *query)
311 : : {
312 [ + + + + ]: 29688 : return FPge(range_box->left.high, query->low) &&
313 : 14232 : FPge(range_box->right.high, query->low);
314 : : }
315 : :
316 : : /* Can any rectangle from rect_box be left of this argument? */
317 : : static bool
2937 318 : 4656 : left4D(RectBox *rect_box, RangeBox *query)
319 : : {
320 : 4656 : return lower2D(&rect_box->range_box_x, &query->left);
321 : : }
322 : :
323 : : /* Can any rectangle from rect_box does not extend the right of this argument? */
324 : : static bool
325 : 7344 : overLeft4D(RectBox *rect_box, RangeBox *query)
326 : : {
2581 327 : 7344 : return overLower2D(&rect_box->range_box_x, &query->left);
328 : : }
329 : :
330 : : /* Can any rectangle from rect_box be right of this argument? */
331 : : static bool
2937 332 : 13152 : right4D(RectBox *rect_box, RangeBox *query)
333 : : {
334 : 13152 : return higher2D(&rect_box->range_box_x, &query->left);
335 : : }
336 : :
337 : : /* Can any rectangle from rect_box does not extend the left of this argument? */
338 : : static bool
339 : 8496 : overRight4D(RectBox *rect_box, RangeBox *query)
340 : : {
2581 341 : 8496 : return overHigher2D(&rect_box->range_box_x, &query->left);
342 : : }
343 : :
344 : : /* Can any rectangle from rect_box be below of this argument? */
345 : : static bool
2937 346 : 2160 : below4D(RectBox *rect_box, RangeBox *query)
347 : : {
348 : 2160 : return lower2D(&rect_box->range_box_y, &query->right);
349 : : }
350 : :
351 : : /* Can any rectangle from rect_box does not extend above this argument? */
352 : : static bool
353 : 4800 : overBelow4D(RectBox *rect_box, RangeBox *query)
354 : : {
2581 355 : 4800 : return overLower2D(&rect_box->range_box_y, &query->right);
356 : : }
357 : :
358 : : /* Can any rectangle from rect_box be above of this argument? */
359 : : static bool
2937 360 : 4272 : above4D(RectBox *rect_box, RangeBox *query)
361 : : {
362 : 4272 : return higher2D(&rect_box->range_box_y, &query->right);
363 : : }
364 : :
365 : : /* Can any rectangle from rect_box does not extend below of this argument? */
366 : : static bool
367 : 6960 : overAbove4D(RectBox *rect_box, RangeBox *query)
368 : : {
2581 369 : 6960 : return overHigher2D(&rect_box->range_box_y, &query->right);
370 : : }
371 : :
372 : : /* Lower bound for the distance between point and rect_box */
373 : : static double
2034 akorotkov@postgresql 374 : 6601 : pointToRectBoxDistance(Point *point, RectBox *rect_box)
375 : : {
376 : : double dx;
377 : : double dy;
378 : :
379 [ + + ]: 6601 : if (point->x < rect_box->range_box_x.left.low)
380 : 5112 : dx = rect_box->range_box_x.left.low - point->x;
381 [ + + ]: 1489 : else if (point->x > rect_box->range_box_x.right.high)
382 : 288 : dx = point->x - rect_box->range_box_x.right.high;
383 : : else
384 : 1201 : dx = 0;
385 : :
386 [ + + ]: 6601 : if (point->y < rect_box->range_box_y.left.low)
387 : 3192 : dy = rect_box->range_box_y.left.low - point->y;
388 [ + + ]: 3409 : else if (point->y > rect_box->range_box_y.right.high)
389 : 3105 : dy = point->y - rect_box->range_box_y.right.high;
390 : : else
391 : 304 : dy = 0;
392 : :
393 : 6601 : return HYPOT(dx, dy);
394 : : }
395 : :
396 : :
397 : : /*
398 : : * SP-GiST config function
399 : : */
400 : : Datum
2937 teodor@sigaev.ru 401 : 39 : spg_box_quad_config(PG_FUNCTION_ARGS)
402 : : {
403 : 39 : spgConfigOut *cfg = (spgConfigOut *) PG_GETARG_POINTER(1);
404 : :
405 : 39 : cfg->prefixType = BOXOID;
406 : 39 : cfg->labelType = VOIDOID; /* We don't need node labels. */
407 : 39 : cfg->canReturnData = true;
408 : 39 : cfg->longValuesOK = false;
409 : :
410 : 39 : PG_RETURN_VOID();
411 : : }
412 : :
413 : : /*
414 : : * SP-GiST choose function
415 : : */
416 : : Datum
417 : 377215 : spg_box_quad_choose(PG_FUNCTION_ARGS)
418 : : {
419 : 377215 : spgChooseIn *in = (spgChooseIn *) PG_GETARG_POINTER(0);
420 : 377215 : spgChooseOut *out = (spgChooseOut *) PG_GETARG_POINTER(1);
421 : 377215 : BOX *centroid = DatumGetBoxP(in->prefixDatum),
2302 422 : 377215 : *box = DatumGetBoxP(in->leafDatum);
423 : :
2937 424 : 377215 : out->resultType = spgMatchNode;
425 : 377215 : out->result.matchNode.restDatum = BoxPGetDatum(box);
426 : :
427 : : /* nodeN will be set by core, when allTheSame. */
428 [ + + ]: 377215 : if (!in->allTheSame)
429 : 370701 : out->result.matchNode.nodeN = getQuadrant(centroid, box);
430 : :
431 : 377215 : PG_RETURN_VOID();
432 : : }
433 : :
434 : : /*
435 : : * SP-GiST pick-split function
436 : : *
437 : : * It splits a list of boxes into quadrants by choosing a central 4D
438 : : * point as the median of the coordinates of the boxes.
439 : : */
440 : : Datum
441 : 654 : spg_box_quad_picksplit(PG_FUNCTION_ARGS)
442 : : {
2866 rhaas@postgresql.org 443 : 654 : spgPickSplitIn *in = (spgPickSplitIn *) PG_GETARG_POINTER(0);
444 : 654 : spgPickSplitOut *out = (spgPickSplitOut *) PG_GETARG_POINTER(1);
445 : : BOX *centroid;
446 : : int median,
447 : : i;
2068 tomas.vondra@postgre 448 : 654 : float8 *lowXs = palloc(sizeof(float8) * in->nTuples);
449 : 654 : float8 *highXs = palloc(sizeof(float8) * in->nTuples);
450 : 654 : float8 *lowYs = palloc(sizeof(float8) * in->nTuples);
451 : 654 : float8 *highYs = palloc(sizeof(float8) * in->nTuples);
452 : :
453 : : /* Calculate median of all 4D coordinates */
2937 teodor@sigaev.ru 454 [ + + ]: 65772 : for (i = 0; i < in->nTuples; i++)
455 : : {
2866 rhaas@postgresql.org 456 : 65118 : BOX *box = DatumGetBoxP(in->datums[i]);
457 : :
2937 teodor@sigaev.ru 458 : 65118 : lowXs[i] = box->low.x;
459 : 65118 : highXs[i] = box->high.x;
460 : 65118 : lowYs[i] = box->low.y;
461 : 65118 : highYs[i] = box->high.y;
462 : : }
463 : :
2068 tomas.vondra@postgre 464 : 654 : qsort(lowXs, in->nTuples, sizeof(float8), compareDoubles);
465 : 654 : qsort(highXs, in->nTuples, sizeof(float8), compareDoubles);
466 : 654 : qsort(lowYs, in->nTuples, sizeof(float8), compareDoubles);
467 : 654 : qsort(highYs, in->nTuples, sizeof(float8), compareDoubles);
468 : :
2937 teodor@sigaev.ru 469 : 654 : median = in->nTuples / 2;
470 : :
471 : 654 : centroid = palloc(sizeof(BOX));
472 : :
473 : 654 : centroid->low.x = lowXs[median];
474 : 654 : centroid->high.x = highXs[median];
475 : 654 : centroid->low.y = lowYs[median];
476 : 654 : centroid->high.y = highYs[median];
477 : :
478 : : /* Fill the output */
479 : 654 : out->hasPrefix = true;
480 : 654 : out->prefixDatum = BoxPGetDatum(centroid);
481 : :
482 : 654 : out->nNodes = 16;
483 : 654 : out->nodeLabels = NULL; /* We don't need node labels. */
484 : :
485 : 654 : out->mapTuplesToNodes = palloc(sizeof(int) * in->nTuples);
486 : 654 : out->leafTupleDatums = palloc(sizeof(Datum) * in->nTuples);
487 : :
488 : : /*
489 : : * Assign ranges to corresponding nodes according to quadrants relative to
490 : : * the "centroid" range
491 : : */
492 [ + + ]: 65772 : for (i = 0; i < in->nTuples; i++)
493 : : {
2866 rhaas@postgresql.org 494 : 65118 : BOX *box = DatumGetBoxP(in->datums[i]);
495 : 65118 : uint8 quadrant = getQuadrant(centroid, box);
496 : :
2937 teodor@sigaev.ru 497 : 65118 : out->leafTupleDatums[i] = BoxPGetDatum(box);
498 : 65118 : out->mapTuplesToNodes[i] = quadrant;
499 : : }
500 : :
501 : 654 : PG_RETURN_VOID();
502 : : }
503 : :
504 : : /*
505 : : * Check if result of consistent method based on bounding box is exact.
506 : : */
507 : : static bool
2302 508 : 196020 : is_bounding_box_test_exact(StrategyNumber strategy)
509 : : {
510 [ + + ]: 196020 : switch (strategy)
511 : : {
512 : 162507 : case RTLeftStrategyNumber:
513 : : case RTOverLeftStrategyNumber:
514 : : case RTOverRightStrategyNumber:
515 : : case RTRightStrategyNumber:
516 : : case RTOverBelowStrategyNumber:
517 : : case RTBelowStrategyNumber:
518 : : case RTAboveStrategyNumber:
519 : : case RTOverAboveStrategyNumber:
520 : 162507 : return true;
521 : :
522 : 33513 : default:
523 : 33513 : return false;
524 : : }
525 : : }
526 : :
527 : : /*
528 : : * Get bounding box for ScanKey.
529 : : */
530 : : static BOX *
531 : 395565 : spg_box_quad_get_scankey_bbox(ScanKey sk, bool *recheck)
532 : : {
533 [ + + - ]: 395565 : switch (sk->sk_subtype)
534 : : {
535 : 197754 : case BOXOID:
536 : 197754 : return DatumGetBoxP(sk->sk_argument);
537 : :
538 : 197811 : case POLYGONOID:
539 [ + + + + ]: 197811 : if (recheck && !is_bounding_box_test_exact(sk->sk_strategy))
540 : 33513 : *recheck = true;
541 : 197811 : return &DatumGetPolygonP(sk->sk_argument)->boundbox;
542 : :
2302 teodor@sigaev.ru 543 :UBC 0 : default:
544 [ # # ]: 0 : elog(ERROR, "unrecognized scankey subtype: %d", sk->sk_subtype);
545 : : return NULL;
546 : : }
547 : : }
548 : :
549 : : /*
550 : : * SP-GiST inner consistent function
551 : : */
552 : : Datum
2937 teodor@sigaev.ru 553 :CBC 4543 : spg_box_quad_inner_consistent(PG_FUNCTION_ARGS)
554 : : {
555 : 4543 : spgInnerConsistentIn *in = (spgInnerConsistentIn *) PG_GETARG_POINTER(0);
556 : 4543 : spgInnerConsistentOut *out = (spgInnerConsistentOut *) PG_GETARG_POINTER(1);
557 : : int i;
558 : : MemoryContext old_ctx;
559 : : RectBox *rect_box;
560 : : uint8 quadrant;
561 : : RangeBox *centroid,
562 : : **queries;
563 : :
564 : : /*
565 : : * We are saving the traversal value or initialize it an unbounded one, if
566 : : * we have just begun to walk the tree.
567 : : */
2034 akorotkov@postgresql 568 [ + + ]: 4543 : if (in->traversalValue)
569 : 4140 : rect_box = in->traversalValue;
570 : : else
571 : 403 : rect_box = initRectBox();
572 : :
2937 teodor@sigaev.ru 573 [ + + ]: 4543 : if (in->allTheSame)
574 : : {
575 : : /* Report that all nodes should be visited */
576 : 355 : out->nNodes = in->nNodes;
577 : 355 : out->nodeNumbers = (int *) palloc(sizeof(int) * in->nNodes);
578 [ + + ]: 3195 : for (i = 0; i < in->nNodes; i++)
579 : 2840 : out->nodeNumbers[i] = i;
580 : :
2034 akorotkov@postgresql 581 [ + + + - ]: 355 : if (in->norderbys > 0 && in->nNodes > 0)
582 : : {
583 : 46 : double *distances = palloc(sizeof(double) * in->norderbys);
584 : : int j;
585 : :
586 [ + + ]: 92 : for (j = 0; j < in->norderbys; j++)
587 : : {
588 : 46 : Point *pt = DatumGetPointP(in->orderbys[j].sk_argument);
589 : :
590 : 46 : distances[j] = pointToRectBoxDistance(pt, rect_box);
591 : : }
592 : :
593 : 46 : out->distances = (double **) palloc(sizeof(double *) * in->nNodes);
594 : 46 : out->distances[0] = distances;
595 : :
596 [ + + ]: 368 : for (i = 1; i < in->nNodes; i++)
597 : : {
598 : 322 : out->distances[i] = palloc(sizeof(double) * in->norderbys);
599 : 322 : memcpy(out->distances[i], distances,
600 : 322 : sizeof(double) * in->norderbys);
601 : : }
602 : : }
603 : :
2937 teodor@sigaev.ru 604 : 355 : PG_RETURN_VOID();
605 : : }
606 : :
607 : : /*
608 : : * We are casting the prefix and queries to RangeBoxes for ease of the
609 : : * following operations.
610 : : */
611 : 4188 : centroid = getRangeBox(DatumGetBoxP(in->prefixDatum));
612 : 4188 : queries = (RangeBox **) palloc(in->nkeys * sizeof(RangeBox *));
613 [ + + ]: 8082 : for (i = 0; i < in->nkeys; i++)
614 : : {
2302 615 : 3894 : BOX *box = spg_box_quad_get_scankey_bbox(&in->scankeys[i], NULL);
616 : :
617 : 3894 : queries[i] = getRangeBox(box);
618 : : }
619 : :
620 : : /* Allocate enough memory for nodes */
2937 621 : 4188 : out->nNodes = 0;
622 : 4188 : out->nodeNumbers = (int *) palloc(sizeof(int) * in->nNodes);
623 : 4188 : out->traversalValues = (void **) palloc(sizeof(void *) * in->nNodes);
2034 akorotkov@postgresql 624 [ + + ]: 4188 : if (in->norderbys > 0)
625 : 498 : out->distances = (double **) palloc(sizeof(double *) * in->nNodes);
626 : :
627 : : /*
628 : : * We switch memory context, because we want to allocate memory for new
629 : : * traversal values (next_rect_box) and pass these pieces of memory to
630 : : * further call of this function.
631 : : */
2937 teodor@sigaev.ru 632 : 4188 : old_ctx = MemoryContextSwitchTo(in->traversalMemoryContext);
633 : :
634 [ + + ]: 71196 : for (quadrant = 0; quadrant < in->nNodes; quadrant++)
635 : : {
2866 rhaas@postgresql.org 636 : 67008 : RectBox *next_rect_box = nextRectBox(rect_box, centroid, quadrant);
2937 tgl@sss.pgh.pa.us 637 : 67008 : bool flag = true;
638 : :
teodor@sigaev.ru 639 [ + + ]: 113259 : for (i = 0; i < in->nkeys; i++)
640 : : {
641 : 62304 : StrategyNumber strategy = in->scankeys[i].sk_strategy;
642 : :
643 [ + + + + : 62304 : switch (strategy)
+ + + + +
+ + - ]
644 : : {
645 : 3168 : case RTOverlapStrategyNumber:
646 : 3168 : flag = overlap4D(next_rect_box, queries[i]);
647 : 3168 : break;
648 : :
649 : 576 : case RTContainsStrategyNumber:
650 : 576 : flag = contain4D(next_rect_box, queries[i]);
651 : 576 : break;
652 : :
653 : 6720 : case RTSameStrategyNumber:
654 : : case RTContainedByStrategyNumber:
655 : 6720 : flag = contained4D(next_rect_box, queries[i]);
656 : 6720 : break;
657 : :
658 : 4656 : case RTLeftStrategyNumber:
659 : 4656 : flag = left4D(next_rect_box, queries[i]);
660 : 4656 : break;
661 : :
662 : 7344 : case RTOverLeftStrategyNumber:
663 : 7344 : flag = overLeft4D(next_rect_box, queries[i]);
664 : 7344 : break;
665 : :
666 : 13152 : case RTRightStrategyNumber:
667 : 13152 : flag = right4D(next_rect_box, queries[i]);
668 : 13152 : break;
669 : :
670 : 8496 : case RTOverRightStrategyNumber:
671 : 8496 : flag = overRight4D(next_rect_box, queries[i]);
672 : 8496 : break;
673 : :
674 : 4272 : case RTAboveStrategyNumber:
675 : 4272 : flag = above4D(next_rect_box, queries[i]);
676 : 4272 : break;
677 : :
678 : 6960 : case RTOverAboveStrategyNumber:
679 : 6960 : flag = overAbove4D(next_rect_box, queries[i]);
680 : 6960 : break;
681 : :
682 : 2160 : case RTBelowStrategyNumber:
683 : 2160 : flag = below4D(next_rect_box, queries[i]);
684 : 2160 : break;
685 : :
686 : 4800 : case RTOverBelowStrategyNumber:
687 : 4800 : flag = overBelow4D(next_rect_box, queries[i]);
688 : 4800 : break;
689 : :
2937 teodor@sigaev.ru 690 :UBC 0 : default:
691 [ # # ]: 0 : elog(ERROR, "unrecognized strategy: %d", strategy);
692 : : }
693 : :
694 : : /* If any check is failed, we have found our answer. */
2937 teodor@sigaev.ru 695 [ + + ]:CBC 62304 : if (!flag)
696 : 16053 : break;
697 : : }
698 : :
699 [ + + ]: 67008 : if (flag)
700 : : {
701 : 50955 : out->traversalValues[out->nNodes] = next_rect_box;
702 : 50955 : out->nodeNumbers[out->nNodes] = quadrant;
703 : :
2034 akorotkov@postgresql 704 [ + + ]: 50955 : if (in->norderbys > 0)
705 : : {
706 : 6555 : double *distances = palloc(sizeof(double) * in->norderbys);
707 : : int j;
708 : :
709 : 6555 : out->distances[out->nNodes] = distances;
710 : :
711 [ + + ]: 13110 : for (j = 0; j < in->norderbys; j++)
712 : : {
713 : 6555 : Point *pt = DatumGetPointP(in->orderbys[j].sk_argument);
714 : :
715 : 6555 : distances[j] = pointToRectBoxDistance(pt, next_rect_box);
716 : : }
717 : : }
718 : :
2937 teodor@sigaev.ru 719 : 50955 : out->nNodes++;
720 : : }
721 : : else
722 : : {
723 : : /*
724 : : * If this node is not selected, we don't need to keep the next
725 : : * traversal value in the memory context.
726 : : */
727 : 16053 : pfree(next_rect_box);
728 : : }
729 : : }
730 : :
731 : : /* Switch back */
732 : 4188 : MemoryContextSwitchTo(old_ctx);
733 : :
734 : 4188 : PG_RETURN_VOID();
735 : : }
736 : :
737 : : /*
738 : : * SP-GiST inner consistent function
739 : : */
740 : : Datum
741 : 424680 : spg_box_quad_leaf_consistent(PG_FUNCTION_ARGS)
742 : : {
743 : 424680 : spgLeafConsistentIn *in = (spgLeafConsistentIn *) PG_GETARG_POINTER(0);
744 : 424680 : spgLeafConsistentOut *out = (spgLeafConsistentOut *) PG_GETARG_POINTER(1);
745 : 424680 : Datum leaf = in->leafDatum;
tgl@sss.pgh.pa.us 746 : 424680 : bool flag = true;
747 : : int i;
748 : :
749 : : /* All tests are exact. */
teodor@sigaev.ru 750 : 424680 : out->recheck = false;
751 : :
752 : : /*
753 : : * Don't return leafValue unless told to; this is used for both box and
754 : : * polygon opclasses, and in the latter case the leaf datum is not even of
755 : : * the right type to return.
756 : : */
1106 tgl@sss.pgh.pa.us 757 [ + + ]: 424680 : if (in->returnData)
758 : 2913 : out->leafValue = leaf;
759 : :
760 : : /* Perform the required comparison(s) */
2937 teodor@sigaev.ru 761 [ + + ]: 743757 : for (i = 0; i < in->nkeys; i++)
762 : : {
2180 tgl@sss.pgh.pa.us 763 : 391671 : StrategyNumber strategy = in->scankeys[i].sk_strategy;
764 : 391671 : BOX *box = spg_box_quad_get_scankey_bbox(&in->scankeys[i],
765 : : &out->recheck);
766 : 391671 : Datum query = BoxPGetDatum(box);
767 : :
2937 teodor@sigaev.ru 768 [ + + + + : 391671 : switch (strategy)
+ + + + +
+ + + - ]
769 : : {
770 : 18159 : case RTOverlapStrategyNumber:
771 : 18159 : flag = DatumGetBool(DirectFunctionCall2(box_overlap, leaf,
772 : : query));
773 : 18159 : break;
774 : :
775 : 4203 : case RTContainsStrategyNumber:
776 : 4203 : flag = DatumGetBool(DirectFunctionCall2(box_contain, leaf,
777 : : query));
778 : 4203 : break;
779 : :
780 : 34626 : case RTContainedByStrategyNumber:
781 : 34626 : flag = DatumGetBool(DirectFunctionCall2(box_contained, leaf,
782 : : query));
783 : 34626 : break;
784 : :
785 : 6516 : case RTSameStrategyNumber:
786 : 6516 : flag = DatumGetBool(DirectFunctionCall2(box_same, leaf,
787 : : query));
788 : 6516 : break;
789 : :
790 : 24096 : case RTLeftStrategyNumber:
791 : 24096 : flag = DatumGetBool(DirectFunctionCall2(box_left, leaf,
792 : : query));
793 : 24096 : break;
794 : :
795 : 48939 : case RTOverLeftStrategyNumber:
796 : 48939 : flag = DatumGetBool(DirectFunctionCall2(box_overleft, leaf,
797 : : query));
798 : 48939 : break;
799 : :
800 : 64956 : case RTRightStrategyNumber:
801 : 64956 : flag = DatumGetBool(DirectFunctionCall2(box_right, leaf,
802 : : query));
803 : 64956 : break;
804 : :
805 : 55284 : case RTOverRightStrategyNumber:
806 : 55284 : flag = DatumGetBool(DirectFunctionCall2(box_overright, leaf,
807 : : query));
808 : 55284 : break;
809 : :
810 : 27960 : case RTAboveStrategyNumber:
811 : 27960 : flag = DatumGetBool(DirectFunctionCall2(box_above, leaf,
812 : : query));
813 : 27960 : break;
814 : :
815 : 54675 : case RTOverAboveStrategyNumber:
816 : 54675 : flag = DatumGetBool(DirectFunctionCall2(box_overabove, leaf,
817 : : query));
818 : 54675 : break;
819 : :
820 : 12939 : case RTBelowStrategyNumber:
821 : 12939 : flag = DatumGetBool(DirectFunctionCall2(box_below, leaf,
822 : : query));
823 : 12939 : break;
824 : :
825 : 39318 : case RTOverBelowStrategyNumber:
826 : 39318 : flag = DatumGetBool(DirectFunctionCall2(box_overbelow, leaf,
827 : : query));
828 : 39318 : break;
829 : :
2937 teodor@sigaev.ru 830 :UBC 0 : default:
831 [ # # ]: 0 : elog(ERROR, "unrecognized strategy: %d", strategy);
832 : : }
833 : :
834 : : /* If any check is failed, we have found our answer. */
2937 teodor@sigaev.ru 835 [ + + ]:CBC 391671 : if (!flag)
836 : 72594 : break;
837 : : }
838 : :
2034 akorotkov@postgresql 839 [ + + + + ]: 424680 : if (flag && in->norderbys > 0)
840 : : {
841 : 43272 : Oid distfnoid = in->orderbys[0].sk_func.fn_oid;
842 : :
843 : 43272 : out->distances = spg_key_orderbys_distances(leaf, false,
844 : : in->orderbys, in->norderbys);
845 : :
846 : : /* Recheck is necessary when computing distance to polygon */
847 : 43272 : out->recheckDistances = distfnoid == F_DIST_POLYP;
848 : : }
849 : :
2937 teodor@sigaev.ru 850 : 424680 : PG_RETURN_BOOL(flag);
851 : : }
852 : :
853 : :
854 : : /*
855 : : * SP-GiST config function for 2-D types that are lossy represented by their
856 : : * bounding boxes
857 : : */
858 : : Datum
2302 859 : 13 : spg_bbox_quad_config(PG_FUNCTION_ARGS)
860 : : {
861 : 13 : spgConfigOut *cfg = (spgConfigOut *) PG_GETARG_POINTER(1);
862 : :
863 : 13 : cfg->prefixType = BOXOID; /* A type represented by its bounding box */
864 : 13 : cfg->labelType = VOIDOID; /* We don't need node labels. */
865 : 13 : cfg->leafType = BOXOID;
866 : 13 : cfg->canReturnData = false;
867 : 13 : cfg->longValuesOK = false;
868 : :
869 : 13 : PG_RETURN_VOID();
870 : : }
871 : :
872 : : /*
873 : : * SP-GiST compress function for polygons
874 : : */
875 : : Datum
876 : 33000 : spg_poly_quad_compress(PG_FUNCTION_ARGS)
877 : : {
2180 tgl@sss.pgh.pa.us 878 : 33000 : POLYGON *polygon = PG_GETARG_POLYGON_P(0);
879 : : BOX *box;
880 : :
2086 tomas.vondra@postgre 881 : 33000 : box = (BOX *) palloc(sizeof(BOX));
882 : 33000 : *box = polygon->boundbox;
883 : :
2302 teodor@sigaev.ru 884 : 33000 : PG_RETURN_BOX_P(box);
885 : : }
|