Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * gistproc.c
4 : * Support procedures for GiSTs over 2-D objects (boxes, polygons, circles,
5 : * points).
6 : *
7 : * This gives R-tree behavior, with Guttman's poly-time split algorithm.
8 : *
9 : *
10 : * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
11 : * Portions Copyright (c) 1994, Regents of the University of California
12 : *
13 : * IDENTIFICATION
14 : * src/backend/access/gist/gistproc.c
15 : *
16 : *-------------------------------------------------------------------------
17 : */
18 : #include "postgres.h"
19 :
20 : #include <math.h>
21 :
22 : #include "access/gist.h"
23 : #include "access/stratnum.h"
24 : #include "utils/float.h"
25 : #include "utils/fmgrprotos.h"
26 : #include "utils/geo_decls.h"
27 : #include "utils/sortsupport.h"
28 :
29 :
30 : static bool gist_box_leaf_consistent(BOX *key, BOX *query,
31 : StrategyNumber strategy);
32 : static bool rtree_internal_consistent(BOX *key, BOX *query,
33 : StrategyNumber strategy);
34 :
35 : static uint64 point_zorder_internal(float4 x, float4 y);
36 : static uint64 part_bits32_by2(uint32 x);
37 : static uint32 ieee_float32_to_uint32(float f);
38 : static int gist_bbox_zorder_cmp(Datum a, Datum b, SortSupport ssup);
39 : static Datum gist_bbox_zorder_abbrev_convert(Datum original, SortSupport ssup);
40 : static bool gist_bbox_zorder_abbrev_abort(int memtupcount, SortSupport ssup);
41 :
42 :
43 : /* Minimum accepted ratio of split */
44 : #define LIMIT_RATIO 0.3
45 :
46 :
47 : /**************************************************
48 : * Box ops
49 : **************************************************/
50 :
51 : /*
52 : * Calculates union of two boxes, a and b. The result is stored in *n.
53 : */
54 : static void
55 40331712 : rt_box_union(BOX *n, const BOX *a, const BOX *b)
56 : {
57 40331712 : n->high.x = float8_max(a->high.x, b->high.x);
58 40331712 : n->high.y = float8_max(a->high.y, b->high.y);
59 40331712 : n->low.x = float8_min(a->low.x, b->low.x);
60 40331712 : n->low.y = float8_min(a->low.y, b->low.y);
61 40331712 : }
62 :
63 : /*
64 : * Size of a BOX for penalty-calculation purposes.
65 : * The result can be +Infinity, but not NaN.
66 : */
67 : static float8
68 80663424 : size_box(const BOX *box)
69 : {
70 : /*
71 : * Check for zero-width cases. Note that we define the size of a zero-
72 : * by-infinity box as zero. It's important to special-case this somehow,
73 : * as naively multiplying infinity by zero will produce NaN.
74 : *
75 : * The less-than cases should not happen, but if they do, say "zero".
76 : */
77 161326812 : if (float8_le(box->high.x, box->low.x) ||
78 80663388 : float8_le(box->high.y, box->low.y))
79 36 : return 0.0;
80 :
81 : /*
82 : * We treat NaN as larger than +Infinity, so any distance involving a NaN
83 : * and a non-NaN is infinite. Note the previous check eliminated the
84 : * possibility that the low fields are NaNs.
85 : */
86 80663388 : if (isnan(box->high.x) || isnan(box->high.y))
87 0 : return get_float8_infinity();
88 80663388 : return float8_mul(float8_mi(box->high.x, box->low.x),
89 80663388 : float8_mi(box->high.y, box->low.y));
90 : }
91 :
92 : /*
93 : * Return amount by which the union of the two boxes is larger than
94 : * the original BOX's area. The result can be +Infinity, but not NaN.
95 : */
96 : static float8
97 40331712 : box_penalty(const BOX *original, const BOX *new)
98 : {
99 : BOX unionbox;
100 :
101 40331712 : rt_box_union(&unionbox, original, new);
102 40331712 : return float8_mi(size_box(&unionbox), size_box(original));
103 : }
104 :
105 : /*
106 : * The GiST Consistent method for boxes
107 : *
108 : * Should return false if for all data items x below entry,
109 : * the predicate x op query must be false, where op is the oper
110 : * corresponding to strategy in the pg_amop table.
111 : */
112 : Datum
113 8166 : gist_box_consistent(PG_FUNCTION_ARGS)
114 : {
115 8166 : GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
116 8166 : BOX *query = PG_GETARG_BOX_P(1);
117 8166 : StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2);
118 : #ifdef NOT_USED
119 : Oid subtype = PG_GETARG_OID(3);
120 : #endif
121 8166 : bool *recheck = (bool *) PG_GETARG_POINTER(4);
122 :
123 : /* All cases served by this function are exact */
124 8166 : *recheck = false;
125 :
126 8166 : if (DatumGetBoxP(entry->key) == NULL || query == NULL)
127 0 : PG_RETURN_BOOL(false);
128 :
129 : /*
130 : * if entry is not leaf, use rtree_internal_consistent, else use
131 : * gist_box_leaf_consistent
132 : */
133 8166 : if (GIST_LEAF(entry))
134 4692 : PG_RETURN_BOOL(gist_box_leaf_consistent(DatumGetBoxP(entry->key),
135 : query,
136 : strategy));
137 : else
138 3474 : PG_RETURN_BOOL(rtree_internal_consistent(DatumGetBoxP(entry->key),
139 : query,
140 : strategy));
141 : }
142 :
143 : /*
144 : * Increase BOX b to include addon.
145 : */
146 : static void
147 5017810 : adjustBox(BOX *b, const BOX *addon)
148 : {
149 5017810 : if (float8_lt(b->high.x, addon->high.x))
150 4245948 : b->high.x = addon->high.x;
151 5017810 : if (float8_gt(b->low.x, addon->low.x))
152 136836 : b->low.x = addon->low.x;
153 5017810 : if (float8_lt(b->high.y, addon->high.y))
154 4243534 : b->high.y = addon->high.y;
155 5017810 : if (float8_gt(b->low.y, addon->low.y))
156 137536 : b->low.y = addon->low.y;
157 5017810 : }
158 :
159 : /*
160 : * The GiST Union method for boxes
161 : *
162 : * returns the minimal bounding box that encloses all the entries in entryvec
163 : */
164 : Datum
165 984530 : gist_box_union(PG_FUNCTION_ARGS)
166 : {
167 984530 : GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
168 984530 : int *sizep = (int *) PG_GETARG_POINTER(1);
169 : int numranges,
170 : i;
171 : BOX *cur,
172 : *pageunion;
173 :
174 984530 : numranges = entryvec->n;
175 984530 : pageunion = palloc_object(BOX);
176 984530 : cur = DatumGetBoxP(entryvec->vector[0].key);
177 984530 : memcpy(pageunion, cur, sizeof(BOX));
178 :
179 2443648 : for (i = 1; i < numranges; i++)
180 : {
181 1459118 : cur = DatumGetBoxP(entryvec->vector[i].key);
182 1459118 : adjustBox(pageunion, cur);
183 : }
184 984530 : *sizep = sizeof(BOX);
185 :
186 984530 : PG_RETURN_POINTER(pageunion);
187 : }
188 :
189 : /*
190 : * We store boxes as boxes in GiST indexes, so we do not need
191 : * compress, decompress, or fetch functions.
192 : */
193 :
194 : /*
195 : * The GiST Penalty method for boxes (also used for points)
196 : *
197 : * As in the R-tree paper, we use change in area as our penalty metric
198 : */
199 : Datum
200 40331712 : gist_box_penalty(PG_FUNCTION_ARGS)
201 : {
202 40331712 : GISTENTRY *origentry = (GISTENTRY *) PG_GETARG_POINTER(0);
203 40331712 : GISTENTRY *newentry = (GISTENTRY *) PG_GETARG_POINTER(1);
204 40331712 : float *result = (float *) PG_GETARG_POINTER(2);
205 40331712 : BOX *origbox = DatumGetBoxP(origentry->key);
206 40331712 : BOX *newbox = DatumGetBoxP(newentry->key);
207 :
208 40331712 : *result = (float) box_penalty(origbox, newbox);
209 40331712 : PG_RETURN_POINTER(result);
210 : }
211 :
212 : /*
213 : * Trivial split: half of entries will be placed on one page
214 : * and another half - to another
215 : */
216 : static void
217 30 : fallbackSplit(GistEntryVector *entryvec, GIST_SPLITVEC *v)
218 : {
219 : OffsetNumber i,
220 : maxoff;
221 30 : BOX *unionL = NULL,
222 30 : *unionR = NULL;
223 : int nbytes;
224 :
225 30 : maxoff = entryvec->n - 1;
226 :
227 30 : nbytes = (maxoff + 2) * sizeof(OffsetNumber);
228 30 : v->spl_left = (OffsetNumber *) palloc(nbytes);
229 30 : v->spl_right = (OffsetNumber *) palloc(nbytes);
230 30 : v->spl_nleft = v->spl_nright = 0;
231 :
232 11592 : for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
233 : {
234 11562 : BOX *cur = DatumGetBoxP(entryvec->vector[i].key);
235 :
236 11562 : if (i <= (maxoff - FirstOffsetNumber + 1) / 2)
237 : {
238 5778 : v->spl_left[v->spl_nleft] = i;
239 5778 : if (unionL == NULL)
240 : {
241 30 : unionL = palloc_object(BOX);
242 30 : *unionL = *cur;
243 : }
244 : else
245 5748 : adjustBox(unionL, cur);
246 :
247 5778 : v->spl_nleft++;
248 : }
249 : else
250 : {
251 5784 : v->spl_right[v->spl_nright] = i;
252 5784 : if (unionR == NULL)
253 : {
254 30 : unionR = palloc_object(BOX);
255 30 : *unionR = *cur;
256 : }
257 : else
258 5754 : adjustBox(unionR, cur);
259 :
260 5784 : v->spl_nright++;
261 : }
262 : }
263 :
264 30 : v->spl_ldatum = BoxPGetDatum(unionL);
265 30 : v->spl_rdatum = BoxPGetDatum(unionR);
266 30 : }
267 :
268 : /*
269 : * Represents information about an entry that can be placed to either group
270 : * without affecting overlap over selected axis ("common entry").
271 : */
272 : typedef struct
273 : {
274 : /* Index of entry in the initial array */
275 : int index;
276 : /* Delta between penalties of entry insertion into different groups */
277 : float8 delta;
278 : } CommonEntry;
279 :
280 : /*
281 : * Context for g_box_consider_split. Contains information about currently
282 : * selected split and some general information.
283 : */
284 : typedef struct
285 : {
286 : int entriesCount; /* total number of entries being split */
287 : BOX boundingBox; /* minimum bounding box across all entries */
288 :
289 : /* Information about currently selected split follows */
290 :
291 : bool first; /* true if no split was selected yet */
292 :
293 : float8 leftUpper; /* upper bound of left interval */
294 : float8 rightLower; /* lower bound of right interval */
295 :
296 : float4 ratio;
297 : float4 overlap;
298 : int dim; /* axis of this split */
299 : float8 range; /* width of general MBR projection to the
300 : * selected axis */
301 : } ConsiderSplitContext;
302 :
303 : /*
304 : * Interval represents projection of box to axis.
305 : */
306 : typedef struct
307 : {
308 : float8 lower,
309 : upper;
310 : } SplitInterval;
311 :
312 : /*
313 : * Interval comparison function by lower bound of the interval;
314 : */
315 : static int
316 8661982 : interval_cmp_lower(const void *i1, const void *i2)
317 : {
318 8661982 : float8 lower1 = ((const SplitInterval *) i1)->lower,
319 8661982 : lower2 = ((const SplitInterval *) i2)->lower;
320 :
321 8661982 : return float8_cmp_internal(lower1, lower2);
322 : }
323 :
324 : /*
325 : * Interval comparison function by upper bound of the interval;
326 : */
327 : static int
328 8655782 : interval_cmp_upper(const void *i1, const void *i2)
329 : {
330 8655782 : float8 upper1 = ((const SplitInterval *) i1)->upper,
331 8655782 : upper2 = ((const SplitInterval *) i2)->upper;
332 :
333 8655782 : return float8_cmp_internal(upper1, upper2);
334 : }
335 :
336 : /*
337 : * Replace negative (or NaN) value with zero.
338 : */
339 : static inline float
340 2717940 : non_negative(float val)
341 : {
342 2717940 : if (val >= 0.0f)
343 727830 : return val;
344 : else
345 1990110 : return 0.0f;
346 : }
347 :
348 : /*
349 : * Consider replacement of currently selected split with the better one.
350 : */
351 : static inline void
352 7070668 : g_box_consider_split(ConsiderSplitContext *context, int dimNum,
353 : float8 rightLower, int minLeftCount,
354 : float8 leftUpper, int maxLeftCount)
355 : {
356 : int leftCount,
357 : rightCount;
358 : float4 ratio,
359 : overlap;
360 : float8 range;
361 :
362 : /*
363 : * Calculate entries distribution ratio assuming most uniform distribution
364 : * of common entries.
365 : */
366 7070668 : if (minLeftCount >= (context->entriesCount + 1) / 2)
367 : {
368 3544786 : leftCount = minLeftCount;
369 : }
370 : else
371 : {
372 3525882 : if (maxLeftCount <= context->entriesCount / 2)
373 3525086 : leftCount = maxLeftCount;
374 : else
375 796 : leftCount = context->entriesCount / 2;
376 : }
377 7070668 : rightCount = context->entriesCount - leftCount;
378 :
379 : /*
380 : * Ratio of split - quotient between size of lesser group and total
381 : * entries count.
382 : */
383 7070668 : ratio = float4_div(Min(leftCount, rightCount), context->entriesCount);
384 :
385 7070668 : if (ratio > LIMIT_RATIO)
386 : {
387 2853460 : bool selectthis = false;
388 :
389 : /*
390 : * The ratio is acceptable, so compare current split with previously
391 : * selected one. Between splits of one dimension we search for minimal
392 : * overlap (allowing negative values) and minimal ration (between same
393 : * overlaps. We switch dimension if find less overlap (non-negative)
394 : * or less range with same overlap.
395 : */
396 2853460 : if (dimNum == 0)
397 1426500 : range = float8_mi(context->boundingBox.high.x,
398 : context->boundingBox.low.x);
399 : else
400 1426960 : range = float8_mi(context->boundingBox.high.y,
401 : context->boundingBox.low.y);
402 :
403 2853460 : overlap = float8_div(float8_mi(leftUpper, rightLower), range);
404 :
405 : /* If there is no previous selection, select this */
406 2853460 : if (context->first)
407 11114 : selectthis = true;
408 2842346 : else if (context->dim == dimNum)
409 : {
410 : /*
411 : * Within the same dimension, choose the new split if it has a
412 : * smaller overlap, or same overlap but better ratio.
413 : */
414 1484286 : if (overlap < context->overlap ||
415 1478974 : (overlap == context->overlap && ratio > context->ratio))
416 218046 : selectthis = true;
417 : }
418 : else
419 : {
420 : /*
421 : * Across dimensions, choose the new split if it has a smaller
422 : * *non-negative* overlap, or same *non-negative* overlap but
423 : * bigger range. This condition differs from the one described in
424 : * the article. On the datasets where leaf MBRs don't overlap
425 : * themselves, non-overlapping splits (i.e. splits which have zero
426 : * *non-negative* overlap) are frequently possible. In this case
427 : * splits tends to be along one dimension, because most distant
428 : * non-overlapping splits (i.e. having lowest negative overlap)
429 : * appears to be in the same dimension as in the previous split.
430 : * Therefore MBRs appear to be very prolonged along another
431 : * dimension, which leads to bad search performance. Using range
432 : * as the second split criteria makes MBRs more quadratic. Using
433 : * *non-negative* overlap instead of overlap as the first split
434 : * criteria gives to range criteria a chance to matter, because
435 : * non-overlapping splits are equivalent in this criteria.
436 : */
437 1358060 : if (non_negative(overlap) < non_negative(context->overlap) ||
438 1358970 : (range > context->range &&
439 910 : non_negative(overlap) <= non_negative(context->overlap)))
440 528 : selectthis = true;
441 : }
442 :
443 2853460 : if (selectthis)
444 : {
445 : /* save information about selected split */
446 229688 : context->first = false;
447 229688 : context->ratio = ratio;
448 229688 : context->range = range;
449 229688 : context->overlap = overlap;
450 229688 : context->rightLower = rightLower;
451 229688 : context->leftUpper = leftUpper;
452 229688 : context->dim = dimNum;
453 : }
454 : }
455 7070668 : }
456 :
457 : /*
458 : * Compare common entries by their deltas.
459 : */
460 : static int
461 0 : common_entry_cmp(const void *i1, const void *i2)
462 : {
463 0 : float8 delta1 = ((const CommonEntry *) i1)->delta,
464 0 : delta2 = ((const CommonEntry *) i2)->delta;
465 :
466 0 : return float8_cmp_internal(delta1, delta2);
467 : }
468 :
469 : /*
470 : * --------------------------------------------------------------------------
471 : * Double sorting split algorithm. This is used for both boxes and points.
472 : *
473 : * The algorithm finds split of boxes by considering splits along each axis.
474 : * Each entry is first projected as an interval on the X-axis, and different
475 : * ways to split the intervals into two groups are considered, trying to
476 : * minimize the overlap of the groups. Then the same is repeated for the
477 : * Y-axis, and the overall best split is chosen. The quality of a split is
478 : * determined by overlap along that axis and some other criteria (see
479 : * g_box_consider_split).
480 : *
481 : * After that, all the entries are divided into three groups:
482 : *
483 : * 1) Entries which should be placed to the left group
484 : * 2) Entries which should be placed to the right group
485 : * 3) "Common entries" which can be placed to any of groups without affecting
486 : * of overlap along selected axis.
487 : *
488 : * The common entries are distributed by minimizing penalty.
489 : *
490 : * For details see:
491 : * "A new double sorting-based node splitting algorithm for R-tree", A. Korotkov
492 : * http://syrcose.ispras.ru/2011/files/SYRCoSE2011_Proceedings.pdf#page=36
493 : * --------------------------------------------------------------------------
494 : */
495 : Datum
496 11144 : gist_box_picksplit(PG_FUNCTION_ARGS)
497 : {
498 11144 : GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
499 11144 : GIST_SPLITVEC *v = (GIST_SPLITVEC *) PG_GETARG_POINTER(1);
500 : OffsetNumber i,
501 : maxoff;
502 : ConsiderSplitContext context;
503 : BOX *box,
504 : *leftBox,
505 : *rightBox;
506 : int dim,
507 : commonEntriesCount;
508 : SplitInterval *intervalsLower,
509 : *intervalsUpper;
510 : CommonEntry *commonEntries;
511 : int nentries;
512 :
513 11144 : memset(&context, 0, sizeof(ConsiderSplitContext));
514 :
515 11144 : maxoff = entryvec->n - 1;
516 11144 : nentries = context.entriesCount = maxoff - FirstOffsetNumber + 1;
517 :
518 : /* Allocate arrays for intervals along axes */
519 11144 : intervalsLower = (SplitInterval *) palloc(nentries * sizeof(SplitInterval));
520 11144 : intervalsUpper = (SplitInterval *) palloc(nentries * sizeof(SplitInterval));
521 :
522 : /*
523 : * Calculate the overall minimum bounding box over all the entries.
524 : */
525 1807206 : for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
526 : {
527 1796062 : box = DatumGetBoxP(entryvec->vector[i].key);
528 1796062 : if (i == FirstOffsetNumber)
529 11144 : context.boundingBox = *box;
530 : else
531 1784918 : adjustBox(&context.boundingBox, box);
532 : }
533 :
534 : /*
535 : * Iterate over axes for optimal split searching.
536 : */
537 11144 : context.first = true; /* nothing selected yet */
538 33432 : for (dim = 0; dim < 2; dim++)
539 : {
540 : float8 leftUpper,
541 : rightLower;
542 : int i1,
543 : i2;
544 :
545 : /* Project each entry as an interval on the selected axis. */
546 3614412 : for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
547 : {
548 3592124 : box = DatumGetBoxP(entryvec->vector[i].key);
549 3592124 : if (dim == 0)
550 : {
551 1796062 : intervalsLower[i - FirstOffsetNumber].lower = box->low.x;
552 1796062 : intervalsLower[i - FirstOffsetNumber].upper = box->high.x;
553 : }
554 : else
555 : {
556 1796062 : intervalsLower[i - FirstOffsetNumber].lower = box->low.y;
557 1796062 : intervalsLower[i - FirstOffsetNumber].upper = box->high.y;
558 : }
559 : }
560 :
561 : /*
562 : * Make two arrays of intervals: one sorted by lower bound and another
563 : * sorted by upper bound.
564 : */
565 22288 : memcpy(intervalsUpper, intervalsLower,
566 : sizeof(SplitInterval) * nentries);
567 22288 : qsort(intervalsLower, nentries, sizeof(SplitInterval),
568 : interval_cmp_lower);
569 22288 : qsort(intervalsUpper, nentries, sizeof(SplitInterval),
570 : interval_cmp_upper);
571 :
572 : /*----
573 : * The goal is to form a left and right interval, so that every entry
574 : * interval is contained by either left or right interval (or both).
575 : *
576 : * For example, with the intervals (0,1), (1,3), (2,3), (2,4):
577 : *
578 : * 0 1 2 3 4
579 : * +-+
580 : * +---+
581 : * +-+
582 : * +---+
583 : *
584 : * The left and right intervals are of the form (0,a) and (b,4).
585 : * We first consider splits where b is the lower bound of an entry.
586 : * We iterate through all entries, and for each b, calculate the
587 : * smallest possible a. Then we consider splits where a is the
588 : * upper bound of an entry, and for each a, calculate the greatest
589 : * possible b.
590 : *
591 : * In the above example, the first loop would consider splits:
592 : * b=0: (0,1)-(0,4)
593 : * b=1: (0,1)-(1,4)
594 : * b=2: (0,3)-(2,4)
595 : *
596 : * And the second loop:
597 : * a=1: (0,1)-(1,4)
598 : * a=3: (0,3)-(2,4)
599 : * a=4: (0,4)-(2,4)
600 : */
601 :
602 : /*
603 : * Iterate over lower bound of right group, finding smallest possible
604 : * upper bound of left group.
605 : */
606 22288 : i1 = 0;
607 22288 : i2 = 0;
608 22288 : rightLower = intervalsLower[i1].lower;
609 22288 : leftUpper = intervalsUpper[i2].lower;
610 : while (true)
611 : {
612 : /*
613 : * Find next lower bound of right group.
614 : */
615 14277404 : while (i1 < nentries &&
616 7127558 : float8_eq(rightLower, intervalsLower[i1].lower))
617 : {
618 3592124 : if (float8_lt(leftUpper, intervalsLower[i1].upper))
619 3500632 : leftUpper = intervalsLower[i1].upper;
620 3592124 : i1++;
621 : }
622 3557722 : if (i1 >= nentries)
623 22288 : break;
624 3535434 : rightLower = intervalsLower[i1].lower;
625 :
626 : /*
627 : * Find count of intervals which anyway should be placed to the
628 : * left group.
629 : */
630 14168964 : while (i2 < nentries &&
631 7084338 : float8_le(intervalsUpper[i2].upper, leftUpper))
632 3549192 : i2++;
633 :
634 : /*
635 : * Consider found split.
636 : */
637 3535434 : g_box_consider_split(&context, dim, rightLower, i1, leftUpper, i2);
638 : }
639 :
640 : /*
641 : * Iterate over upper bound of left group finding greatest possible
642 : * lower bound of right group.
643 : */
644 22288 : i1 = nentries - 1;
645 22288 : i2 = nentries - 1;
646 22288 : rightLower = intervalsLower[i1].upper;
647 22288 : leftUpper = intervalsUpper[i2].upper;
648 : while (true)
649 : {
650 : /*
651 : * Find next upper bound of left group.
652 : */
653 7149646 : while (i2 >= 0 && float8_eq(leftUpper, intervalsUpper[i2].upper))
654 : {
655 3592124 : if (float8_gt(rightLower, intervalsUpper[i2].lower))
656 3500480 : rightLower = intervalsUpper[i2].lower;
657 3592124 : i2--;
658 : }
659 3557522 : if (i2 < 0)
660 22288 : break;
661 3535234 : leftUpper = intervalsUpper[i2].upper;
662 :
663 : /*
664 : * Find count of intervals which anyway should be placed to the
665 : * right group.
666 : */
667 7081932 : while (i1 >= 0 && float8_ge(intervalsLower[i1].lower, rightLower))
668 3546698 : i1--;
669 :
670 : /*
671 : * Consider found split.
672 : */
673 3535234 : g_box_consider_split(&context, dim,
674 : rightLower, i1 + 1, leftUpper, i2 + 1);
675 : }
676 : }
677 :
678 : /*
679 : * If we failed to find any acceptable splits, use trivial split.
680 : */
681 11144 : if (context.first)
682 : {
683 30 : fallbackSplit(entryvec, v);
684 30 : PG_RETURN_POINTER(v);
685 : }
686 :
687 : /*
688 : * Ok, we have now selected the split across one axis.
689 : *
690 : * While considering the splits, we already determined that there will be
691 : * enough entries in both groups to reach the desired ratio, but we did
692 : * not memorize which entries go to which group. So determine that now.
693 : */
694 :
695 : /* Allocate vectors for results */
696 11114 : v->spl_left = (OffsetNumber *) palloc(nentries * sizeof(OffsetNumber));
697 11114 : v->spl_right = (OffsetNumber *) palloc(nentries * sizeof(OffsetNumber));
698 11114 : v->spl_nleft = 0;
699 11114 : v->spl_nright = 0;
700 :
701 : /* Allocate bounding boxes of left and right groups */
702 11114 : leftBox = palloc0_object(BOX);
703 11114 : rightBox = palloc0_object(BOX);
704 :
705 : /*
706 : * Allocate an array for "common entries" - entries which can be placed to
707 : * either group without affecting overlap along selected axis.
708 : */
709 11114 : commonEntriesCount = 0;
710 11114 : commonEntries = (CommonEntry *) palloc(nentries * sizeof(CommonEntry));
711 :
712 : /* Helper macros to place an entry in the left or right group */
713 : #define PLACE_LEFT(box, off) \
714 : do { \
715 : if (v->spl_nleft > 0) \
716 : adjustBox(leftBox, box); \
717 : else \
718 : *leftBox = *(box); \
719 : v->spl_left[v->spl_nleft++] = off; \
720 : } while(0)
721 :
722 : #define PLACE_RIGHT(box, off) \
723 : do { \
724 : if (v->spl_nright > 0) \
725 : adjustBox(rightBox, box); \
726 : else \
727 : *rightBox = *(box); \
728 : v->spl_right[v->spl_nright++] = off; \
729 : } while(0)
730 :
731 : /*
732 : * Distribute entries which can be distributed unambiguously, and collect
733 : * common entries.
734 : */
735 1795614 : for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
736 : {
737 : float8 lower,
738 : upper;
739 :
740 : /*
741 : * Get upper and lower bounds along selected axis.
742 : */
743 1784500 : box = DatumGetBoxP(entryvec->vector[i].key);
744 1784500 : if (context.dim == 0)
745 : {
746 1696324 : lower = box->low.x;
747 1696324 : upper = box->high.x;
748 : }
749 : else
750 : {
751 88176 : lower = box->low.y;
752 88176 : upper = box->high.y;
753 : }
754 :
755 1784500 : if (float8_le(upper, context.leftUpper))
756 : {
757 : /* Fits to the left group */
758 823118 : if (float8_ge(lower, context.rightLower))
759 : {
760 : /* Fits also to the right group, so "common entry" */
761 0 : commonEntries[commonEntriesCount++].index = i;
762 : }
763 : else
764 : {
765 : /* Doesn't fit to the right group, so join to the left group */
766 823118 : PLACE_LEFT(box, i);
767 : }
768 : }
769 : else
770 : {
771 : /*
772 : * Each entry should fit on either left or right group. Since this
773 : * entry didn't fit on the left group, it better fit in the right
774 : * group.
775 : */
776 : Assert(float8_ge(lower, context.rightLower));
777 :
778 : /* Doesn't fit to the left group, so join to the right group */
779 961382 : PLACE_RIGHT(box, i);
780 : }
781 : }
782 :
783 : /*
784 : * Distribute "common entries", if any.
785 : */
786 11114 : if (commonEntriesCount > 0)
787 : {
788 : /*
789 : * Calculate minimum number of entries that must be placed in both
790 : * groups, to reach LIMIT_RATIO.
791 : */
792 0 : int m = ceil(LIMIT_RATIO * nentries);
793 :
794 : /*
795 : * Calculate delta between penalties of join "common entries" to
796 : * different groups.
797 : */
798 0 : for (i = 0; i < commonEntriesCount; i++)
799 : {
800 0 : box = DatumGetBoxP(entryvec->vector[commonEntries[i].index].key);
801 0 : commonEntries[i].delta = fabs(float8_mi(box_penalty(leftBox, box),
802 : box_penalty(rightBox, box)));
803 : }
804 :
805 : /*
806 : * Sort "common entries" by calculated deltas in order to distribute
807 : * the most ambiguous entries first.
808 : */
809 0 : qsort(commonEntries, commonEntriesCount, sizeof(CommonEntry), common_entry_cmp);
810 :
811 : /*
812 : * Distribute "common entries" between groups.
813 : */
814 0 : for (i = 0; i < commonEntriesCount; i++)
815 : {
816 0 : box = DatumGetBoxP(entryvec->vector[commonEntries[i].index].key);
817 :
818 : /*
819 : * Check if we have to place this entry in either group to achieve
820 : * LIMIT_RATIO.
821 : */
822 0 : if (v->spl_nleft + (commonEntriesCount - i) <= m)
823 0 : PLACE_LEFT(box, commonEntries[i].index);
824 0 : else if (v->spl_nright + (commonEntriesCount - i) <= m)
825 0 : PLACE_RIGHT(box, commonEntries[i].index);
826 : else
827 : {
828 : /* Otherwise select the group by minimal penalty */
829 0 : if (box_penalty(leftBox, box) < box_penalty(rightBox, box))
830 0 : PLACE_LEFT(box, commonEntries[i].index);
831 : else
832 0 : PLACE_RIGHT(box, commonEntries[i].index);
833 : }
834 : }
835 : }
836 :
837 11114 : v->spl_ldatum = PointerGetDatum(leftBox);
838 11114 : v->spl_rdatum = PointerGetDatum(rightBox);
839 11114 : PG_RETURN_POINTER(v);
840 : }
841 :
842 : /*
843 : * Equality method
844 : *
845 : * This is used for boxes, points, circles, and polygons, all of which store
846 : * boxes as GiST index entries.
847 : *
848 : * Returns true only when boxes are exactly the same. We can't use fuzzy
849 : * comparisons here without breaking index consistency; therefore, this isn't
850 : * equivalent to box_same().
851 : */
852 : Datum
853 793952 : gist_box_same(PG_FUNCTION_ARGS)
854 : {
855 793952 : BOX *b1 = PG_GETARG_BOX_P(0);
856 793952 : BOX *b2 = PG_GETARG_BOX_P(1);
857 793952 : bool *result = (bool *) PG_GETARG_POINTER(2);
858 :
859 793952 : if (b1 && b2)
860 1542238 : *result = (float8_eq(b1->low.x, b2->low.x) &&
861 1495470 : float8_eq(b1->low.y, b2->low.y) &&
862 2289422 : float8_eq(b1->high.x, b2->high.x) &&
863 191680 : float8_eq(b1->high.y, b2->high.y));
864 : else
865 0 : *result = (b1 == NULL && b2 == NULL);
866 793952 : PG_RETURN_POINTER(result);
867 : }
868 :
869 : /*
870 : * Leaf-level consistency for boxes: just apply the query operator
871 : */
872 : static bool
873 4692 : gist_box_leaf_consistent(BOX *key, BOX *query, StrategyNumber strategy)
874 : {
875 : bool retval;
876 :
877 4692 : switch (strategy)
878 : {
879 0 : case RTLeftStrategyNumber:
880 0 : retval = DatumGetBool(DirectFunctionCall2(box_left,
881 : PointerGetDatum(key),
882 : PointerGetDatum(query)));
883 0 : break;
884 0 : case RTOverLeftStrategyNumber:
885 0 : retval = DatumGetBool(DirectFunctionCall2(box_overleft,
886 : PointerGetDatum(key),
887 : PointerGetDatum(query)));
888 0 : break;
889 1626 : case RTOverlapStrategyNumber:
890 1626 : retval = DatumGetBool(DirectFunctionCall2(box_overlap,
891 : PointerGetDatum(key),
892 : PointerGetDatum(query)));
893 1626 : break;
894 0 : case RTOverRightStrategyNumber:
895 0 : retval = DatumGetBool(DirectFunctionCall2(box_overright,
896 : PointerGetDatum(key),
897 : PointerGetDatum(query)));
898 0 : break;
899 0 : case RTRightStrategyNumber:
900 0 : retval = DatumGetBool(DirectFunctionCall2(box_right,
901 : PointerGetDatum(key),
902 : PointerGetDatum(query)));
903 0 : break;
904 0 : case RTSameStrategyNumber:
905 0 : retval = DatumGetBool(DirectFunctionCall2(box_same,
906 : PointerGetDatum(key),
907 : PointerGetDatum(query)));
908 0 : break;
909 0 : case RTContainsStrategyNumber:
910 0 : retval = DatumGetBool(DirectFunctionCall2(box_contain,
911 : PointerGetDatum(key),
912 : PointerGetDatum(query)));
913 0 : break;
914 3066 : case RTContainedByStrategyNumber:
915 3066 : retval = DatumGetBool(DirectFunctionCall2(box_contained,
916 : PointerGetDatum(key),
917 : PointerGetDatum(query)));
918 3066 : break;
919 0 : case RTOverBelowStrategyNumber:
920 0 : retval = DatumGetBool(DirectFunctionCall2(box_overbelow,
921 : PointerGetDatum(key),
922 : PointerGetDatum(query)));
923 0 : break;
924 0 : case RTBelowStrategyNumber:
925 0 : retval = DatumGetBool(DirectFunctionCall2(box_below,
926 : PointerGetDatum(key),
927 : PointerGetDatum(query)));
928 0 : break;
929 0 : case RTAboveStrategyNumber:
930 0 : retval = DatumGetBool(DirectFunctionCall2(box_above,
931 : PointerGetDatum(key),
932 : PointerGetDatum(query)));
933 0 : break;
934 0 : case RTOverAboveStrategyNumber:
935 0 : retval = DatumGetBool(DirectFunctionCall2(box_overabove,
936 : PointerGetDatum(key),
937 : PointerGetDatum(query)));
938 0 : break;
939 0 : default:
940 0 : elog(ERROR, "unrecognized strategy number: %d", strategy);
941 : retval = false; /* keep compiler quiet */
942 : break;
943 : }
944 4692 : return retval;
945 : }
946 :
947 : /*****************************************
948 : * Common rtree functions (for boxes, polygons, and circles)
949 : *****************************************/
950 :
951 : /*
952 : * Internal-page consistency for all these types
953 : *
954 : * We can use the same function since all types use bounding boxes as the
955 : * internal-page representation.
956 : */
957 : static bool
958 6360 : rtree_internal_consistent(BOX *key, BOX *query, StrategyNumber strategy)
959 : {
960 : bool retval;
961 :
962 6360 : switch (strategy)
963 : {
964 0 : case RTLeftStrategyNumber:
965 0 : retval = !DatumGetBool(DirectFunctionCall2(box_overright,
966 : PointerGetDatum(key),
967 : PointerGetDatum(query)));
968 0 : break;
969 0 : case RTOverLeftStrategyNumber:
970 0 : retval = !DatumGetBool(DirectFunctionCall2(box_right,
971 : PointerGetDatum(key),
972 : PointerGetDatum(query)));
973 0 : break;
974 2610 : case RTOverlapStrategyNumber:
975 2610 : retval = DatumGetBool(DirectFunctionCall2(box_overlap,
976 : PointerGetDatum(key),
977 : PointerGetDatum(query)));
978 2610 : break;
979 0 : case RTOverRightStrategyNumber:
980 0 : retval = !DatumGetBool(DirectFunctionCall2(box_left,
981 : PointerGetDatum(key),
982 : PointerGetDatum(query)));
983 0 : break;
984 0 : case RTRightStrategyNumber:
985 0 : retval = !DatumGetBool(DirectFunctionCall2(box_overleft,
986 : PointerGetDatum(key),
987 : PointerGetDatum(query)));
988 0 : break;
989 600 : case RTSameStrategyNumber:
990 : case RTContainsStrategyNumber:
991 600 : retval = DatumGetBool(DirectFunctionCall2(box_contain,
992 : PointerGetDatum(key),
993 : PointerGetDatum(query)));
994 600 : break;
995 3150 : case RTContainedByStrategyNumber:
996 3150 : retval = DatumGetBool(DirectFunctionCall2(box_overlap,
997 : PointerGetDatum(key),
998 : PointerGetDatum(query)));
999 3150 : break;
1000 0 : case RTOverBelowStrategyNumber:
1001 0 : retval = !DatumGetBool(DirectFunctionCall2(box_above,
1002 : PointerGetDatum(key),
1003 : PointerGetDatum(query)));
1004 0 : break;
1005 0 : case RTBelowStrategyNumber:
1006 0 : retval = !DatumGetBool(DirectFunctionCall2(box_overabove,
1007 : PointerGetDatum(key),
1008 : PointerGetDatum(query)));
1009 0 : break;
1010 0 : case RTAboveStrategyNumber:
1011 0 : retval = !DatumGetBool(DirectFunctionCall2(box_overbelow,
1012 : PointerGetDatum(key),
1013 : PointerGetDatum(query)));
1014 0 : break;
1015 0 : case RTOverAboveStrategyNumber:
1016 0 : retval = !DatumGetBool(DirectFunctionCall2(box_below,
1017 : PointerGetDatum(key),
1018 : PointerGetDatum(query)));
1019 0 : break;
1020 0 : default:
1021 0 : elog(ERROR, "unrecognized strategy number: %d", strategy);
1022 : retval = false; /* keep compiler quiet */
1023 : break;
1024 : }
1025 6360 : return retval;
1026 : }
1027 :
1028 : /**************************************************
1029 : * Polygon ops
1030 : **************************************************/
1031 :
1032 : /*
1033 : * GiST compress for polygons: represent a polygon by its bounding box
1034 : */
1035 : Datum
1036 39534 : gist_poly_compress(PG_FUNCTION_ARGS)
1037 : {
1038 39534 : GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
1039 : GISTENTRY *retval;
1040 :
1041 39534 : if (entry->leafkey)
1042 : {
1043 37266 : POLYGON *in = DatumGetPolygonP(entry->key);
1044 : BOX *r;
1045 :
1046 37266 : r = palloc_object(BOX);
1047 37266 : memcpy(r, &(in->boundbox), sizeof(BOX));
1048 :
1049 37266 : retval = palloc_object(GISTENTRY);
1050 37266 : gistentryinit(*retval, PointerGetDatum(r),
1051 : entry->rel, entry->page,
1052 : entry->offset, false);
1053 : }
1054 : else
1055 2268 : retval = entry;
1056 39534 : PG_RETURN_POINTER(retval);
1057 : }
1058 :
1059 : /*
1060 : * The GiST Consistent method for polygons
1061 : */
1062 : Datum
1063 786 : gist_poly_consistent(PG_FUNCTION_ARGS)
1064 : {
1065 786 : GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
1066 786 : POLYGON *query = PG_GETARG_POLYGON_P(1);
1067 786 : StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2);
1068 : #ifdef NOT_USED
1069 : Oid subtype = PG_GETARG_OID(3);
1070 : #endif
1071 786 : bool *recheck = (bool *) PG_GETARG_POINTER(4);
1072 : bool result;
1073 :
1074 : /* All cases served by this function are inexact */
1075 786 : *recheck = true;
1076 :
1077 786 : if (DatumGetBoxP(entry->key) == NULL || query == NULL)
1078 0 : PG_RETURN_BOOL(false);
1079 :
1080 : /*
1081 : * Since the operators require recheck anyway, we can just use
1082 : * rtree_internal_consistent even at leaf nodes. (This works in part
1083 : * because the index entries are bounding boxes not polygons.)
1084 : */
1085 786 : result = rtree_internal_consistent(DatumGetBoxP(entry->key),
1086 : &(query->boundbox), strategy);
1087 :
1088 : /* Avoid memory leak if supplied poly is toasted */
1089 786 : PG_FREE_IF_COPY(query, 1);
1090 :
1091 786 : PG_RETURN_BOOL(result);
1092 : }
1093 :
1094 : /**************************************************
1095 : * Circle ops
1096 : **************************************************/
1097 :
1098 : /*
1099 : * GiST compress for circles: represent a circle by its bounding box
1100 : */
1101 : Datum
1102 347736 : gist_circle_compress(PG_FUNCTION_ARGS)
1103 : {
1104 347736 : GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
1105 : GISTENTRY *retval;
1106 :
1107 347736 : if (entry->leafkey)
1108 : {
1109 157404 : CIRCLE *in = DatumGetCircleP(entry->key);
1110 : BOX *r;
1111 :
1112 157404 : r = palloc_object(BOX);
1113 157404 : r->high.x = float8_pl(in->center.x, in->radius);
1114 157404 : r->low.x = float8_mi(in->center.x, in->radius);
1115 157404 : r->high.y = float8_pl(in->center.y, in->radius);
1116 157404 : r->low.y = float8_mi(in->center.y, in->radius);
1117 :
1118 157404 : retval = palloc_object(GISTENTRY);
1119 157404 : gistentryinit(*retval, PointerGetDatum(r),
1120 : entry->rel, entry->page,
1121 : entry->offset, false);
1122 : }
1123 : else
1124 190332 : retval = entry;
1125 347736 : PG_RETURN_POINTER(retval);
1126 : }
1127 :
1128 : /*
1129 : * The GiST Consistent method for circles
1130 : */
1131 : Datum
1132 2100 : gist_circle_consistent(PG_FUNCTION_ARGS)
1133 : {
1134 2100 : GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
1135 2100 : CIRCLE *query = PG_GETARG_CIRCLE_P(1);
1136 2100 : StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2);
1137 : #ifdef NOT_USED
1138 : Oid subtype = PG_GETARG_OID(3);
1139 : #endif
1140 2100 : bool *recheck = (bool *) PG_GETARG_POINTER(4);
1141 : BOX bbox;
1142 : bool result;
1143 :
1144 : /* All cases served by this function are inexact */
1145 2100 : *recheck = true;
1146 :
1147 2100 : if (DatumGetBoxP(entry->key) == NULL || query == NULL)
1148 0 : PG_RETURN_BOOL(false);
1149 :
1150 : /*
1151 : * Since the operators require recheck anyway, we can just use
1152 : * rtree_internal_consistent even at leaf nodes. (This works in part
1153 : * because the index entries are bounding boxes not circles.)
1154 : */
1155 2100 : bbox.high.x = float8_pl(query->center.x, query->radius);
1156 2100 : bbox.low.x = float8_mi(query->center.x, query->radius);
1157 2100 : bbox.high.y = float8_pl(query->center.y, query->radius);
1158 2100 : bbox.low.y = float8_mi(query->center.y, query->radius);
1159 :
1160 2100 : result = rtree_internal_consistent(DatumGetBoxP(entry->key),
1161 : &bbox, strategy);
1162 :
1163 2100 : PG_RETURN_BOOL(result);
1164 : }
1165 :
1166 : /**************************************************
1167 : * Point ops
1168 : **************************************************/
1169 :
1170 : Datum
1171 1032972 : gist_point_compress(PG_FUNCTION_ARGS)
1172 : {
1173 1032972 : GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
1174 :
1175 1032972 : if (entry->leafkey) /* Point, actually */
1176 : {
1177 589374 : BOX *box = palloc_object(BOX);
1178 589374 : Point *point = DatumGetPointP(entry->key);
1179 589374 : GISTENTRY *retval = palloc_object(GISTENTRY);
1180 :
1181 589374 : box->high = box->low = *point;
1182 :
1183 589374 : gistentryinit(*retval, BoxPGetDatum(box),
1184 : entry->rel, entry->page, entry->offset, false);
1185 :
1186 589374 : PG_RETURN_POINTER(retval);
1187 : }
1188 :
1189 443598 : PG_RETURN_POINTER(entry);
1190 : }
1191 :
1192 : /*
1193 : * GiST Fetch method for point
1194 : *
1195 : * Get point coordinates from its bounding box coordinates and form new
1196 : * gistentry.
1197 : */
1198 : Datum
1199 100976 : gist_point_fetch(PG_FUNCTION_ARGS)
1200 : {
1201 100976 : GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
1202 100976 : BOX *in = DatumGetBoxP(entry->key);
1203 : Point *r;
1204 : GISTENTRY *retval;
1205 :
1206 100976 : retval = palloc_object(GISTENTRY);
1207 :
1208 100976 : r = palloc_object(Point);
1209 100976 : r->x = in->high.x;
1210 100976 : r->y = in->high.y;
1211 100976 : gistentryinit(*retval, PointerGetDatum(r),
1212 : entry->rel, entry->page,
1213 : entry->offset, false);
1214 :
1215 100976 : PG_RETURN_POINTER(retval);
1216 : }
1217 :
1218 :
1219 : #define point_point_distance(p1,p2) \
1220 : DatumGetFloat8(DirectFunctionCall2(point_distance, \
1221 : PointPGetDatum(p1), PointPGetDatum(p2)))
1222 :
1223 : static float8
1224 11160 : computeDistance(bool isLeaf, BOX *box, Point *point)
1225 : {
1226 11160 : float8 result = 0.0;
1227 :
1228 11160 : if (isLeaf)
1229 : {
1230 : /* simple point to point distance */
1231 414 : result = point_point_distance(point, &box->low);
1232 : }
1233 10746 : else if (point->x <= box->high.x && point->x >= box->low.x &&
1234 492 : point->y <= box->high.y && point->y >= box->low.y)
1235 : {
1236 : /* point inside the box */
1237 330 : result = 0.0;
1238 : }
1239 10416 : else if (point->x <= box->high.x && point->x >= box->low.x)
1240 : {
1241 : /* point is over or below box */
1242 : Assert(box->low.y <= box->high.y);
1243 162 : if (point->y > box->high.y)
1244 12 : result = float8_mi(point->y, box->high.y);
1245 150 : else if (point->y < box->low.y)
1246 150 : result = float8_mi(box->low.y, point->y);
1247 : else
1248 0 : elog(ERROR, "inconsistent point values");
1249 : }
1250 10254 : else if (point->y <= box->high.y && point->y >= box->low.y)
1251 : {
1252 : /* point is to left or right of box */
1253 : Assert(box->low.x <= box->high.x);
1254 54 : if (point->x > box->high.x)
1255 0 : result = float8_mi(point->x, box->high.x);
1256 54 : else if (point->x < box->low.x)
1257 54 : result = float8_mi(box->low.x, point->x);
1258 : else
1259 0 : elog(ERROR, "inconsistent point values");
1260 : }
1261 : else
1262 : {
1263 : /* closest point will be a vertex */
1264 : Point p;
1265 : float8 subresult;
1266 :
1267 10200 : result = point_point_distance(point, &box->low);
1268 :
1269 10200 : subresult = point_point_distance(point, &box->high);
1270 10200 : if (result > subresult)
1271 0 : result = subresult;
1272 :
1273 10200 : p.x = box->low.x;
1274 10200 : p.y = box->high.y;
1275 10200 : subresult = point_point_distance(point, &p);
1276 10200 : if (result > subresult)
1277 18 : result = subresult;
1278 :
1279 10200 : p.x = box->high.x;
1280 10200 : p.y = box->low.y;
1281 10200 : subresult = point_point_distance(point, &p);
1282 10200 : if (result > subresult)
1283 12 : result = subresult;
1284 : }
1285 :
1286 11160 : return result;
1287 : }
1288 :
1289 : static bool
1290 55162 : gist_point_consistent_internal(StrategyNumber strategy,
1291 : bool isLeaf, BOX *key, Point *query)
1292 : {
1293 55162 : bool result = false;
1294 :
1295 55162 : switch (strategy)
1296 : {
1297 19500 : case RTLeftStrategyNumber:
1298 19500 : result = FPlt(key->low.x, query->x);
1299 19500 : break;
1300 28828 : case RTRightStrategyNumber:
1301 28828 : result = FPgt(key->high.x, query->x);
1302 28828 : break;
1303 60 : case RTAboveStrategyNumber:
1304 60 : result = FPgt(key->high.y, query->y);
1305 60 : break;
1306 60 : case RTBelowStrategyNumber:
1307 60 : result = FPlt(key->low.y, query->y);
1308 60 : break;
1309 6714 : case RTSameStrategyNumber:
1310 6714 : if (isLeaf)
1311 : {
1312 : /* key.high must equal key.low, so we can disregard it */
1313 12654 : result = (FPeq(key->low.x, query->x) &&
1314 6024 : FPeq(key->low.y, query->y));
1315 : }
1316 : else
1317 : {
1318 216 : result = (FPle(query->x, key->high.x) &&
1319 96 : FPge(query->x, key->low.x) &&
1320 180 : FPle(query->y, key->high.y) &&
1321 48 : FPge(query->y, key->low.y));
1322 : }
1323 6714 : break;
1324 0 : default:
1325 0 : elog(ERROR, "unrecognized strategy number: %d", strategy);
1326 : result = false; /* keep compiler quiet */
1327 : break;
1328 : }
1329 :
1330 55162 : return result;
1331 : }
1332 :
1333 : #define GeoStrategyNumberOffset 20
1334 : #define PointStrategyNumberGroup 0
1335 : #define BoxStrategyNumberGroup 1
1336 : #define PolygonStrategyNumberGroup 2
1337 : #define CircleStrategyNumberGroup 3
1338 :
1339 : Datum
1340 65998 : gist_point_consistent(PG_FUNCTION_ARGS)
1341 : {
1342 65998 : GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
1343 65998 : StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2);
1344 65998 : bool *recheck = (bool *) PG_GETARG_POINTER(4);
1345 : bool result;
1346 : StrategyNumber strategyGroup;
1347 :
1348 : /*
1349 : * We have to remap these strategy numbers to get this klugy
1350 : * classification logic to work.
1351 : */
1352 65998 : if (strategy == RTOldBelowStrategyNumber)
1353 0 : strategy = RTBelowStrategyNumber;
1354 65998 : else if (strategy == RTOldAboveStrategyNumber)
1355 0 : strategy = RTAboveStrategyNumber;
1356 :
1357 65998 : strategyGroup = strategy / GeoStrategyNumberOffset;
1358 65998 : switch (strategyGroup)
1359 : {
1360 55162 : case PointStrategyNumberGroup:
1361 55162 : result = gist_point_consistent_internal(strategy % GeoStrategyNumberOffset,
1362 55162 : GIST_LEAF(entry),
1363 : DatumGetBoxP(entry->key),
1364 : PG_GETARG_POINT_P(1));
1365 55162 : *recheck = false;
1366 55162 : break;
1367 10716 : case BoxStrategyNumberGroup:
1368 : {
1369 : /*
1370 : * The only operator in this group is point <@ box (on_pb), so
1371 : * we needn't examine strategy again.
1372 : *
1373 : * For historical reasons, on_pb uses exact rather than fuzzy
1374 : * comparisons. We could use box_overlap when at an internal
1375 : * page, but that would lead to possibly visiting child pages
1376 : * uselessly, because box_overlap uses fuzzy comparisons.
1377 : * Instead we write a non-fuzzy overlap test. The same code
1378 : * will also serve for leaf-page tests, since leaf keys have
1379 : * high == low.
1380 : */
1381 : BOX *query,
1382 : *key;
1383 :
1384 10716 : query = PG_GETARG_BOX_P(1);
1385 10716 : key = DatumGetBoxP(entry->key);
1386 :
1387 31272 : result = (key->high.x >= query->low.x &&
1388 9840 : key->low.x <= query->high.x &&
1389 21234 : key->high.y >= query->low.y &&
1390 678 : key->low.y <= query->high.y);
1391 10716 : *recheck = false;
1392 : }
1393 10716 : break;
1394 60 : case PolygonStrategyNumberGroup:
1395 : {
1396 60 : POLYGON *query = PG_GETARG_POLYGON_P(1);
1397 :
1398 60 : result = DatumGetBool(DirectFunctionCall5(gist_poly_consistent,
1399 : PointerGetDatum(entry),
1400 : PolygonPGetDatum(query),
1401 : Int16GetDatum(RTOverlapStrategyNumber),
1402 : 0, PointerGetDatum(recheck)));
1403 :
1404 60 : if (GIST_LEAF(entry) && result)
1405 : {
1406 : /*
1407 : * We are on leaf page and quick check shows overlapping
1408 : * of polygon's bounding box and point
1409 : */
1410 24 : BOX *box = DatumGetBoxP(entry->key);
1411 :
1412 : Assert(box->high.x == box->low.x
1413 : && box->high.y == box->low.y);
1414 24 : result = DatumGetBool(DirectFunctionCall2(poly_contain_pt,
1415 : PolygonPGetDatum(query),
1416 : PointPGetDatum(&box->high)));
1417 24 : *recheck = false;
1418 : }
1419 : }
1420 60 : break;
1421 60 : case CircleStrategyNumberGroup:
1422 : {
1423 60 : CIRCLE *query = PG_GETARG_CIRCLE_P(1);
1424 :
1425 60 : result = DatumGetBool(DirectFunctionCall5(gist_circle_consistent,
1426 : PointerGetDatum(entry),
1427 : CirclePGetDatum(query),
1428 : Int16GetDatum(RTOverlapStrategyNumber),
1429 : 0, PointerGetDatum(recheck)));
1430 :
1431 60 : if (GIST_LEAF(entry) && result)
1432 : {
1433 : /*
1434 : * We are on leaf page and quick check shows overlapping
1435 : * of polygon's bounding box and point
1436 : */
1437 24 : BOX *box = DatumGetBoxP(entry->key);
1438 :
1439 : Assert(box->high.x == box->low.x
1440 : && box->high.y == box->low.y);
1441 24 : result = DatumGetBool(DirectFunctionCall2(circle_contain_pt,
1442 : CirclePGetDatum(query),
1443 : PointPGetDatum(&box->high)));
1444 24 : *recheck = false;
1445 : }
1446 : }
1447 60 : break;
1448 0 : default:
1449 0 : elog(ERROR, "unrecognized strategy number: %d", strategy);
1450 : result = false; /* keep compiler quiet */
1451 : break;
1452 : }
1453 :
1454 65998 : PG_RETURN_BOOL(result);
1455 : }
1456 :
1457 : Datum
1458 444 : gist_point_distance(PG_FUNCTION_ARGS)
1459 : {
1460 444 : GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
1461 444 : StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2);
1462 : float8 distance;
1463 444 : StrategyNumber strategyGroup = strategy / GeoStrategyNumberOffset;
1464 :
1465 444 : switch (strategyGroup)
1466 : {
1467 444 : case PointStrategyNumberGroup:
1468 444 : distance = computeDistance(GIST_LEAF(entry),
1469 : DatumGetBoxP(entry->key),
1470 : PG_GETARG_POINT_P(1));
1471 444 : break;
1472 0 : default:
1473 0 : elog(ERROR, "unrecognized strategy number: %d", strategy);
1474 : distance = 0.0; /* keep compiler quiet */
1475 : break;
1476 : }
1477 :
1478 444 : PG_RETURN_FLOAT8(distance);
1479 : }
1480 :
1481 : static float8
1482 10716 : gist_bbox_distance(GISTENTRY *entry, Datum query, StrategyNumber strategy)
1483 : {
1484 : float8 distance;
1485 10716 : StrategyNumber strategyGroup = strategy / GeoStrategyNumberOffset;
1486 :
1487 10716 : switch (strategyGroup)
1488 : {
1489 10716 : case PointStrategyNumberGroup:
1490 10716 : distance = computeDistance(false,
1491 : DatumGetBoxP(entry->key),
1492 : DatumGetPointP(query));
1493 10716 : break;
1494 0 : default:
1495 0 : elog(ERROR, "unrecognized strategy number: %d", strategy);
1496 : distance = 0.0; /* keep compiler quiet */
1497 : }
1498 :
1499 10716 : return distance;
1500 : }
1501 :
1502 : Datum
1503 264 : gist_box_distance(PG_FUNCTION_ARGS)
1504 : {
1505 264 : GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
1506 264 : Datum query = PG_GETARG_DATUM(1);
1507 264 : StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2);
1508 : #ifdef NOT_USED
1509 : Oid subtype = PG_GETARG_OID(3);
1510 : bool *recheck = (bool *) PG_GETARG_POINTER(4);
1511 : #endif
1512 : float8 distance;
1513 :
1514 264 : distance = gist_bbox_distance(entry, query, strategy);
1515 :
1516 264 : PG_RETURN_FLOAT8(distance);
1517 : }
1518 :
1519 : /*
1520 : * The inexact GiST distance methods for geometric types that store bounding
1521 : * boxes.
1522 : *
1523 : * Compute lossy distance from point to index entries. The result is inexact
1524 : * because index entries are bounding boxes, not the exact shapes of the
1525 : * indexed geometric types. We use distance from point to MBR of index entry.
1526 : * This is a lower bound estimate of distance from point to indexed geometric
1527 : * type.
1528 : */
1529 : Datum
1530 1740 : gist_circle_distance(PG_FUNCTION_ARGS)
1531 : {
1532 1740 : GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
1533 1740 : Datum query = PG_GETARG_DATUM(1);
1534 1740 : StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2);
1535 : #ifdef NOT_USED
1536 : Oid subtype = PG_GETARG_OID(3);
1537 : #endif
1538 1740 : bool *recheck = (bool *) PG_GETARG_POINTER(4);
1539 : float8 distance;
1540 :
1541 1740 : distance = gist_bbox_distance(entry, query, strategy);
1542 1740 : *recheck = true;
1543 :
1544 1740 : PG_RETURN_FLOAT8(distance);
1545 : }
1546 :
1547 : Datum
1548 8712 : gist_poly_distance(PG_FUNCTION_ARGS)
1549 : {
1550 8712 : GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
1551 8712 : Datum query = PG_GETARG_DATUM(1);
1552 8712 : StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2);
1553 : #ifdef NOT_USED
1554 : Oid subtype = PG_GETARG_OID(3);
1555 : #endif
1556 8712 : bool *recheck = (bool *) PG_GETARG_POINTER(4);
1557 : float8 distance;
1558 :
1559 8712 : distance = gist_bbox_distance(entry, query, strategy);
1560 8712 : *recheck = true;
1561 :
1562 8712 : PG_RETURN_FLOAT8(distance);
1563 : }
1564 :
1565 : /*
1566 : * Z-order routines for fast index build
1567 : */
1568 :
1569 : /*
1570 : * Compute Z-value of a point
1571 : *
1572 : * Z-order (also known as Morton Code) maps a two-dimensional point to a
1573 : * single integer, in a way that preserves locality. Points that are close in
1574 : * the two-dimensional space are mapped to integer that are not far from each
1575 : * other. We do that by interleaving the bits in the X and Y components.
1576 : *
1577 : * Morton Code is normally defined only for integers, but the X and Y values
1578 : * of a point are floating point. We expect floats to be in IEEE format.
1579 : */
1580 : static uint64
1581 196156 : point_zorder_internal(float4 x, float4 y)
1582 : {
1583 196156 : uint32 ix = ieee_float32_to_uint32(x);
1584 196156 : uint32 iy = ieee_float32_to_uint32(y);
1585 :
1586 : /* Interleave the bits */
1587 196156 : return part_bits32_by2(ix) | (part_bits32_by2(iy) << 1);
1588 : }
1589 :
1590 : /* Interleave 32 bits with zeroes */
1591 : static uint64
1592 392312 : part_bits32_by2(uint32 x)
1593 : {
1594 392312 : uint64 n = x;
1595 :
1596 392312 : n = (n | (n << 16)) & UINT64CONST(0x0000FFFF0000FFFF);
1597 392312 : n = (n | (n << 8)) & UINT64CONST(0x00FF00FF00FF00FF);
1598 392312 : n = (n | (n << 4)) & UINT64CONST(0x0F0F0F0F0F0F0F0F);
1599 392312 : n = (n | (n << 2)) & UINT64CONST(0x3333333333333333);
1600 392312 : n = (n | (n << 1)) & UINT64CONST(0x5555555555555555);
1601 :
1602 392312 : return n;
1603 : }
1604 :
1605 : /*
1606 : * Convert a 32-bit IEEE float to uint32 in a way that preserves the ordering
1607 : */
1608 : static uint32
1609 392312 : ieee_float32_to_uint32(float f)
1610 : {
1611 : /*----
1612 : *
1613 : * IEEE 754 floating point format
1614 : * ------------------------------
1615 : *
1616 : * IEEE 754 floating point numbers have this format:
1617 : *
1618 : * exponent (8 bits)
1619 : * |
1620 : * s eeeeeeee mmmmmmmmmmmmmmmmmmmmmmm
1621 : * | |
1622 : * sign mantissa (23 bits)
1623 : *
1624 : * Infinity has all bits in the exponent set and the mantissa is all
1625 : * zeros. Negative infinity is the same but with the sign bit set.
1626 : *
1627 : * NaNs are represented with all bits in the exponent set, and the least
1628 : * significant bit in the mantissa also set. The rest of the mantissa bits
1629 : * can be used to distinguish different kinds of NaNs.
1630 : *
1631 : * The IEEE format has the nice property that when you take the bit
1632 : * representation and interpret it as an integer, the order is preserved,
1633 : * except for the sign. That holds for the +-Infinity values too.
1634 : *
1635 : * Mapping to uint32
1636 : * -----------------
1637 : *
1638 : * In order to have a smooth transition from negative to positive numbers,
1639 : * we map floats to unsigned integers like this:
1640 : *
1641 : * x < 0 to range 0-7FFFFFFF
1642 : * x = 0 to value 8000000 (both positive and negative zero)
1643 : * x > 0 to range 8000001-FFFFFFFF
1644 : *
1645 : * We don't care to distinguish different kind of NaNs, so they are all
1646 : * mapped to the same arbitrary value, FFFFFFFF. Because of the IEEE bit
1647 : * representation of NaNs, there aren't any non-NaN values that would be
1648 : * mapped to FFFFFFFF. In fact, there is a range of unused values on both
1649 : * ends of the uint32 space.
1650 : */
1651 392312 : if (isnan(f))
1652 24 : return 0xFFFFFFFF;
1653 : else
1654 : {
1655 : union
1656 : {
1657 : float f;
1658 : uint32 i;
1659 : } u;
1660 :
1661 392288 : u.f = f;
1662 :
1663 : /* Check the sign bit */
1664 392288 : if ((u.i & 0x80000000) != 0)
1665 : {
1666 : /*
1667 : * Map the negative value to range 0-7FFFFFFF. This flips the sign
1668 : * bit to 0 in the same instruction.
1669 : */
1670 : Assert(f <= 0); /* can be -0 */
1671 60 : u.i ^= 0xFFFFFFFF;
1672 : }
1673 : else
1674 : {
1675 : /* Map the positive value (or 0) to range 80000000-FFFFFFFF */
1676 392228 : u.i |= 0x80000000;
1677 : }
1678 :
1679 392288 : return u.i;
1680 : }
1681 : }
1682 :
1683 : /*
1684 : * Compare the Z-order of points
1685 : */
1686 : static int
1687 6012 : gist_bbox_zorder_cmp(Datum a, Datum b, SortSupport ssup)
1688 : {
1689 6012 : Point *p1 = &(DatumGetBoxP(a)->low);
1690 6012 : Point *p2 = &(DatumGetBoxP(b)->low);
1691 : uint64 z1;
1692 : uint64 z2;
1693 :
1694 : /*
1695 : * Do a quick check for equality first. It's not clear if this is worth it
1696 : * in general, but certainly is when used as tie-breaker with abbreviated
1697 : * keys,
1698 : */
1699 6012 : if (p1->x == p2->x && p1->y == p2->y)
1700 6000 : return 0;
1701 :
1702 12 : z1 = point_zorder_internal(p1->x, p1->y);
1703 12 : z2 = point_zorder_internal(p2->x, p2->y);
1704 12 : if (z1 > z2)
1705 0 : return 1;
1706 12 : else if (z1 < z2)
1707 0 : return -1;
1708 : else
1709 12 : return 0;
1710 : }
1711 :
1712 : /*
1713 : * Abbreviated version of Z-order comparison
1714 : *
1715 : * The abbreviated format is a Z-order value computed from the two 32-bit
1716 : * floats. Now that sizeof(Datum) is always 8, the 64-bit Z-order value
1717 : * always fits fully in the abbreviated Datum.
1718 : */
1719 : static Datum
1720 196132 : gist_bbox_zorder_abbrev_convert(Datum original, SortSupport ssup)
1721 : {
1722 196132 : Point *p = &(DatumGetBoxP(original)->low);
1723 : uint64 z;
1724 :
1725 196132 : z = point_zorder_internal(p->x, p->y);
1726 :
1727 196132 : return UInt64GetDatum(z);
1728 : }
1729 :
1730 : /*
1731 : * We never consider aborting the abbreviation.
1732 : *
1733 : * On 64-bit systems, the abbreviation is not lossy so it is always
1734 : * worthwhile. (Perhaps it's not on 32-bit systems, but we don't bother
1735 : * with logic to decide.)
1736 : */
1737 : static bool
1738 238 : gist_bbox_zorder_abbrev_abort(int memtupcount, SortSupport ssup)
1739 : {
1740 238 : return false;
1741 : }
1742 :
1743 : /*
1744 : * Sort support routine for fast GiST index build by sorting.
1745 : */
1746 : Datum
1747 150 : gist_point_sortsupport(PG_FUNCTION_ARGS)
1748 : {
1749 150 : SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0);
1750 :
1751 150 : if (ssup->abbreviate)
1752 : {
1753 150 : ssup->comparator = ssup_datum_unsigned_cmp;
1754 150 : ssup->abbrev_converter = gist_bbox_zorder_abbrev_convert;
1755 150 : ssup->abbrev_abort = gist_bbox_zorder_abbrev_abort;
1756 150 : ssup->abbrev_full_comparator = gist_bbox_zorder_cmp;
1757 : }
1758 : else
1759 : {
1760 0 : ssup->comparator = gist_bbox_zorder_cmp;
1761 : }
1762 150 : PG_RETURN_VOID();
1763 : }
|