/src/postgres/src/backend/utils/adt/geo_spgist.c
Line | Count | Source |
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-2026, 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 |
93 | | compareDoubles(const void *a, const void *b) |
94 | 0 | { |
95 | 0 | float8 x = *(const float8 *) a; |
96 | 0 | float8 y = *(const float8 *) b; |
97 | |
|
98 | 0 | if (x == y) |
99 | 0 | return 0; |
100 | 0 | return (x > y) ? 1 : -1; |
101 | 0 | } |
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 | | getQuadrant(BOX *centroid, BOX *inBox) |
131 | 0 | { |
132 | 0 | uint8 quadrant = 0; |
133 | |
|
134 | 0 | if (inBox->low.x > centroid->low.x) |
135 | 0 | quadrant |= 0x8; |
136 | |
|
137 | 0 | if (inBox->high.x > centroid->high.x) |
138 | 0 | quadrant |= 0x4; |
139 | |
|
140 | 0 | if (inBox->low.y > centroid->low.y) |
141 | 0 | quadrant |= 0x2; |
142 | |
|
143 | 0 | if (inBox->high.y > centroid->high.y) |
144 | 0 | quadrant |= 0x1; |
145 | |
|
146 | 0 | return quadrant; |
147 | 0 | } |
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 | | getRangeBox(BOX *box) |
158 | 0 | { |
159 | 0 | RangeBox *range_box = palloc_object(RangeBox); |
160 | |
|
161 | 0 | range_box->left.low = box->low.x; |
162 | 0 | range_box->left.high = box->high.x; |
163 | |
|
164 | 0 | range_box->right.low = box->low.y; |
165 | 0 | range_box->right.high = box->high.y; |
166 | |
|
167 | 0 | return range_box; |
168 | 0 | } |
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 * |
177 | | initRectBox(void) |
178 | 0 | { |
179 | 0 | RectBox *rect_box = palloc_object(RectBox); |
180 | 0 | float8 infinity = get_float8_infinity(); |
181 | |
|
182 | 0 | rect_box->range_box_x.left.low = -infinity; |
183 | 0 | rect_box->range_box_x.left.high = infinity; |
184 | |
|
185 | 0 | rect_box->range_box_x.right.low = -infinity; |
186 | 0 | rect_box->range_box_x.right.high = infinity; |
187 | |
|
188 | 0 | rect_box->range_box_y.left.low = -infinity; |
189 | 0 | rect_box->range_box_y.left.high = infinity; |
190 | |
|
191 | 0 | rect_box->range_box_y.right.low = -infinity; |
192 | 0 | rect_box->range_box_y.right.high = infinity; |
193 | |
|
194 | 0 | return rect_box; |
195 | 0 | } |
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 | | nextRectBox(RectBox *rect_box, RangeBox *centroid, uint8 quadrant) |
206 | 0 | { |
207 | 0 | RectBox *next_rect_box = palloc_object(RectBox); |
208 | |
|
209 | 0 | memcpy(next_rect_box, rect_box, sizeof(RectBox)); |
210 | |
|
211 | 0 | if (quadrant & 0x8) |
212 | 0 | next_rect_box->range_box_x.left.low = centroid->left.low; |
213 | 0 | else |
214 | 0 | next_rect_box->range_box_x.left.high = centroid->left.low; |
215 | |
|
216 | 0 | if (quadrant & 0x4) |
217 | 0 | next_rect_box->range_box_x.right.low = centroid->left.high; |
218 | 0 | else |
219 | 0 | next_rect_box->range_box_x.right.high = centroid->left.high; |
220 | |
|
221 | 0 | if (quadrant & 0x2) |
222 | 0 | next_rect_box->range_box_y.left.low = centroid->right.low; |
223 | 0 | else |
224 | 0 | next_rect_box->range_box_y.left.high = centroid->right.low; |
225 | |
|
226 | 0 | if (quadrant & 0x1) |
227 | 0 | next_rect_box->range_box_y.right.low = centroid->right.high; |
228 | 0 | else |
229 | 0 | next_rect_box->range_box_y.right.high = centroid->right.high; |
230 | |
|
231 | 0 | return next_rect_box; |
232 | 0 | } |
233 | | |
234 | | /* Can any range from range_box overlap with this argument? */ |
235 | | static bool |
236 | | overlap2D(RangeBox *range_box, Range *query) |
237 | 0 | { |
238 | 0 | return FPge(range_box->right.high, query->low) && |
239 | 0 | FPle(range_box->left.low, query->high); |
240 | 0 | } |
241 | | |
242 | | /* Can any rectangle from rect_box overlap with this argument? */ |
243 | | static bool |
244 | | overlap4D(RectBox *rect_box, RangeBox *query) |
245 | 0 | { |
246 | 0 | return overlap2D(&rect_box->range_box_x, &query->left) && |
247 | 0 | overlap2D(&rect_box->range_box_y, &query->right); |
248 | 0 | } |
249 | | |
250 | | /* Can any range from range_box contain this argument? */ |
251 | | static bool |
252 | | contain2D(RangeBox *range_box, Range *query) |
253 | 0 | { |
254 | 0 | return FPge(range_box->right.high, query->high) && |
255 | 0 | FPle(range_box->left.low, query->low); |
256 | 0 | } |
257 | | |
258 | | /* Can any rectangle from rect_box contain this argument? */ |
259 | | static bool |
260 | | contain4D(RectBox *rect_box, RangeBox *query) |
261 | 0 | { |
262 | 0 | return contain2D(&rect_box->range_box_x, &query->left) && |
263 | 0 | contain2D(&rect_box->range_box_y, &query->right); |
264 | 0 | } |
265 | | |
266 | | /* Can any range from range_box be contained by this argument? */ |
267 | | static bool |
268 | | contained2D(RangeBox *range_box, Range *query) |
269 | 0 | { |
270 | 0 | return FPle(range_box->left.low, query->high) && |
271 | 0 | FPge(range_box->left.high, query->low) && |
272 | 0 | FPle(range_box->right.low, query->high) && |
273 | 0 | FPge(range_box->right.high, query->low); |
274 | 0 | } |
275 | | |
276 | | /* Can any rectangle from rect_box be contained by this argument? */ |
277 | | static bool |
278 | | contained4D(RectBox *rect_box, RangeBox *query) |
279 | 0 | { |
280 | 0 | return contained2D(&rect_box->range_box_x, &query->left) && |
281 | 0 | contained2D(&rect_box->range_box_y, &query->right); |
282 | 0 | } |
283 | | |
284 | | /* Can any range from range_box to be lower than this argument? */ |
285 | | static bool |
286 | | lower2D(RangeBox *range_box, Range *query) |
287 | 0 | { |
288 | 0 | return FPlt(range_box->left.low, query->low) && |
289 | 0 | FPlt(range_box->right.low, query->low); |
290 | 0 | } |
291 | | |
292 | | /* Can any range from range_box not extend to the right side of the query? */ |
293 | | static bool |
294 | | overLower2D(RangeBox *range_box, Range *query) |
295 | 0 | { |
296 | 0 | return FPle(range_box->left.low, query->high) && |
297 | 0 | FPle(range_box->right.low, query->high); |
298 | 0 | } |
299 | | |
300 | | /* Can any range from range_box to be higher than this argument? */ |
301 | | static bool |
302 | | higher2D(RangeBox *range_box, Range *query) |
303 | 0 | { |
304 | 0 | return FPgt(range_box->left.high, query->high) && |
305 | 0 | FPgt(range_box->right.high, query->high); |
306 | 0 | } |
307 | | |
308 | | /* Can any range from range_box not extend to the left side of the query? */ |
309 | | static bool |
310 | | overHigher2D(RangeBox *range_box, Range *query) |
311 | 0 | { |
312 | 0 | return FPge(range_box->left.high, query->low) && |
313 | 0 | FPge(range_box->right.high, query->low); |
314 | 0 | } |
315 | | |
316 | | /* Can any rectangle from rect_box be left of this argument? */ |
317 | | static bool |
318 | | left4D(RectBox *rect_box, RangeBox *query) |
319 | 0 | { |
320 | 0 | return lower2D(&rect_box->range_box_x, &query->left); |
321 | 0 | } |
322 | | |
323 | | /* Can any rectangle from rect_box does not extend the right of this argument? */ |
324 | | static bool |
325 | | overLeft4D(RectBox *rect_box, RangeBox *query) |
326 | 0 | { |
327 | 0 | return overLower2D(&rect_box->range_box_x, &query->left); |
328 | 0 | } |
329 | | |
330 | | /* Can any rectangle from rect_box be right of this argument? */ |
331 | | static bool |
332 | | right4D(RectBox *rect_box, RangeBox *query) |
333 | 0 | { |
334 | 0 | return higher2D(&rect_box->range_box_x, &query->left); |
335 | 0 | } |
336 | | |
337 | | /* Can any rectangle from rect_box does not extend the left of this argument? */ |
338 | | static bool |
339 | | overRight4D(RectBox *rect_box, RangeBox *query) |
340 | 0 | { |
341 | 0 | return overHigher2D(&rect_box->range_box_x, &query->left); |
342 | 0 | } |
343 | | |
344 | | /* Can any rectangle from rect_box be below of this argument? */ |
345 | | static bool |
346 | | below4D(RectBox *rect_box, RangeBox *query) |
347 | 0 | { |
348 | 0 | return lower2D(&rect_box->range_box_y, &query->right); |
349 | 0 | } |
350 | | |
351 | | /* Can any rectangle from rect_box does not extend above this argument? */ |
352 | | static bool |
353 | | overBelow4D(RectBox *rect_box, RangeBox *query) |
354 | 0 | { |
355 | 0 | return overLower2D(&rect_box->range_box_y, &query->right); |
356 | 0 | } |
357 | | |
358 | | /* Can any rectangle from rect_box be above of this argument? */ |
359 | | static bool |
360 | | above4D(RectBox *rect_box, RangeBox *query) |
361 | 0 | { |
362 | 0 | return higher2D(&rect_box->range_box_y, &query->right); |
363 | 0 | } |
364 | | |
365 | | /* Can any rectangle from rect_box does not extend below of this argument? */ |
366 | | static bool |
367 | | overAbove4D(RectBox *rect_box, RangeBox *query) |
368 | 0 | { |
369 | 0 | return overHigher2D(&rect_box->range_box_y, &query->right); |
370 | 0 | } |
371 | | |
372 | | /* Lower bound for the distance between point and rect_box */ |
373 | | static double |
374 | | pointToRectBoxDistance(Point *point, RectBox *rect_box) |
375 | 0 | { |
376 | 0 | double dx; |
377 | 0 | double dy; |
378 | |
|
379 | 0 | if (point->x < rect_box->range_box_x.left.low) |
380 | 0 | dx = rect_box->range_box_x.left.low - point->x; |
381 | 0 | else if (point->x > rect_box->range_box_x.right.high) |
382 | 0 | dx = point->x - rect_box->range_box_x.right.high; |
383 | 0 | else |
384 | 0 | dx = 0; |
385 | |
|
386 | 0 | if (point->y < rect_box->range_box_y.left.low) |
387 | 0 | dy = rect_box->range_box_y.left.low - point->y; |
388 | 0 | else if (point->y > rect_box->range_box_y.right.high) |
389 | 0 | dy = point->y - rect_box->range_box_y.right.high; |
390 | 0 | else |
391 | 0 | dy = 0; |
392 | |
|
393 | 0 | return hypot(dx, dy); |
394 | 0 | } |
395 | | |
396 | | |
397 | | /* |
398 | | * SP-GiST config function |
399 | | */ |
400 | | Datum |
401 | | spg_box_quad_config(PG_FUNCTION_ARGS) |
402 | 0 | { |
403 | 0 | spgConfigOut *cfg = (spgConfigOut *) PG_GETARG_POINTER(1); |
404 | |
|
405 | 0 | cfg->prefixType = BOXOID; |
406 | 0 | cfg->labelType = VOIDOID; /* We don't need node labels. */ |
407 | 0 | cfg->canReturnData = true; |
408 | 0 | cfg->longValuesOK = false; |
409 | |
|
410 | 0 | PG_RETURN_VOID(); |
411 | 0 | } |
412 | | |
413 | | /* |
414 | | * SP-GiST choose function |
415 | | */ |
416 | | Datum |
417 | | spg_box_quad_choose(PG_FUNCTION_ARGS) |
418 | 0 | { |
419 | 0 | spgChooseIn *in = (spgChooseIn *) PG_GETARG_POINTER(0); |
420 | 0 | spgChooseOut *out = (spgChooseOut *) PG_GETARG_POINTER(1); |
421 | 0 | BOX *centroid = DatumGetBoxP(in->prefixDatum), |
422 | 0 | *box = DatumGetBoxP(in->leafDatum); |
423 | |
|
424 | 0 | out->resultType = spgMatchNode; |
425 | 0 | out->result.matchNode.restDatum = BoxPGetDatum(box); |
426 | | |
427 | | /* nodeN will be set by core, when allTheSame. */ |
428 | 0 | if (!in->allTheSame) |
429 | 0 | out->result.matchNode.nodeN = getQuadrant(centroid, box); |
430 | |
|
431 | 0 | PG_RETURN_VOID(); |
432 | 0 | } |
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 | | spg_box_quad_picksplit(PG_FUNCTION_ARGS) |
442 | 0 | { |
443 | 0 | spgPickSplitIn *in = (spgPickSplitIn *) PG_GETARG_POINTER(0); |
444 | 0 | spgPickSplitOut *out = (spgPickSplitOut *) PG_GETARG_POINTER(1); |
445 | 0 | BOX *centroid; |
446 | 0 | int median, |
447 | 0 | i; |
448 | 0 | float8 *lowXs = palloc_array(float8, in->nTuples); |
449 | 0 | float8 *highXs = palloc_array(float8, in->nTuples); |
450 | 0 | float8 *lowYs = palloc_array(float8, in->nTuples); |
451 | 0 | float8 *highYs = palloc_array(float8, in->nTuples); |
452 | | |
453 | | /* Calculate median of all 4D coordinates */ |
454 | 0 | for (i = 0; i < in->nTuples; i++) |
455 | 0 | { |
456 | 0 | BOX *box = DatumGetBoxP(in->datums[i]); |
457 | |
|
458 | 0 | lowXs[i] = box->low.x; |
459 | 0 | highXs[i] = box->high.x; |
460 | 0 | lowYs[i] = box->low.y; |
461 | 0 | highYs[i] = box->high.y; |
462 | 0 | } |
463 | |
|
464 | 0 | qsort(lowXs, in->nTuples, sizeof(float8), compareDoubles); |
465 | 0 | qsort(highXs, in->nTuples, sizeof(float8), compareDoubles); |
466 | 0 | qsort(lowYs, in->nTuples, sizeof(float8), compareDoubles); |
467 | 0 | qsort(highYs, in->nTuples, sizeof(float8), compareDoubles); |
468 | |
|
469 | 0 | median = in->nTuples / 2; |
470 | |
|
471 | 0 | centroid = palloc_object(BOX); |
472 | |
|
473 | 0 | centroid->low.x = lowXs[median]; |
474 | 0 | centroid->high.x = highXs[median]; |
475 | 0 | centroid->low.y = lowYs[median]; |
476 | 0 | centroid->high.y = highYs[median]; |
477 | | |
478 | | /* Fill the output */ |
479 | 0 | out->hasPrefix = true; |
480 | 0 | out->prefixDatum = BoxPGetDatum(centroid); |
481 | |
|
482 | 0 | out->nNodes = 16; |
483 | 0 | out->nodeLabels = NULL; /* We don't need node labels. */ |
484 | |
|
485 | 0 | out->mapTuplesToNodes = palloc_array(int, in->nTuples); |
486 | 0 | out->leafTupleDatums = palloc_array(Datum, in->nTuples); |
487 | | |
488 | | /* |
489 | | * Assign ranges to corresponding nodes according to quadrants relative to |
490 | | * the "centroid" range |
491 | | */ |
492 | 0 | for (i = 0; i < in->nTuples; i++) |
493 | 0 | { |
494 | 0 | BOX *box = DatumGetBoxP(in->datums[i]); |
495 | 0 | uint8 quadrant = getQuadrant(centroid, box); |
496 | |
|
497 | 0 | out->leafTupleDatums[i] = BoxPGetDatum(box); |
498 | 0 | out->mapTuplesToNodes[i] = quadrant; |
499 | 0 | } |
500 | |
|
501 | 0 | PG_RETURN_VOID(); |
502 | 0 | } |
503 | | |
504 | | /* |
505 | | * Check if result of consistent method based on bounding box is exact. |
506 | | */ |
507 | | static bool |
508 | | is_bounding_box_test_exact(StrategyNumber strategy) |
509 | 0 | { |
510 | 0 | switch (strategy) |
511 | 0 | { |
512 | 0 | case RTLeftStrategyNumber: |
513 | 0 | case RTOverLeftStrategyNumber: |
514 | 0 | case RTOverRightStrategyNumber: |
515 | 0 | case RTRightStrategyNumber: |
516 | 0 | case RTOverBelowStrategyNumber: |
517 | 0 | case RTBelowStrategyNumber: |
518 | 0 | case RTAboveStrategyNumber: |
519 | 0 | case RTOverAboveStrategyNumber: |
520 | 0 | return true; |
521 | | |
522 | 0 | default: |
523 | 0 | return false; |
524 | 0 | } |
525 | 0 | } |
526 | | |
527 | | /* |
528 | | * Get bounding box for ScanKey. |
529 | | */ |
530 | | static BOX * |
531 | | spg_box_quad_get_scankey_bbox(ScanKey sk, bool *recheck) |
532 | 0 | { |
533 | 0 | switch (sk->sk_subtype) |
534 | 0 | { |
535 | 0 | case BOXOID: |
536 | 0 | return DatumGetBoxP(sk->sk_argument); |
537 | | |
538 | 0 | case POLYGONOID: |
539 | 0 | if (recheck && !is_bounding_box_test_exact(sk->sk_strategy)) |
540 | 0 | *recheck = true; |
541 | 0 | return &DatumGetPolygonP(sk->sk_argument)->boundbox; |
542 | | |
543 | 0 | default: |
544 | 0 | elog(ERROR, "unrecognized scankey subtype: %d", sk->sk_subtype); |
545 | 0 | return NULL; |
546 | 0 | } |
547 | 0 | } |
548 | | |
549 | | /* |
550 | | * SP-GiST inner consistent function |
551 | | */ |
552 | | Datum |
553 | | spg_box_quad_inner_consistent(PG_FUNCTION_ARGS) |
554 | 0 | { |
555 | 0 | spgInnerConsistentIn *in = (spgInnerConsistentIn *) PG_GETARG_POINTER(0); |
556 | 0 | spgInnerConsistentOut *out = (spgInnerConsistentOut *) PG_GETARG_POINTER(1); |
557 | 0 | int i; |
558 | 0 | MemoryContext old_ctx; |
559 | 0 | RectBox *rect_box; |
560 | 0 | uint8 quadrant; |
561 | 0 | RangeBox *centroid, |
562 | 0 | **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 | | */ |
568 | 0 | if (in->traversalValue) |
569 | 0 | rect_box = in->traversalValue; |
570 | 0 | else |
571 | 0 | rect_box = initRectBox(); |
572 | |
|
573 | 0 | if (in->allTheSame) |
574 | 0 | { |
575 | | /* Report that all nodes should be visited */ |
576 | 0 | out->nNodes = in->nNodes; |
577 | 0 | out->nodeNumbers = palloc_array(int, in->nNodes); |
578 | 0 | for (i = 0; i < in->nNodes; i++) |
579 | 0 | out->nodeNumbers[i] = i; |
580 | |
|
581 | 0 | if (in->norderbys > 0 && in->nNodes > 0) |
582 | 0 | { |
583 | 0 | double *distances = palloc_array(double, in->norderbys); |
584 | 0 | int j; |
585 | |
|
586 | 0 | for (j = 0; j < in->norderbys; j++) |
587 | 0 | { |
588 | 0 | Point *pt = DatumGetPointP(in->orderbys[j].sk_argument); |
589 | |
|
590 | 0 | distances[j] = pointToRectBoxDistance(pt, rect_box); |
591 | 0 | } |
592 | |
|
593 | 0 | out->distances = palloc_array(double *, in->nNodes); |
594 | 0 | out->distances[0] = distances; |
595 | |
|
596 | 0 | for (i = 1; i < in->nNodes; i++) |
597 | 0 | { |
598 | 0 | out->distances[i] = palloc_array(double, in->norderbys); |
599 | 0 | memcpy(out->distances[i], distances, |
600 | 0 | sizeof(double) * in->norderbys); |
601 | 0 | } |
602 | 0 | } |
603 | |
|
604 | 0 | PG_RETURN_VOID(); |
605 | 0 | } |
606 | | |
607 | | /* |
608 | | * We are casting the prefix and queries to RangeBoxes for ease of the |
609 | | * following operations. |
610 | | */ |
611 | 0 | centroid = getRangeBox(DatumGetBoxP(in->prefixDatum)); |
612 | 0 | queries = palloc_array(RangeBox *, in->nkeys); |
613 | 0 | for (i = 0; i < in->nkeys; i++) |
614 | 0 | { |
615 | 0 | BOX *box = spg_box_quad_get_scankey_bbox(&in->scankeys[i], NULL); |
616 | |
|
617 | 0 | queries[i] = getRangeBox(box); |
618 | 0 | } |
619 | | |
620 | | /* Allocate enough memory for nodes */ |
621 | 0 | out->nNodes = 0; |
622 | 0 | out->nodeNumbers = palloc_array(int, in->nNodes); |
623 | 0 | out->traversalValues = palloc_array(void *, in->nNodes); |
624 | 0 | if (in->norderbys > 0) |
625 | 0 | out->distances = palloc_array(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 | | */ |
632 | 0 | old_ctx = MemoryContextSwitchTo(in->traversalMemoryContext); |
633 | |
|
634 | 0 | for (quadrant = 0; quadrant < in->nNodes; quadrant++) |
635 | 0 | { |
636 | 0 | RectBox *next_rect_box = nextRectBox(rect_box, centroid, quadrant); |
637 | 0 | bool flag = true; |
638 | |
|
639 | 0 | for (i = 0; i < in->nkeys; i++) |
640 | 0 | { |
641 | 0 | StrategyNumber strategy = in->scankeys[i].sk_strategy; |
642 | |
|
643 | 0 | switch (strategy) |
644 | 0 | { |
645 | 0 | case RTOverlapStrategyNumber: |
646 | 0 | flag = overlap4D(next_rect_box, queries[i]); |
647 | 0 | break; |
648 | | |
649 | 0 | case RTContainsStrategyNumber: |
650 | 0 | flag = contain4D(next_rect_box, queries[i]); |
651 | 0 | break; |
652 | | |
653 | 0 | case RTSameStrategyNumber: |
654 | 0 | case RTContainedByStrategyNumber: |
655 | 0 | flag = contained4D(next_rect_box, queries[i]); |
656 | 0 | break; |
657 | | |
658 | 0 | case RTLeftStrategyNumber: |
659 | 0 | flag = left4D(next_rect_box, queries[i]); |
660 | 0 | break; |
661 | | |
662 | 0 | case RTOverLeftStrategyNumber: |
663 | 0 | flag = overLeft4D(next_rect_box, queries[i]); |
664 | 0 | break; |
665 | | |
666 | 0 | case RTRightStrategyNumber: |
667 | 0 | flag = right4D(next_rect_box, queries[i]); |
668 | 0 | break; |
669 | | |
670 | 0 | case RTOverRightStrategyNumber: |
671 | 0 | flag = overRight4D(next_rect_box, queries[i]); |
672 | 0 | break; |
673 | | |
674 | 0 | case RTAboveStrategyNumber: |
675 | 0 | flag = above4D(next_rect_box, queries[i]); |
676 | 0 | break; |
677 | | |
678 | 0 | case RTOverAboveStrategyNumber: |
679 | 0 | flag = overAbove4D(next_rect_box, queries[i]); |
680 | 0 | break; |
681 | | |
682 | 0 | case RTBelowStrategyNumber: |
683 | 0 | flag = below4D(next_rect_box, queries[i]); |
684 | 0 | break; |
685 | | |
686 | 0 | case RTOverBelowStrategyNumber: |
687 | 0 | flag = overBelow4D(next_rect_box, queries[i]); |
688 | 0 | break; |
689 | | |
690 | 0 | default: |
691 | 0 | elog(ERROR, "unrecognized strategy: %d", strategy); |
692 | 0 | } |
693 | | |
694 | | /* If any check is failed, we have found our answer. */ |
695 | 0 | if (!flag) |
696 | 0 | break; |
697 | 0 | } |
698 | | |
699 | 0 | if (flag) |
700 | 0 | { |
701 | 0 | out->traversalValues[out->nNodes] = next_rect_box; |
702 | 0 | out->nodeNumbers[out->nNodes] = quadrant; |
703 | |
|
704 | 0 | if (in->norderbys > 0) |
705 | 0 | { |
706 | 0 | double *distances = palloc_array(double, in->norderbys); |
707 | 0 | int j; |
708 | |
|
709 | 0 | out->distances[out->nNodes] = distances; |
710 | |
|
711 | 0 | for (j = 0; j < in->norderbys; j++) |
712 | 0 | { |
713 | 0 | Point *pt = DatumGetPointP(in->orderbys[j].sk_argument); |
714 | |
|
715 | 0 | distances[j] = pointToRectBoxDistance(pt, next_rect_box); |
716 | 0 | } |
717 | 0 | } |
718 | |
|
719 | 0 | out->nNodes++; |
720 | 0 | } |
721 | 0 | else |
722 | 0 | { |
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 | 0 | pfree(next_rect_box); |
728 | 0 | } |
729 | 0 | } |
730 | | |
731 | | /* Switch back */ |
732 | 0 | MemoryContextSwitchTo(old_ctx); |
733 | |
|
734 | 0 | PG_RETURN_VOID(); |
735 | 0 | } |
736 | | |
737 | | /* |
738 | | * SP-GiST inner consistent function |
739 | | */ |
740 | | Datum |
741 | | spg_box_quad_leaf_consistent(PG_FUNCTION_ARGS) |
742 | 0 | { |
743 | 0 | spgLeafConsistentIn *in = (spgLeafConsistentIn *) PG_GETARG_POINTER(0); |
744 | 0 | spgLeafConsistentOut *out = (spgLeafConsistentOut *) PG_GETARG_POINTER(1); |
745 | 0 | Datum leaf = in->leafDatum; |
746 | 0 | bool flag = true; |
747 | 0 | int i; |
748 | | |
749 | | /* All tests are exact. */ |
750 | 0 | 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 | | */ |
757 | 0 | if (in->returnData) |
758 | 0 | out->leafValue = leaf; |
759 | | |
760 | | /* Perform the required comparison(s) */ |
761 | 0 | for (i = 0; i < in->nkeys; i++) |
762 | 0 | { |
763 | 0 | StrategyNumber strategy = in->scankeys[i].sk_strategy; |
764 | 0 | BOX *box = spg_box_quad_get_scankey_bbox(&in->scankeys[i], |
765 | 0 | &out->recheck); |
766 | 0 | Datum query = BoxPGetDatum(box); |
767 | |
|
768 | 0 | switch (strategy) |
769 | 0 | { |
770 | 0 | case RTOverlapStrategyNumber: |
771 | 0 | flag = DatumGetBool(DirectFunctionCall2(box_overlap, leaf, |
772 | 0 | query)); |
773 | 0 | break; |
774 | | |
775 | 0 | case RTContainsStrategyNumber: |
776 | 0 | flag = DatumGetBool(DirectFunctionCall2(box_contain, leaf, |
777 | 0 | query)); |
778 | 0 | break; |
779 | | |
780 | 0 | case RTContainedByStrategyNumber: |
781 | 0 | flag = DatumGetBool(DirectFunctionCall2(box_contained, leaf, |
782 | 0 | query)); |
783 | 0 | break; |
784 | | |
785 | 0 | case RTSameStrategyNumber: |
786 | 0 | flag = DatumGetBool(DirectFunctionCall2(box_same, leaf, |
787 | 0 | query)); |
788 | 0 | break; |
789 | | |
790 | 0 | case RTLeftStrategyNumber: |
791 | 0 | flag = DatumGetBool(DirectFunctionCall2(box_left, leaf, |
792 | 0 | query)); |
793 | 0 | break; |
794 | | |
795 | 0 | case RTOverLeftStrategyNumber: |
796 | 0 | flag = DatumGetBool(DirectFunctionCall2(box_overleft, leaf, |
797 | 0 | query)); |
798 | 0 | break; |
799 | | |
800 | 0 | case RTRightStrategyNumber: |
801 | 0 | flag = DatumGetBool(DirectFunctionCall2(box_right, leaf, |
802 | 0 | query)); |
803 | 0 | break; |
804 | | |
805 | 0 | case RTOverRightStrategyNumber: |
806 | 0 | flag = DatumGetBool(DirectFunctionCall2(box_overright, leaf, |
807 | 0 | query)); |
808 | 0 | break; |
809 | | |
810 | 0 | case RTAboveStrategyNumber: |
811 | 0 | flag = DatumGetBool(DirectFunctionCall2(box_above, leaf, |
812 | 0 | query)); |
813 | 0 | break; |
814 | | |
815 | 0 | case RTOverAboveStrategyNumber: |
816 | 0 | flag = DatumGetBool(DirectFunctionCall2(box_overabove, leaf, |
817 | 0 | query)); |
818 | 0 | break; |
819 | | |
820 | 0 | case RTBelowStrategyNumber: |
821 | 0 | flag = DatumGetBool(DirectFunctionCall2(box_below, leaf, |
822 | 0 | query)); |
823 | 0 | break; |
824 | | |
825 | 0 | case RTOverBelowStrategyNumber: |
826 | 0 | flag = DatumGetBool(DirectFunctionCall2(box_overbelow, leaf, |
827 | 0 | query)); |
828 | 0 | break; |
829 | | |
830 | 0 | default: |
831 | 0 | elog(ERROR, "unrecognized strategy: %d", strategy); |
832 | 0 | } |
833 | | |
834 | | /* If any check is failed, we have found our answer. */ |
835 | 0 | if (!flag) |
836 | 0 | break; |
837 | 0 | } |
838 | | |
839 | 0 | if (flag && in->norderbys > 0) |
840 | 0 | { |
841 | 0 | Oid distfnoid = in->orderbys[0].sk_func.fn_oid; |
842 | |
|
843 | 0 | out->distances = spg_key_orderbys_distances(leaf, false, |
844 | 0 | in->orderbys, in->norderbys); |
845 | | |
846 | | /* Recheck is necessary when computing distance to polygon */ |
847 | 0 | out->recheckDistances = distfnoid == F_DIST_POLYP; |
848 | 0 | } |
849 | |
|
850 | 0 | PG_RETURN_BOOL(flag); |
851 | 0 | } |
852 | | |
853 | | |
854 | | /* |
855 | | * SP-GiST config function for 2-D types that are lossy represented by their |
856 | | * bounding boxes |
857 | | */ |
858 | | Datum |
859 | | spg_bbox_quad_config(PG_FUNCTION_ARGS) |
860 | 0 | { |
861 | 0 | spgConfigOut *cfg = (spgConfigOut *) PG_GETARG_POINTER(1); |
862 | |
|
863 | 0 | cfg->prefixType = BOXOID; /* A type represented by its bounding box */ |
864 | 0 | cfg->labelType = VOIDOID; /* We don't need node labels. */ |
865 | 0 | cfg->leafType = BOXOID; |
866 | 0 | cfg->canReturnData = false; |
867 | 0 | cfg->longValuesOK = false; |
868 | |
|
869 | 0 | PG_RETURN_VOID(); |
870 | 0 | } |
871 | | |
872 | | /* |
873 | | * SP-GiST compress function for polygons |
874 | | */ |
875 | | Datum |
876 | | spg_poly_quad_compress(PG_FUNCTION_ARGS) |
877 | 0 | { |
878 | 0 | POLYGON *polygon = PG_GETARG_POLYGON_P(0); |
879 | 0 | BOX *box; |
880 | |
|
881 | 0 | box = palloc_object(BOX); |
882 | 0 | *box = polygon->boundbox; |
883 | |
|
884 | 0 | PG_RETURN_BOX_P(box); |
885 | 0 | } |