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-2018, 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 <float.h>
21 #include <math.h>
22
23 #include "access/gist.h"
24 #include "access/stratnum.h"
25 #include "utils/builtins.h"
26 #include "utils/geo_decls.h"
27
28
29 static bool gist_box_leaf_consistent(BOX *key, BOX *query,
30 StrategyNumber strategy);
31 static bool rtree_internal_consistent(BOX *key, BOX *query,
32 StrategyNumber strategy);
33
34 /* Minimum accepted ratio of split */
35 #define LIMIT_RATIO 0.3
36
37 /* Convenience macros for NaN-aware comparisons */
38 #define FLOAT8_EQ(a,b) (float8_cmp_internal(a, b) == 0)
39 #define FLOAT8_LT(a,b) (float8_cmp_internal(a, b) < 0)
40 #define FLOAT8_LE(a,b) (float8_cmp_internal(a, b) <= 0)
41 #define FLOAT8_GT(a,b) (float8_cmp_internal(a, b) > 0)
42 #define FLOAT8_GE(a,b) (float8_cmp_internal(a, b) >= 0)
43 #define FLOAT8_MAX(a,b) (FLOAT8_GT(a, b) ? (a) : (b))
44 #define FLOAT8_MIN(a,b) (FLOAT8_LT(a, b) ? (a) : (b))
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
rt_box_union(BOX * n,const BOX * a,const BOX * b)55 rt_box_union(BOX *n, const BOX *a, const BOX *b)
56 {
57 n->high.x = FLOAT8_MAX(a->high.x, b->high.x);
58 n->high.y = FLOAT8_MAX(a->high.y, b->high.y);
59 n->low.x = FLOAT8_MIN(a->low.x, b->low.x);
60 n->low.y = FLOAT8_MIN(a->low.y, b->low.y);
61 }
62
63 /*
64 * Size of a BOX for penalty-calculation purposes.
65 * The result can be +Infinity, but not NaN.
66 */
67 static double
size_box(const BOX * box)68 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 if (FLOAT8_LE(box->high.x, box->low.x) ||
78 FLOAT8_LE(box->high.y, box->low.y))
79 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 if (isnan(box->high.x) || isnan(box->high.y))
87 return get_float8_infinity();
88 return (box->high.x - box->low.x) * (box->high.y - box->low.y);
89 }
90
91 /*
92 * Return amount by which the union of the two boxes is larger than
93 * the original BOX's area. The result can be +Infinity, but not NaN.
94 */
95 static double
box_penalty(const BOX * original,const BOX * new)96 box_penalty(const BOX *original, const BOX *new)
97 {
98 BOX unionbox;
99
100 rt_box_union(&unionbox, original, new);
101 return size_box(&unionbox) - size_box(original);
102 }
103
104 /*
105 * The GiST Consistent method for boxes
106 *
107 * Should return false if for all data items x below entry,
108 * the predicate x op query must be false, where op is the oper
109 * corresponding to strategy in the pg_amop table.
110 */
111 Datum
gist_box_consistent(PG_FUNCTION_ARGS)112 gist_box_consistent(PG_FUNCTION_ARGS)
113 {
114 GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
115 BOX *query = PG_GETARG_BOX_P(1);
116 StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2);
117
118 /* Oid subtype = PG_GETARG_OID(3); */
119 bool *recheck = (bool *) PG_GETARG_POINTER(4);
120
121 /* All cases served by this function are exact */
122 *recheck = false;
123
124 if (DatumGetBoxP(entry->key) == NULL || query == NULL)
125 PG_RETURN_BOOL(false);
126
127 /*
128 * if entry is not leaf, use rtree_internal_consistent, else use
129 * gist_box_leaf_consistent
130 */
131 if (GIST_LEAF(entry))
132 PG_RETURN_BOOL(gist_box_leaf_consistent(DatumGetBoxP(entry->key),
133 query,
134 strategy));
135 else
136 PG_RETURN_BOOL(rtree_internal_consistent(DatumGetBoxP(entry->key),
137 query,
138 strategy));
139 }
140
141 /*
142 * Increase BOX b to include addon.
143 */
144 static void
adjustBox(BOX * b,const BOX * addon)145 adjustBox(BOX *b, const BOX *addon)
146 {
147 if (FLOAT8_LT(b->high.x, addon->high.x))
148 b->high.x = addon->high.x;
149 if (FLOAT8_GT(b->low.x, addon->low.x))
150 b->low.x = addon->low.x;
151 if (FLOAT8_LT(b->high.y, addon->high.y))
152 b->high.y = addon->high.y;
153 if (FLOAT8_GT(b->low.y, addon->low.y))
154 b->low.y = addon->low.y;
155 }
156
157 /*
158 * The GiST Union method for boxes
159 *
160 * returns the minimal bounding box that encloses all the entries in entryvec
161 */
162 Datum
gist_box_union(PG_FUNCTION_ARGS)163 gist_box_union(PG_FUNCTION_ARGS)
164 {
165 GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
166 int *sizep = (int *) PG_GETARG_POINTER(1);
167 int numranges,
168 i;
169 BOX *cur,
170 *pageunion;
171
172 numranges = entryvec->n;
173 pageunion = (BOX *) palloc(sizeof(BOX));
174 cur = DatumGetBoxP(entryvec->vector[0].key);
175 memcpy((void *) pageunion, (void *) cur, sizeof(BOX));
176
177 for (i = 1; i < numranges; i++)
178 {
179 cur = DatumGetBoxP(entryvec->vector[i].key);
180 adjustBox(pageunion, cur);
181 }
182 *sizep = sizeof(BOX);
183
184 PG_RETURN_POINTER(pageunion);
185 }
186
187 /*
188 * We store boxes as boxes in GiST indexes, so we do not need
189 * compress, decompress, or fetch functions.
190 */
191
192 /*
193 * The GiST Penalty method for boxes (also used for points)
194 *
195 * As in the R-tree paper, we use change in area as our penalty metric
196 */
197 Datum
gist_box_penalty(PG_FUNCTION_ARGS)198 gist_box_penalty(PG_FUNCTION_ARGS)
199 {
200 GISTENTRY *origentry = (GISTENTRY *) PG_GETARG_POINTER(0);
201 GISTENTRY *newentry = (GISTENTRY *) PG_GETARG_POINTER(1);
202 float *result = (float *) PG_GETARG_POINTER(2);
203 BOX *origbox = DatumGetBoxP(origentry->key);
204 BOX *newbox = DatumGetBoxP(newentry->key);
205
206 *result = (float) box_penalty(origbox, newbox);
207 PG_RETURN_POINTER(result);
208 }
209
210 /*
211 * Trivial split: half of entries will be placed on one page
212 * and another half - to another
213 */
214 static void
fallbackSplit(GistEntryVector * entryvec,GIST_SPLITVEC * v)215 fallbackSplit(GistEntryVector *entryvec, GIST_SPLITVEC *v)
216 {
217 OffsetNumber i,
218 maxoff;
219 BOX *unionL = NULL,
220 *unionR = NULL;
221 int nbytes;
222
223 maxoff = entryvec->n - 1;
224
225 nbytes = (maxoff + 2) * sizeof(OffsetNumber);
226 v->spl_left = (OffsetNumber *) palloc(nbytes);
227 v->spl_right = (OffsetNumber *) palloc(nbytes);
228 v->spl_nleft = v->spl_nright = 0;
229
230 for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
231 {
232 BOX *cur = DatumGetBoxP(entryvec->vector[i].key);
233
234 if (i <= (maxoff - FirstOffsetNumber + 1) / 2)
235 {
236 v->spl_left[v->spl_nleft] = i;
237 if (unionL == NULL)
238 {
239 unionL = (BOX *) palloc(sizeof(BOX));
240 *unionL = *cur;
241 }
242 else
243 adjustBox(unionL, cur);
244
245 v->spl_nleft++;
246 }
247 else
248 {
249 v->spl_right[v->spl_nright] = i;
250 if (unionR == NULL)
251 {
252 unionR = (BOX *) palloc(sizeof(BOX));
253 *unionR = *cur;
254 }
255 else
256 adjustBox(unionR, cur);
257
258 v->spl_nright++;
259 }
260 }
261
262 v->spl_ldatum = BoxPGetDatum(unionL);
263 v->spl_rdatum = BoxPGetDatum(unionR);
264 }
265
266 /*
267 * Represents information about an entry that can be placed to either group
268 * without affecting overlap over selected axis ("common entry").
269 */
270 typedef struct
271 {
272 /* Index of entry in the initial array */
273 int index;
274 /* Delta between penalties of entry insertion into different groups */
275 double delta;
276 } CommonEntry;
277
278 /*
279 * Context for g_box_consider_split. Contains information about currently
280 * selected split and some general information.
281 */
282 typedef struct
283 {
284 int entriesCount; /* total number of entries being split */
285 BOX boundingBox; /* minimum bounding box across all entries */
286
287 /* Information about currently selected split follows */
288
289 bool first; /* true if no split was selected yet */
290
291 double leftUpper; /* upper bound of left interval */
292 double rightLower; /* lower bound of right interval */
293
294 float4 ratio;
295 float4 overlap;
296 int dim; /* axis of this split */
297 double range; /* width of general MBR projection to the
298 * selected axis */
299 } ConsiderSplitContext;
300
301 /*
302 * Interval represents projection of box to axis.
303 */
304 typedef struct
305 {
306 double lower,
307 upper;
308 } SplitInterval;
309
310 /*
311 * Interval comparison function by lower bound of the interval;
312 */
313 static int
interval_cmp_lower(const void * i1,const void * i2)314 interval_cmp_lower(const void *i1, const void *i2)
315 {
316 double lower1 = ((const SplitInterval *) i1)->lower,
317 lower2 = ((const SplitInterval *) i2)->lower;
318
319 return float8_cmp_internal(lower1, lower2);
320 }
321
322 /*
323 * Interval comparison function by upper bound of the interval;
324 */
325 static int
interval_cmp_upper(const void * i1,const void * i2)326 interval_cmp_upper(const void *i1, const void *i2)
327 {
328 double upper1 = ((const SplitInterval *) i1)->upper,
329 upper2 = ((const SplitInterval *) i2)->upper;
330
331 return float8_cmp_internal(upper1, upper2);
332 }
333
334 /*
335 * Replace negative (or NaN) value with zero.
336 */
337 static inline float
non_negative(float val)338 non_negative(float val)
339 {
340 if (val >= 0.0f)
341 return val;
342 else
343 return 0.0f;
344 }
345
346 /*
347 * Consider replacement of currently selected split with the better one.
348 */
349 static inline void
g_box_consider_split(ConsiderSplitContext * context,int dimNum,double rightLower,int minLeftCount,double leftUpper,int maxLeftCount)350 g_box_consider_split(ConsiderSplitContext *context, int dimNum,
351 double rightLower, int minLeftCount,
352 double leftUpper, int maxLeftCount)
353 {
354 int leftCount,
355 rightCount;
356 float4 ratio,
357 overlap;
358 double range;
359
360 /*
361 * Calculate entries distribution ratio assuming most uniform distribution
362 * of common entries.
363 */
364 if (minLeftCount >= (context->entriesCount + 1) / 2)
365 {
366 leftCount = minLeftCount;
367 }
368 else
369 {
370 if (maxLeftCount <= context->entriesCount / 2)
371 leftCount = maxLeftCount;
372 else
373 leftCount = context->entriesCount / 2;
374 }
375 rightCount = context->entriesCount - leftCount;
376
377 /*
378 * Ratio of split - quotient between size of lesser group and total
379 * entries count.
380 */
381 ratio = ((float4) Min(leftCount, rightCount)) /
382 ((float4) context->entriesCount);
383
384 if (ratio > LIMIT_RATIO)
385 {
386 bool selectthis = false;
387
388 /*
389 * The ratio is acceptable, so compare current split with previously
390 * selected one. Between splits of one dimension we search for minimal
391 * overlap (allowing negative values) and minimal ration (between same
392 * overlaps. We switch dimension if find less overlap (non-negative)
393 * or less range with same overlap.
394 */
395 if (dimNum == 0)
396 range = context->boundingBox.high.x - context->boundingBox.low.x;
397 else
398 range = context->boundingBox.high.y - context->boundingBox.low.y;
399
400 overlap = (leftUpper - rightLower) / range;
401
402 /* If there is no previous selection, select this */
403 if (context->first)
404 selectthis = true;
405 else if (context->dim == dimNum)
406 {
407 /*
408 * Within the same dimension, choose the new split if it has a
409 * smaller overlap, or same overlap but better ratio.
410 */
411 if (overlap < context->overlap ||
412 (overlap == context->overlap && ratio > context->ratio))
413 selectthis = true;
414 }
415 else
416 {
417 /*
418 * Across dimensions, choose the new split if it has a smaller
419 * *non-negative* overlap, or same *non-negative* overlap but
420 * bigger range. This condition differs from the one described in
421 * the article. On the datasets where leaf MBRs don't overlap
422 * themselves, non-overlapping splits (i.e. splits which have zero
423 * *non-negative* overlap) are frequently possible. In this case
424 * splits tends to be along one dimension, because most distant
425 * non-overlapping splits (i.e. having lowest negative overlap)
426 * appears to be in the same dimension as in the previous split.
427 * Therefore MBRs appear to be very prolonged along another
428 * dimension, which leads to bad search performance. Using range
429 * as the second split criteria makes MBRs more quadratic. Using
430 * *non-negative* overlap instead of overlap as the first split
431 * criteria gives to range criteria a chance to matter, because
432 * non-overlapping splits are equivalent in this criteria.
433 */
434 if (non_negative(overlap) < non_negative(context->overlap) ||
435 (range > context->range &&
436 non_negative(overlap) <= non_negative(context->overlap)))
437 selectthis = true;
438 }
439
440 if (selectthis)
441 {
442 /* save information about selected split */
443 context->first = false;
444 context->ratio = ratio;
445 context->range = range;
446 context->overlap = overlap;
447 context->rightLower = rightLower;
448 context->leftUpper = leftUpper;
449 context->dim = dimNum;
450 }
451 }
452 }
453
454 /*
455 * Compare common entries by their deltas.
456 * (We assume the deltas can't be NaN.)
457 */
458 static int
common_entry_cmp(const void * i1,const void * i2)459 common_entry_cmp(const void *i1, const void *i2)
460 {
461 double delta1 = ((const CommonEntry *) i1)->delta,
462 delta2 = ((const CommonEntry *) i2)->delta;
463
464 if (delta1 < delta2)
465 return -1;
466 else if (delta1 > delta2)
467 return 1;
468 else
469 return 0;
470 }
471
472 /*
473 * --------------------------------------------------------------------------
474 * Double sorting split algorithm. This is used for both boxes and points.
475 *
476 * The algorithm finds split of boxes by considering splits along each axis.
477 * Each entry is first projected as an interval on the X-axis, and different
478 * ways to split the intervals into two groups are considered, trying to
479 * minimize the overlap of the groups. Then the same is repeated for the
480 * Y-axis, and the overall best split is chosen. The quality of a split is
481 * determined by overlap along that axis and some other criteria (see
482 * g_box_consider_split).
483 *
484 * After that, all the entries are divided into three groups:
485 *
486 * 1) Entries which should be placed to the left group
487 * 2) Entries which should be placed to the right group
488 * 3) "Common entries" which can be placed to any of groups without affecting
489 * of overlap along selected axis.
490 *
491 * The common entries are distributed by minimizing penalty.
492 *
493 * For details see:
494 * "A new double sorting-based node splitting algorithm for R-tree", A. Korotkov
495 * http://syrcose.ispras.ru/2011/files/SYRCoSE2011_Proceedings.pdf#page=36
496 * --------------------------------------------------------------------------
497 */
498 Datum
gist_box_picksplit(PG_FUNCTION_ARGS)499 gist_box_picksplit(PG_FUNCTION_ARGS)
500 {
501 GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
502 GIST_SPLITVEC *v = (GIST_SPLITVEC *) PG_GETARG_POINTER(1);
503 OffsetNumber i,
504 maxoff;
505 ConsiderSplitContext context;
506 BOX *box,
507 *leftBox,
508 *rightBox;
509 int dim,
510 commonEntriesCount;
511 SplitInterval *intervalsLower,
512 *intervalsUpper;
513 CommonEntry *commonEntries;
514 int nentries;
515
516 memset(&context, 0, sizeof(ConsiderSplitContext));
517
518 maxoff = entryvec->n - 1;
519 nentries = context.entriesCount = maxoff - FirstOffsetNumber + 1;
520
521 /* Allocate arrays for intervals along axes */
522 intervalsLower = (SplitInterval *) palloc(nentries * sizeof(SplitInterval));
523 intervalsUpper = (SplitInterval *) palloc(nentries * sizeof(SplitInterval));
524
525 /*
526 * Calculate the overall minimum bounding box over all the entries.
527 */
528 for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
529 {
530 box = DatumGetBoxP(entryvec->vector[i].key);
531 if (i == FirstOffsetNumber)
532 context.boundingBox = *box;
533 else
534 adjustBox(&context.boundingBox, box);
535 }
536
537 /*
538 * Iterate over axes for optimal split searching.
539 */
540 context.first = true; /* nothing selected yet */
541 for (dim = 0; dim < 2; dim++)
542 {
543 double leftUpper,
544 rightLower;
545 int i1,
546 i2;
547
548 /* Project each entry as an interval on the selected axis. */
549 for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
550 {
551 box = DatumGetBoxP(entryvec->vector[i].key);
552 if (dim == 0)
553 {
554 intervalsLower[i - FirstOffsetNumber].lower = box->low.x;
555 intervalsLower[i - FirstOffsetNumber].upper = box->high.x;
556 }
557 else
558 {
559 intervalsLower[i - FirstOffsetNumber].lower = box->low.y;
560 intervalsLower[i - FirstOffsetNumber].upper = box->high.y;
561 }
562 }
563
564 /*
565 * Make two arrays of intervals: one sorted by lower bound and another
566 * sorted by upper bound.
567 */
568 memcpy(intervalsUpper, intervalsLower,
569 sizeof(SplitInterval) * nentries);
570 qsort(intervalsLower, nentries, sizeof(SplitInterval),
571 interval_cmp_lower);
572 qsort(intervalsUpper, nentries, sizeof(SplitInterval),
573 interval_cmp_upper);
574
575 /*----
576 * The goal is to form a left and right interval, so that every entry
577 * interval is contained by either left or right interval (or both).
578 *
579 * For example, with the intervals (0,1), (1,3), (2,3), (2,4):
580 *
581 * 0 1 2 3 4
582 * +-+
583 * +---+
584 * +-+
585 * +---+
586 *
587 * The left and right intervals are of the form (0,a) and (b,4).
588 * We first consider splits where b is the lower bound of an entry.
589 * We iterate through all entries, and for each b, calculate the
590 * smallest possible a. Then we consider splits where a is the
591 * upper bound of an entry, and for each a, calculate the greatest
592 * possible b.
593 *
594 * In the above example, the first loop would consider splits:
595 * b=0: (0,1)-(0,4)
596 * b=1: (0,1)-(1,4)
597 * b=2: (0,3)-(2,4)
598 *
599 * And the second loop:
600 * a=1: (0,1)-(1,4)
601 * a=3: (0,3)-(2,4)
602 * a=4: (0,4)-(2,4)
603 */
604
605 /*
606 * Iterate over lower bound of right group, finding smallest possible
607 * upper bound of left group.
608 */
609 i1 = 0;
610 i2 = 0;
611 rightLower = intervalsLower[i1].lower;
612 leftUpper = intervalsUpper[i2].lower;
613 while (true)
614 {
615 /*
616 * Find next lower bound of right group.
617 */
618 while (i1 < nentries &&
619 FLOAT8_EQ(rightLower, intervalsLower[i1].lower))
620 {
621 if (FLOAT8_LT(leftUpper, intervalsLower[i1].upper))
622 leftUpper = intervalsLower[i1].upper;
623 i1++;
624 }
625 if (i1 >= nentries)
626 break;
627 rightLower = intervalsLower[i1].lower;
628
629 /*
630 * Find count of intervals which anyway should be placed to the
631 * left group.
632 */
633 while (i2 < nentries &&
634 FLOAT8_LE(intervalsUpper[i2].upper, leftUpper))
635 i2++;
636
637 /*
638 * Consider found split.
639 */
640 g_box_consider_split(&context, dim, rightLower, i1, leftUpper, i2);
641 }
642
643 /*
644 * Iterate over upper bound of left group finding greatest possible
645 * lower bound of right group.
646 */
647 i1 = nentries - 1;
648 i2 = nentries - 1;
649 rightLower = intervalsLower[i1].upper;
650 leftUpper = intervalsUpper[i2].upper;
651 while (true)
652 {
653 /*
654 * Find next upper bound of left group.
655 */
656 while (i2 >= 0 && FLOAT8_EQ(leftUpper, intervalsUpper[i2].upper))
657 {
658 if (FLOAT8_GT(rightLower, intervalsUpper[i2].lower))
659 rightLower = intervalsUpper[i2].lower;
660 i2--;
661 }
662 if (i2 < 0)
663 break;
664 leftUpper = intervalsUpper[i2].upper;
665
666 /*
667 * Find count of intervals which anyway should be placed to the
668 * right group.
669 */
670 while (i1 >= 0 && FLOAT8_GE(intervalsLower[i1].lower, rightLower))
671 i1--;
672
673 /*
674 * Consider found split.
675 */
676 g_box_consider_split(&context, dim,
677 rightLower, i1 + 1, leftUpper, i2 + 1);
678 }
679 }
680
681 /*
682 * If we failed to find any acceptable splits, use trivial split.
683 */
684 if (context.first)
685 {
686 fallbackSplit(entryvec, v);
687 PG_RETURN_POINTER(v);
688 }
689
690 /*
691 * Ok, we have now selected the split across one axis.
692 *
693 * While considering the splits, we already determined that there will be
694 * enough entries in both groups to reach the desired ratio, but we did
695 * not memorize which entries go to which group. So determine that now.
696 */
697
698 /* Allocate vectors for results */
699 v->spl_left = (OffsetNumber *) palloc(nentries * sizeof(OffsetNumber));
700 v->spl_right = (OffsetNumber *) palloc(nentries * sizeof(OffsetNumber));
701 v->spl_nleft = 0;
702 v->spl_nright = 0;
703
704 /* Allocate bounding boxes of left and right groups */
705 leftBox = palloc0(sizeof(BOX));
706 rightBox = palloc0(sizeof(BOX));
707
708 /*
709 * Allocate an array for "common entries" - entries which can be placed to
710 * either group without affecting overlap along selected axis.
711 */
712 commonEntriesCount = 0;
713 commonEntries = (CommonEntry *) palloc(nentries * sizeof(CommonEntry));
714
715 /* Helper macros to place an entry in the left or right group */
716 #define PLACE_LEFT(box, off) \
717 do { \
718 if (v->spl_nleft > 0) \
719 adjustBox(leftBox, box); \
720 else \
721 *leftBox = *(box); \
722 v->spl_left[v->spl_nleft++] = off; \
723 } while(0)
724
725 #define PLACE_RIGHT(box, off) \
726 do { \
727 if (v->spl_nright > 0) \
728 adjustBox(rightBox, box); \
729 else \
730 *rightBox = *(box); \
731 v->spl_right[v->spl_nright++] = off; \
732 } while(0)
733
734 /*
735 * Distribute entries which can be distributed unambiguously, and collect
736 * common entries.
737 */
738 for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
739 {
740 double lower,
741 upper;
742
743 /*
744 * Get upper and lower bounds along selected axis.
745 */
746 box = DatumGetBoxP(entryvec->vector[i].key);
747 if (context.dim == 0)
748 {
749 lower = box->low.x;
750 upper = box->high.x;
751 }
752 else
753 {
754 lower = box->low.y;
755 upper = box->high.y;
756 }
757
758 if (FLOAT8_LE(upper, context.leftUpper))
759 {
760 /* Fits to the left group */
761 if (FLOAT8_GE(lower, context.rightLower))
762 {
763 /* Fits also to the right group, so "common entry" */
764 commonEntries[commonEntriesCount++].index = i;
765 }
766 else
767 {
768 /* Doesn't fit to the right group, so join to the left group */
769 PLACE_LEFT(box, i);
770 }
771 }
772 else
773 {
774 /*
775 * Each entry should fit on either left or right group. Since this
776 * entry didn't fit on the left group, it better fit in the right
777 * group.
778 */
779 Assert(FLOAT8_GE(lower, context.rightLower));
780
781 /* Doesn't fit to the left group, so join to the right group */
782 PLACE_RIGHT(box, i);
783 }
784 }
785
786 /*
787 * Distribute "common entries", if any.
788 */
789 if (commonEntriesCount > 0)
790 {
791 /*
792 * Calculate minimum number of entries that must be placed in both
793 * groups, to reach LIMIT_RATIO.
794 */
795 int m = ceil(LIMIT_RATIO * (double) nentries);
796
797 /*
798 * Calculate delta between penalties of join "common entries" to
799 * different groups.
800 */
801 for (i = 0; i < commonEntriesCount; i++)
802 {
803 box = DatumGetBoxP(entryvec->vector[commonEntries[i].index].key);
804 commonEntries[i].delta = Abs(box_penalty(leftBox, box) -
805 box_penalty(rightBox, box));
806 }
807
808 /*
809 * Sort "common entries" by calculated deltas in order to distribute
810 * the most ambiguous entries first.
811 */
812 qsort(commonEntries, commonEntriesCount, sizeof(CommonEntry), common_entry_cmp);
813
814 /*
815 * Distribute "common entries" between groups.
816 */
817 for (i = 0; i < commonEntriesCount; i++)
818 {
819 box = DatumGetBoxP(entryvec->vector[commonEntries[i].index].key);
820
821 /*
822 * Check if we have to place this entry in either group to achieve
823 * LIMIT_RATIO.
824 */
825 if (v->spl_nleft + (commonEntriesCount - i) <= m)
826 PLACE_LEFT(box, commonEntries[i].index);
827 else if (v->spl_nright + (commonEntriesCount - i) <= m)
828 PLACE_RIGHT(box, commonEntries[i].index);
829 else
830 {
831 /* Otherwise select the group by minimal penalty */
832 if (box_penalty(leftBox, box) < box_penalty(rightBox, box))
833 PLACE_LEFT(box, commonEntries[i].index);
834 else
835 PLACE_RIGHT(box, commonEntries[i].index);
836 }
837 }
838 }
839
840 v->spl_ldatum = PointerGetDatum(leftBox);
841 v->spl_rdatum = PointerGetDatum(rightBox);
842 PG_RETURN_POINTER(v);
843 }
844
845 /*
846 * Equality method
847 *
848 * This is used for boxes, points, circles, and polygons, all of which store
849 * boxes as GiST index entries.
850 *
851 * Returns true only when boxes are exactly the same. We can't use fuzzy
852 * comparisons here without breaking index consistency; therefore, this isn't
853 * equivalent to box_same().
854 */
855 Datum
gist_box_same(PG_FUNCTION_ARGS)856 gist_box_same(PG_FUNCTION_ARGS)
857 {
858 BOX *b1 = PG_GETARG_BOX_P(0);
859 BOX *b2 = PG_GETARG_BOX_P(1);
860 bool *result = (bool *) PG_GETARG_POINTER(2);
861
862 if (b1 && b2)
863 *result = (FLOAT8_EQ(b1->low.x, b2->low.x) &&
864 FLOAT8_EQ(b1->low.y, b2->low.y) &&
865 FLOAT8_EQ(b1->high.x, b2->high.x) &&
866 FLOAT8_EQ(b1->high.y, b2->high.y));
867 else
868 *result = (b1 == NULL && b2 == NULL);
869 PG_RETURN_POINTER(result);
870 }
871
872 /*
873 * Leaf-level consistency for boxes: just apply the query operator
874 */
875 static bool
gist_box_leaf_consistent(BOX * key,BOX * query,StrategyNumber strategy)876 gist_box_leaf_consistent(BOX *key, BOX *query, StrategyNumber strategy)
877 {
878 bool retval;
879
880 switch (strategy)
881 {
882 case RTLeftStrategyNumber:
883 retval = DatumGetBool(DirectFunctionCall2(box_left,
884 PointerGetDatum(key),
885 PointerGetDatum(query)));
886 break;
887 case RTOverLeftStrategyNumber:
888 retval = DatumGetBool(DirectFunctionCall2(box_overleft,
889 PointerGetDatum(key),
890 PointerGetDatum(query)));
891 break;
892 case RTOverlapStrategyNumber:
893 retval = DatumGetBool(DirectFunctionCall2(box_overlap,
894 PointerGetDatum(key),
895 PointerGetDatum(query)));
896 break;
897 case RTOverRightStrategyNumber:
898 retval = DatumGetBool(DirectFunctionCall2(box_overright,
899 PointerGetDatum(key),
900 PointerGetDatum(query)));
901 break;
902 case RTRightStrategyNumber:
903 retval = DatumGetBool(DirectFunctionCall2(box_right,
904 PointerGetDatum(key),
905 PointerGetDatum(query)));
906 break;
907 case RTSameStrategyNumber:
908 retval = DatumGetBool(DirectFunctionCall2(box_same,
909 PointerGetDatum(key),
910 PointerGetDatum(query)));
911 break;
912 case RTContainsStrategyNumber:
913 case RTOldContainsStrategyNumber:
914 retval = DatumGetBool(DirectFunctionCall2(box_contain,
915 PointerGetDatum(key),
916 PointerGetDatum(query)));
917 break;
918 case RTContainedByStrategyNumber:
919 case RTOldContainedByStrategyNumber:
920 retval = DatumGetBool(DirectFunctionCall2(box_contained,
921 PointerGetDatum(key),
922 PointerGetDatum(query)));
923 break;
924 case RTOverBelowStrategyNumber:
925 retval = DatumGetBool(DirectFunctionCall2(box_overbelow,
926 PointerGetDatum(key),
927 PointerGetDatum(query)));
928 break;
929 case RTBelowStrategyNumber:
930 retval = DatumGetBool(DirectFunctionCall2(box_below,
931 PointerGetDatum(key),
932 PointerGetDatum(query)));
933 break;
934 case RTAboveStrategyNumber:
935 retval = DatumGetBool(DirectFunctionCall2(box_above,
936 PointerGetDatum(key),
937 PointerGetDatum(query)));
938 break;
939 case RTOverAboveStrategyNumber:
940 retval = DatumGetBool(DirectFunctionCall2(box_overabove,
941 PointerGetDatum(key),
942 PointerGetDatum(query)));
943 break;
944 default:
945 elog(ERROR, "unrecognized strategy number: %d", strategy);
946 retval = false; /* keep compiler quiet */
947 break;
948 }
949 return retval;
950 }
951
952 /*****************************************
953 * Common rtree functions (for boxes, polygons, and circles)
954 *****************************************/
955
956 /*
957 * Internal-page consistency for all these types
958 *
959 * We can use the same function since all types use bounding boxes as the
960 * internal-page representation.
961 */
962 static bool
rtree_internal_consistent(BOX * key,BOX * query,StrategyNumber strategy)963 rtree_internal_consistent(BOX *key, BOX *query, StrategyNumber strategy)
964 {
965 bool retval;
966
967 switch (strategy)
968 {
969 case RTLeftStrategyNumber:
970 retval = !DatumGetBool(DirectFunctionCall2(box_overright,
971 PointerGetDatum(key),
972 PointerGetDatum(query)));
973 break;
974 case RTOverLeftStrategyNumber:
975 retval = !DatumGetBool(DirectFunctionCall2(box_right,
976 PointerGetDatum(key),
977 PointerGetDatum(query)));
978 break;
979 case RTOverlapStrategyNumber:
980 retval = DatumGetBool(DirectFunctionCall2(box_overlap,
981 PointerGetDatum(key),
982 PointerGetDatum(query)));
983 break;
984 case RTOverRightStrategyNumber:
985 retval = !DatumGetBool(DirectFunctionCall2(box_left,
986 PointerGetDatum(key),
987 PointerGetDatum(query)));
988 break;
989 case RTRightStrategyNumber:
990 retval = !DatumGetBool(DirectFunctionCall2(box_overleft,
991 PointerGetDatum(key),
992 PointerGetDatum(query)));
993 break;
994 case RTSameStrategyNumber:
995 case RTContainsStrategyNumber:
996 case RTOldContainsStrategyNumber:
997 retval = DatumGetBool(DirectFunctionCall2(box_contain,
998 PointerGetDatum(key),
999 PointerGetDatum(query)));
1000 break;
1001 case RTContainedByStrategyNumber:
1002 case RTOldContainedByStrategyNumber:
1003 retval = DatumGetBool(DirectFunctionCall2(box_overlap,
1004 PointerGetDatum(key),
1005 PointerGetDatum(query)));
1006 break;
1007 case RTOverBelowStrategyNumber:
1008 retval = !DatumGetBool(DirectFunctionCall2(box_above,
1009 PointerGetDatum(key),
1010 PointerGetDatum(query)));
1011 break;
1012 case RTBelowStrategyNumber:
1013 retval = !DatumGetBool(DirectFunctionCall2(box_overabove,
1014 PointerGetDatum(key),
1015 PointerGetDatum(query)));
1016 break;
1017 case RTAboveStrategyNumber:
1018 retval = !DatumGetBool(DirectFunctionCall2(box_overbelow,
1019 PointerGetDatum(key),
1020 PointerGetDatum(query)));
1021 break;
1022 case RTOverAboveStrategyNumber:
1023 retval = !DatumGetBool(DirectFunctionCall2(box_below,
1024 PointerGetDatum(key),
1025 PointerGetDatum(query)));
1026 break;
1027 default:
1028 elog(ERROR, "unrecognized strategy number: %d", strategy);
1029 retval = false; /* keep compiler quiet */
1030 break;
1031 }
1032 return retval;
1033 }
1034
1035 /**************************************************
1036 * Polygon ops
1037 **************************************************/
1038
1039 /*
1040 * GiST compress for polygons: represent a polygon by its bounding box
1041 */
1042 Datum
gist_poly_compress(PG_FUNCTION_ARGS)1043 gist_poly_compress(PG_FUNCTION_ARGS)
1044 {
1045 GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
1046 GISTENTRY *retval;
1047
1048 if (entry->leafkey)
1049 {
1050 POLYGON *in = DatumGetPolygonP(entry->key);
1051 BOX *r;
1052
1053 r = (BOX *) palloc(sizeof(BOX));
1054 memcpy((void *) r, (void *) &(in->boundbox), sizeof(BOX));
1055
1056 retval = (GISTENTRY *) palloc(sizeof(GISTENTRY));
1057 gistentryinit(*retval, PointerGetDatum(r),
1058 entry->rel, entry->page,
1059 entry->offset, false);
1060 }
1061 else
1062 retval = entry;
1063 PG_RETURN_POINTER(retval);
1064 }
1065
1066 /*
1067 * The GiST Consistent method for polygons
1068 */
1069 Datum
gist_poly_consistent(PG_FUNCTION_ARGS)1070 gist_poly_consistent(PG_FUNCTION_ARGS)
1071 {
1072 GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
1073 POLYGON *query = PG_GETARG_POLYGON_P(1);
1074 StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2);
1075
1076 /* Oid subtype = PG_GETARG_OID(3); */
1077 bool *recheck = (bool *) PG_GETARG_POINTER(4);
1078 bool result;
1079
1080 /* All cases served by this function are inexact */
1081 *recheck = true;
1082
1083 if (DatumGetBoxP(entry->key) == NULL || query == NULL)
1084 PG_RETURN_BOOL(false);
1085
1086 /*
1087 * Since the operators require recheck anyway, we can just use
1088 * rtree_internal_consistent even at leaf nodes. (This works in part
1089 * because the index entries are bounding boxes not polygons.)
1090 */
1091 result = rtree_internal_consistent(DatumGetBoxP(entry->key),
1092 &(query->boundbox), strategy);
1093
1094 /* Avoid memory leak if supplied poly is toasted */
1095 PG_FREE_IF_COPY(query, 1);
1096
1097 PG_RETURN_BOOL(result);
1098 }
1099
1100 /**************************************************
1101 * Circle ops
1102 **************************************************/
1103
1104 /*
1105 * GiST compress for circles: represent a circle by its bounding box
1106 */
1107 Datum
gist_circle_compress(PG_FUNCTION_ARGS)1108 gist_circle_compress(PG_FUNCTION_ARGS)
1109 {
1110 GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
1111 GISTENTRY *retval;
1112
1113 if (entry->leafkey)
1114 {
1115 CIRCLE *in = DatumGetCircleP(entry->key);
1116 BOX *r;
1117
1118 r = (BOX *) palloc(sizeof(BOX));
1119 r->high.x = in->center.x + in->radius;
1120 r->low.x = in->center.x - in->radius;
1121 r->high.y = in->center.y + in->radius;
1122 r->low.y = in->center.y - in->radius;
1123
1124 retval = (GISTENTRY *) palloc(sizeof(GISTENTRY));
1125 gistentryinit(*retval, PointerGetDatum(r),
1126 entry->rel, entry->page,
1127 entry->offset, false);
1128 }
1129 else
1130 retval = entry;
1131 PG_RETURN_POINTER(retval);
1132 }
1133
1134 /*
1135 * The GiST Consistent method for circles
1136 */
1137 Datum
gist_circle_consistent(PG_FUNCTION_ARGS)1138 gist_circle_consistent(PG_FUNCTION_ARGS)
1139 {
1140 GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
1141 CIRCLE *query = PG_GETARG_CIRCLE_P(1);
1142 StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2);
1143
1144 /* Oid subtype = PG_GETARG_OID(3); */
1145 bool *recheck = (bool *) PG_GETARG_POINTER(4);
1146 BOX bbox;
1147 bool result;
1148
1149 /* All cases served by this function are inexact */
1150 *recheck = true;
1151
1152 if (DatumGetBoxP(entry->key) == NULL || query == NULL)
1153 PG_RETURN_BOOL(false);
1154
1155 /*
1156 * Since the operators require recheck anyway, we can just use
1157 * rtree_internal_consistent even at leaf nodes. (This works in part
1158 * because the index entries are bounding boxes not circles.)
1159 */
1160 bbox.high.x = query->center.x + query->radius;
1161 bbox.low.x = query->center.x - query->radius;
1162 bbox.high.y = query->center.y + query->radius;
1163 bbox.low.y = query->center.y - query->radius;
1164
1165 result = rtree_internal_consistent(DatumGetBoxP(entry->key),
1166 &bbox, strategy);
1167
1168 PG_RETURN_BOOL(result);
1169 }
1170
1171 /**************************************************
1172 * Point ops
1173 **************************************************/
1174
1175 Datum
gist_point_compress(PG_FUNCTION_ARGS)1176 gist_point_compress(PG_FUNCTION_ARGS)
1177 {
1178 GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
1179
1180 if (entry->leafkey) /* Point, actually */
1181 {
1182 BOX *box = palloc(sizeof(BOX));
1183 Point *point = DatumGetPointP(entry->key);
1184 GISTENTRY *retval = palloc(sizeof(GISTENTRY));
1185
1186 box->high = box->low = *point;
1187
1188 gistentryinit(*retval, BoxPGetDatum(box),
1189 entry->rel, entry->page, entry->offset, false);
1190
1191 PG_RETURN_POINTER(retval);
1192 }
1193
1194 PG_RETURN_POINTER(entry);
1195 }
1196
1197 /*
1198 * GiST Fetch method for point
1199 *
1200 * Get point coordinates from its bounding box coordinates and form new
1201 * gistentry.
1202 */
1203 Datum
gist_point_fetch(PG_FUNCTION_ARGS)1204 gist_point_fetch(PG_FUNCTION_ARGS)
1205 {
1206 GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
1207 BOX *in = DatumGetBoxP(entry->key);
1208 Point *r;
1209 GISTENTRY *retval;
1210
1211 retval = palloc(sizeof(GISTENTRY));
1212
1213 r = (Point *) palloc(sizeof(Point));
1214 r->x = in->high.x;
1215 r->y = in->high.y;
1216 gistentryinit(*retval, PointerGetDatum(r),
1217 entry->rel, entry->page,
1218 entry->offset, false);
1219
1220 PG_RETURN_POINTER(retval);
1221 }
1222
1223
1224 #define point_point_distance(p1,p2) \
1225 DatumGetFloat8(DirectFunctionCall2(point_distance, \
1226 PointPGetDatum(p1), PointPGetDatum(p2)))
1227
1228 static double
computeDistance(bool isLeaf,BOX * box,Point * point)1229 computeDistance(bool isLeaf, BOX *box, Point *point)
1230 {
1231 double result = 0.0;
1232
1233 if (isLeaf)
1234 {
1235 /* simple point to point distance */
1236 result = point_point_distance(point, &box->low);
1237 }
1238 else if (point->x <= box->high.x && point->x >= box->low.x &&
1239 point->y <= box->high.y && point->y >= box->low.y)
1240 {
1241 /* point inside the box */
1242 result = 0.0;
1243 }
1244 else if (point->x <= box->high.x && point->x >= box->low.x)
1245 {
1246 /* point is over or below box */
1247 Assert(box->low.y <= box->high.y);
1248 if (point->y > box->high.y)
1249 result = point->y - box->high.y;
1250 else if (point->y < box->low.y)
1251 result = box->low.y - point->y;
1252 else
1253 elog(ERROR, "inconsistent point values");
1254 }
1255 else if (point->y <= box->high.y && point->y >= box->low.y)
1256 {
1257 /* point is to left or right of box */
1258 Assert(box->low.x <= box->high.x);
1259 if (point->x > box->high.x)
1260 result = point->x - box->high.x;
1261 else if (point->x < box->low.x)
1262 result = box->low.x - point->x;
1263 else
1264 elog(ERROR, "inconsistent point values");
1265 }
1266 else
1267 {
1268 /* closest point will be a vertex */
1269 Point p;
1270 double subresult;
1271
1272 result = point_point_distance(point, &box->low);
1273
1274 subresult = point_point_distance(point, &box->high);
1275 if (result > subresult)
1276 result = subresult;
1277
1278 p.x = box->low.x;
1279 p.y = box->high.y;
1280 subresult = point_point_distance(point, &p);
1281 if (result > subresult)
1282 result = subresult;
1283
1284 p.x = box->high.x;
1285 p.y = box->low.y;
1286 subresult = point_point_distance(point, &p);
1287 if (result > subresult)
1288 result = subresult;
1289 }
1290
1291 return result;
1292 }
1293
1294 static bool
gist_point_consistent_internal(StrategyNumber strategy,bool isLeaf,BOX * key,Point * query)1295 gist_point_consistent_internal(StrategyNumber strategy,
1296 bool isLeaf, BOX *key, Point *query)
1297 {
1298 bool result = false;
1299
1300 switch (strategy)
1301 {
1302 case RTLeftStrategyNumber:
1303 result = FPlt(key->low.x, query->x);
1304 break;
1305 case RTRightStrategyNumber:
1306 result = FPgt(key->high.x, query->x);
1307 break;
1308 case RTAboveStrategyNumber:
1309 result = FPgt(key->high.y, query->y);
1310 break;
1311 case RTBelowStrategyNumber:
1312 result = FPlt(key->low.y, query->y);
1313 break;
1314 case RTSameStrategyNumber:
1315 if (isLeaf)
1316 {
1317 /* key.high must equal key.low, so we can disregard it */
1318 result = (FPeq(key->low.x, query->x) &&
1319 FPeq(key->low.y, query->y));
1320 }
1321 else
1322 {
1323 result = (FPle(query->x, key->high.x) &&
1324 FPge(query->x, key->low.x) &&
1325 FPle(query->y, key->high.y) &&
1326 FPge(query->y, key->low.y));
1327 }
1328 break;
1329 default:
1330 elog(ERROR, "unrecognized strategy number: %d", strategy);
1331 result = false; /* keep compiler quiet */
1332 break;
1333 }
1334
1335 return result;
1336 }
1337
1338 #define GeoStrategyNumberOffset 20
1339 #define PointStrategyNumberGroup 0
1340 #define BoxStrategyNumberGroup 1
1341 #define PolygonStrategyNumberGroup 2
1342 #define CircleStrategyNumberGroup 3
1343
1344 Datum
gist_point_consistent(PG_FUNCTION_ARGS)1345 gist_point_consistent(PG_FUNCTION_ARGS)
1346 {
1347 GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
1348 StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2);
1349 bool *recheck = (bool *) PG_GETARG_POINTER(4);
1350 bool result;
1351 StrategyNumber strategyGroup = strategy / GeoStrategyNumberOffset;
1352
1353 switch (strategyGroup)
1354 {
1355 case PointStrategyNumberGroup:
1356 result = gist_point_consistent_internal(strategy % GeoStrategyNumberOffset,
1357 GIST_LEAF(entry),
1358 DatumGetBoxP(entry->key),
1359 PG_GETARG_POINT_P(1));
1360 *recheck = false;
1361 break;
1362 case BoxStrategyNumberGroup:
1363 {
1364 /*
1365 * The only operator in this group is point <@ box (on_pb), so
1366 * we needn't examine strategy again.
1367 *
1368 * For historical reasons, on_pb uses exact rather than fuzzy
1369 * comparisons. We could use box_overlap when at an internal
1370 * page, but that would lead to possibly visiting child pages
1371 * uselessly, because box_overlap uses fuzzy comparisons.
1372 * Instead we write a non-fuzzy overlap test. The same code
1373 * will also serve for leaf-page tests, since leaf keys have
1374 * high == low.
1375 */
1376 BOX *query,
1377 *key;
1378
1379 query = PG_GETARG_BOX_P(1);
1380 key = DatumGetBoxP(entry->key);
1381
1382 result = (key->high.x >= query->low.x &&
1383 key->low.x <= query->high.x &&
1384 key->high.y >= query->low.y &&
1385 key->low.y <= query->high.y);
1386 *recheck = false;
1387 }
1388 break;
1389 case PolygonStrategyNumberGroup:
1390 {
1391 POLYGON *query = PG_GETARG_POLYGON_P(1);
1392
1393 result = DatumGetBool(DirectFunctionCall5(
1394 gist_poly_consistent,
1395 PointerGetDatum(entry),
1396 PolygonPGetDatum(query),
1397 Int16GetDatum(RTOverlapStrategyNumber),
1398 0, PointerGetDatum(recheck)));
1399
1400 if (GIST_LEAF(entry) && result)
1401 {
1402 /*
1403 * We are on leaf page and quick check shows overlapping
1404 * of polygon's bounding box and point
1405 */
1406 BOX *box = DatumGetBoxP(entry->key);
1407
1408 Assert(box->high.x == box->low.x
1409 && box->high.y == box->low.y);
1410 result = DatumGetBool(DirectFunctionCall2(
1411 poly_contain_pt,
1412 PolygonPGetDatum(query),
1413 PointPGetDatum(&box->high)));
1414 *recheck = false;
1415 }
1416 }
1417 break;
1418 case CircleStrategyNumberGroup:
1419 {
1420 CIRCLE *query = PG_GETARG_CIRCLE_P(1);
1421
1422 result = DatumGetBool(DirectFunctionCall5(
1423 gist_circle_consistent,
1424 PointerGetDatum(entry),
1425 CirclePGetDatum(query),
1426 Int16GetDatum(RTOverlapStrategyNumber),
1427 0, PointerGetDatum(recheck)));
1428
1429 if (GIST_LEAF(entry) && result)
1430 {
1431 /*
1432 * We are on leaf page and quick check shows overlapping
1433 * of polygon's bounding box and point
1434 */
1435 BOX *box = DatumGetBoxP(entry->key);
1436
1437 Assert(box->high.x == box->low.x
1438 && box->high.y == box->low.y);
1439 result = DatumGetBool(DirectFunctionCall2(
1440 circle_contain_pt,
1441 CirclePGetDatum(query),
1442 PointPGetDatum(&box->high)));
1443 *recheck = false;
1444 }
1445 }
1446 break;
1447 default:
1448 elog(ERROR, "unrecognized strategy number: %d", strategy);
1449 result = false; /* keep compiler quiet */
1450 break;
1451 }
1452
1453 PG_RETURN_BOOL(result);
1454 }
1455
1456 Datum
gist_point_distance(PG_FUNCTION_ARGS)1457 gist_point_distance(PG_FUNCTION_ARGS)
1458 {
1459 GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
1460 StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2);
1461 double distance;
1462 StrategyNumber strategyGroup = strategy / GeoStrategyNumberOffset;
1463
1464 switch (strategyGroup)
1465 {
1466 case PointStrategyNumberGroup:
1467 distance = computeDistance(GIST_LEAF(entry),
1468 DatumGetBoxP(entry->key),
1469 PG_GETARG_POINT_P(1));
1470 break;
1471 default:
1472 elog(ERROR, "unrecognized strategy number: %d", strategy);
1473 distance = 0.0; /* keep compiler quiet */
1474 break;
1475 }
1476
1477 PG_RETURN_FLOAT8(distance);
1478 }
1479
1480 /*
1481 * The inexact GiST distance method for geometric types that store bounding
1482 * boxes.
1483 *
1484 * Compute lossy distance from point to index entries. The result is inexact
1485 * because index entries are bounding boxes, not the exact shapes of the
1486 * indexed geometric types. We use distance from point to MBR of index entry.
1487 * This is a lower bound estimate of distance from point to indexed geometric
1488 * type.
1489 */
1490 static double
gist_bbox_distance(GISTENTRY * entry,Datum query,StrategyNumber strategy,bool * recheck)1491 gist_bbox_distance(GISTENTRY *entry, Datum query,
1492 StrategyNumber strategy, bool *recheck)
1493 {
1494 double distance;
1495 StrategyNumber strategyGroup = strategy / GeoStrategyNumberOffset;
1496
1497 /* Bounding box distance is always inexact. */
1498 *recheck = true;
1499
1500 switch (strategyGroup)
1501 {
1502 case PointStrategyNumberGroup:
1503 distance = computeDistance(false,
1504 DatumGetBoxP(entry->key),
1505 DatumGetPointP(query));
1506 break;
1507 default:
1508 elog(ERROR, "unrecognized strategy number: %d", strategy);
1509 distance = 0.0; /* keep compiler quiet */
1510 }
1511
1512 return distance;
1513 }
1514
1515 Datum
gist_circle_distance(PG_FUNCTION_ARGS)1516 gist_circle_distance(PG_FUNCTION_ARGS)
1517 {
1518 GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
1519 Datum query = PG_GETARG_DATUM(1);
1520 StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2);
1521
1522 /* Oid subtype = PG_GETARG_OID(3); */
1523 bool *recheck = (bool *) PG_GETARG_POINTER(4);
1524 double distance;
1525
1526 distance = gist_bbox_distance(entry, query, strategy, recheck);
1527
1528 PG_RETURN_FLOAT8(distance);
1529 }
1530
1531 Datum
gist_poly_distance(PG_FUNCTION_ARGS)1532 gist_poly_distance(PG_FUNCTION_ARGS)
1533 {
1534 GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
1535 Datum query = PG_GETARG_DATUM(1);
1536 StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2);
1537
1538 /* Oid subtype = PG_GETARG_OID(3); */
1539 bool *recheck = (bool *) PG_GETARG_POINTER(4);
1540 double distance;
1541
1542 distance = gist_bbox_distance(entry, query, strategy, recheck);
1543
1544 PG_RETURN_FLOAT8(distance);
1545 }
1546