1 /*-------------------------------------------------------------------------
4 * Support procedures for GiSTs over 2-D objects (boxes, polygons, circles,
7 * This gives R-tree behavior, with Guttman's poly-time split algorithm.
10 * Portions Copyright (c) 1996-2015, PostgreSQL Global Development Group
11 * Portions Copyright (c) 1994, Regents of the University of California
14 * src/backend/access/gist/gistproc.c
16 *-------------------------------------------------------------------------
20 #include "access/gist.h"
21 #include "access/skey.h"
22 #include "utils/geo_decls.h"
25 static bool gist_box_leaf_consistent(BOX *key, BOX *query,
26 StrategyNumber strategy);
27 static double size_box(BOX *box);
28 static bool rtree_internal_consistent(BOX *key, BOX *query,
29 StrategyNumber strategy);
31 /* Minimum accepted ratio of split */
32 #define LIMIT_RATIO 0.3
35 /**************************************************
37 **************************************************/
40 * Calculates union of two boxes, a and b. The result is stored in *n.
43 rt_box_union(BOX *n, BOX *a, BOX *b)
45 n->high.x = Max(a->high.x, b->high.x);
46 n->high.y = Max(a->high.y, b->high.y);
47 n->low.x = Min(a->low.x, b->low.x);
48 n->low.y = Min(a->low.y, b->low.y);
52 * The GiST Consistent method for boxes
54 * Should return false if for all data items x below entry,
55 * the predicate x op query must be FALSE, where op is the oper
56 * corresponding to strategy in the pg_amop table.
59 gist_box_consistent(PG_FUNCTION_ARGS)
61 GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
62 BOX *query = PG_GETARG_BOX_P(1);
63 StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2);
65 /* Oid subtype = PG_GETARG_OID(3); */
66 bool *recheck = (bool *) PG_GETARG_POINTER(4);
68 /* All cases served by this function are exact */
71 if (DatumGetBoxP(entry->key) == NULL || query == NULL)
72 PG_RETURN_BOOL(FALSE);
75 * if entry is not leaf, use rtree_internal_consistent, else use
76 * gist_box_leaf_consistent
79 PG_RETURN_BOOL(gist_box_leaf_consistent(DatumGetBoxP(entry->key),
83 PG_RETURN_BOOL(rtree_internal_consistent(DatumGetBoxP(entry->key),
89 adjustBox(BOX *b, BOX *addon)
91 if (b->high.x < addon->high.x)
92 b->high.x = addon->high.x;
93 if (b->low.x > addon->low.x)
94 b->low.x = addon->low.x;
95 if (b->high.y < addon->high.y)
96 b->high.y = addon->high.y;
97 if (b->low.y > addon->low.y)
98 b->low.y = addon->low.y;
102 * The GiST Union method for boxes
104 * returns the minimal bounding box that encloses all the entries in entryvec
107 gist_box_union(PG_FUNCTION_ARGS)
109 GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
110 int *sizep = (int *) PG_GETARG_POINTER(1);
116 numranges = entryvec->n;
117 pageunion = (BOX *) palloc(sizeof(BOX));
118 cur = DatumGetBoxP(entryvec->vector[0].key);
119 memcpy((void *) pageunion, (void *) cur, sizeof(BOX));
121 for (i = 1; i < numranges; i++)
123 cur = DatumGetBoxP(entryvec->vector[i].key);
124 adjustBox(pageunion, cur);
126 *sizep = sizeof(BOX);
128 PG_RETURN_POINTER(pageunion);
132 * GiST Compress methods for boxes
134 * do not do anything.
137 gist_box_compress(PG_FUNCTION_ARGS)
139 PG_RETURN_POINTER(PG_GETARG_POINTER(0));
143 * GiST DeCompress method for boxes (also used for points, polygons
146 * do not do anything --- we just use the stored box as is.
149 gist_box_decompress(PG_FUNCTION_ARGS)
151 PG_RETURN_POINTER(PG_GETARG_POINTER(0));
155 * The GiST Penalty method for boxes (also used for points)
157 * As in the R-tree paper, we use change in area as our penalty metric
160 gist_box_penalty(PG_FUNCTION_ARGS)
162 GISTENTRY *origentry = (GISTENTRY *) PG_GETARG_POINTER(0);
163 GISTENTRY *newentry = (GISTENTRY *) PG_GETARG_POINTER(1);
164 float *result = (float *) PG_GETARG_POINTER(2);
165 BOX *origbox = DatumGetBoxP(origentry->key);
166 BOX *newbox = DatumGetBoxP(newentry->key);
169 rt_box_union(&unionbox, origbox, newbox);
170 *result = (float) (size_box(&unionbox) - size_box(origbox));
171 PG_RETURN_POINTER(result);
175 * Trivial split: half of entries will be placed on one page
176 * and another half - to another
179 fallbackSplit(GistEntryVector *entryvec, GIST_SPLITVEC *v)
187 maxoff = entryvec->n - 1;
189 nbytes = (maxoff + 2) * sizeof(OffsetNumber);
190 v->spl_left = (OffsetNumber *) palloc(nbytes);
191 v->spl_right = (OffsetNumber *) palloc(nbytes);
192 v->spl_nleft = v->spl_nright = 0;
194 for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
196 BOX *cur = DatumGetBoxP(entryvec->vector[i].key);
198 if (i <= (maxoff - FirstOffsetNumber + 1) / 2)
200 v->spl_left[v->spl_nleft] = i;
203 unionL = (BOX *) palloc(sizeof(BOX));
207 adjustBox(unionL, cur);
213 v->spl_right[v->spl_nright] = i;
216 unionR = (BOX *) palloc(sizeof(BOX));
220 adjustBox(unionR, cur);
226 v->spl_ldatum = BoxPGetDatum(unionL);
227 v->spl_rdatum = BoxPGetDatum(unionR);
231 * Represents information about an entry that can be placed to either group
232 * without affecting overlap over selected axis ("common entry").
236 /* Index of entry in the initial array */
238 /* Delta between penalties of entry insertion into different groups */
243 * Context for g_box_consider_split. Contains information about currently
244 * selected split and some general information.
248 int entriesCount; /* total number of entries being split */
249 BOX boundingBox; /* minimum bounding box across all entries */
251 /* Information about currently selected split follows */
253 bool first; /* true if no split was selected yet */
255 double leftUpper; /* upper bound of left interval */
256 double rightLower; /* lower bound of right interval */
260 int dim; /* axis of this split */
261 double range; /* width of general MBR projection to the
263 } ConsiderSplitContext;
266 * Interval represents projection of box to axis.
275 * Interval comparison function by lower bound of the interval;
278 interval_cmp_lower(const void *i1, const void *i2)
280 double lower1 = ((const SplitInterval *) i1)->lower,
281 lower2 = ((const SplitInterval *) i2)->lower;
285 else if (lower1 > lower2)
292 * Interval comparison function by upper bound of the interval;
295 interval_cmp_upper(const void *i1, const void *i2)
297 double upper1 = ((const SplitInterval *) i1)->upper,
298 upper2 = ((const SplitInterval *) i2)->upper;
302 else if (upper1 > upper2)
309 * Replace negative value with zero.
312 non_negative(float val)
321 * Consider replacement of currently selected split with the better one.
324 g_box_consider_split(ConsiderSplitContext *context, int dimNum,
325 double rightLower, int minLeftCount,
326 double leftUpper, int maxLeftCount)
335 * Calculate entries distribution ratio assuming most uniform distribution
338 if (minLeftCount >= (context->entriesCount + 1) / 2)
340 leftCount = minLeftCount;
344 if (maxLeftCount <= context->entriesCount / 2)
345 leftCount = maxLeftCount;
347 leftCount = context->entriesCount / 2;
349 rightCount = context->entriesCount - leftCount;
352 * Ratio of split - quotient between size of lesser group and total
355 ratio = ((float4) Min(leftCount, rightCount)) /
356 ((float4) context->entriesCount);
358 if (ratio > LIMIT_RATIO)
360 bool selectthis = false;
363 * The ratio is acceptable, so compare current split with previously
364 * selected one. Between splits of one dimension we search for minimal
365 * overlap (allowing negative values) and minimal ration (between same
366 * overlaps. We switch dimension if find less overlap (non-negative)
367 * or less range with same overlap.
370 range = context->boundingBox.high.x - context->boundingBox.low.x;
372 range = context->boundingBox.high.y - context->boundingBox.low.y;
374 overlap = (leftUpper - rightLower) / range;
376 /* If there is no previous selection, select this */
379 else if (context->dim == dimNum)
382 * Within the same dimension, choose the new split if it has a
383 * smaller overlap, or same overlap but better ratio.
385 if (overlap < context->overlap ||
386 (overlap == context->overlap && ratio > context->ratio))
392 * Across dimensions, choose the new split if it has a smaller
393 * *non-negative* overlap, or same *non-negative* overlap but
394 * bigger range. This condition differs from the one described in
395 * the article. On the datasets where leaf MBRs don't overlap
396 * themselves, non-overlapping splits (i.e. splits which have zero
397 * *non-negative* overlap) are frequently possible. In this case
398 * splits tends to be along one dimension, because most distant
399 * non-overlapping splits (i.e. having lowest negative overlap)
400 * appears to be in the same dimension as in the previous split.
401 * Therefore MBRs appear to be very prolonged along another
402 * dimension, which leads to bad search performance. Using range
403 * as the second split criteria makes MBRs more quadratic. Using
404 * *non-negative* overlap instead of overlap as the first split
405 * criteria gives to range criteria a chance to matter, because
406 * non-overlapping splits are equivalent in this criteria.
408 if (non_negative(overlap) < non_negative(context->overlap) ||
409 (range > context->range &&
410 non_negative(overlap) <= non_negative(context->overlap)))
416 /* save information about selected split */
417 context->first = false;
418 context->ratio = ratio;
419 context->range = range;
420 context->overlap = overlap;
421 context->rightLower = rightLower;
422 context->leftUpper = leftUpper;
423 context->dim = dimNum;
429 * Return increase of original BOX area by new BOX area insertion.
432 box_penalty(BOX *original, BOX *new)
437 union_width = Max(original->high.x, new->high.x) -
438 Min(original->low.x, new->low.x);
439 union_height = Max(original->high.y, new->high.y) -
440 Min(original->low.y, new->low.y);
441 return union_width * union_height - (original->high.x - original->low.x) *
442 (original->high.y - original->low.y);
446 * Compare common entries by their deltas.
449 common_entry_cmp(const void *i1, const void *i2)
451 double delta1 = ((const CommonEntry *) i1)->delta,
452 delta2 = ((const CommonEntry *) i2)->delta;
456 else if (delta1 > delta2)
463 * --------------------------------------------------------------------------
464 * Double sorting split algorithm. This is used for both boxes and points.
466 * The algorithm finds split of boxes by considering splits along each axis.
467 * Each entry is first projected as an interval on the X-axis, and different
468 * ways to split the intervals into two groups are considered, trying to
469 * minimize the overlap of the groups. Then the same is repeated for the
470 * Y-axis, and the overall best split is chosen. The quality of a split is
471 * determined by overlap along that axis and some other criteria (see
472 * g_box_consider_split).
474 * After that, all the entries are divided into three groups:
476 * 1) Entries which should be placed to the left group
477 * 2) Entries which should be placed to the right group
478 * 3) "Common entries" which can be placed to any of groups without affecting
479 * of overlap along selected axis.
481 * The common entries are distributed by minimizing penalty.
484 * "A new double sorting-based node splitting algorithm for R-tree", A. Korotkov
485 * http://syrcose.ispras.ru/2011/files/SYRCoSE2011_Proceedings.pdf#page=36
486 * --------------------------------------------------------------------------
489 gist_box_picksplit(PG_FUNCTION_ARGS)
491 GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
492 GIST_SPLITVEC *v = (GIST_SPLITVEC *) PG_GETARG_POINTER(1);
495 ConsiderSplitContext context;
501 SplitInterval *intervalsLower,
503 CommonEntry *commonEntries;
506 memset(&context, 0, sizeof(ConsiderSplitContext));
508 maxoff = entryvec->n - 1;
509 nentries = context.entriesCount = maxoff - FirstOffsetNumber + 1;
511 /* Allocate arrays for intervals along axes */
512 intervalsLower = (SplitInterval *) palloc(nentries * sizeof(SplitInterval));
513 intervalsUpper = (SplitInterval *) palloc(nentries * sizeof(SplitInterval));
516 * Calculate the overall minimum bounding box over all the entries.
518 for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
520 box = DatumGetBoxP(entryvec->vector[i].key);
521 if (i == FirstOffsetNumber)
522 context.boundingBox = *box;
524 adjustBox(&context.boundingBox, box);
528 * Iterate over axes for optimal split searching.
530 context.first = true; /* nothing selected yet */
531 for (dim = 0; dim < 2; dim++)
538 /* Project each entry as an interval on the selected axis. */
539 for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
541 box = DatumGetBoxP(entryvec->vector[i].key);
544 intervalsLower[i - FirstOffsetNumber].lower = box->low.x;
545 intervalsLower[i - FirstOffsetNumber].upper = box->high.x;
549 intervalsLower[i - FirstOffsetNumber].lower = box->low.y;
550 intervalsLower[i - FirstOffsetNumber].upper = box->high.y;
555 * Make two arrays of intervals: one sorted by lower bound and another
556 * sorted by upper bound.
558 memcpy(intervalsUpper, intervalsLower,
559 sizeof(SplitInterval) * nentries);
560 qsort(intervalsLower, nentries, sizeof(SplitInterval),
562 qsort(intervalsUpper, nentries, sizeof(SplitInterval),
566 * The goal is to form a left and right interval, so that every entry
567 * interval is contained by either left or right interval (or both).
569 * For example, with the intervals (0,1), (1,3), (2,3), (2,4):
577 * The left and right intervals are of the form (0,a) and (b,4).
578 * We first consider splits where b is the lower bound of an entry.
579 * We iterate through all entries, and for each b, calculate the
580 * smallest possible a. Then we consider splits where a is the
581 * uppper bound of an entry, and for each a, calculate the greatest
584 * In the above example, the first loop would consider splits:
589 * And the second loop:
596 * Iterate over lower bound of right group, finding smallest possible
597 * upper bound of left group.
601 rightLower = intervalsLower[i1].lower;
602 leftUpper = intervalsUpper[i2].lower;
606 * Find next lower bound of right group.
608 while (i1 < nentries && rightLower == intervalsLower[i1].lower)
610 leftUpper = Max(leftUpper, intervalsLower[i1].upper);
615 rightLower = intervalsLower[i1].lower;
618 * Find count of intervals which anyway should be placed to the
621 while (i2 < nentries && intervalsUpper[i2].upper <= leftUpper)
625 * Consider found split.
627 g_box_consider_split(&context, dim, rightLower, i1, leftUpper, i2);
631 * Iterate over upper bound of left group finding greates possible
632 * lower bound of right group.
636 rightLower = intervalsLower[i1].upper;
637 leftUpper = intervalsUpper[i2].upper;
641 * Find next upper bound of left group.
643 while (i2 >= 0 && leftUpper == intervalsUpper[i2].upper)
645 rightLower = Min(rightLower, intervalsUpper[i2].lower);
650 leftUpper = intervalsUpper[i2].upper;
653 * Find count of intervals which anyway should be placed to the
656 while (i1 >= 0 && intervalsLower[i1].lower >= rightLower)
660 * Consider found split.
662 g_box_consider_split(&context, dim,
663 rightLower, i1 + 1, leftUpper, i2 + 1);
668 * If we failed to find any acceptable splits, use trivial split.
672 fallbackSplit(entryvec, v);
673 PG_RETURN_POINTER(v);
677 * Ok, we have now selected the split across one axis.
679 * While considering the splits, we already determined that there will be
680 * enough entries in both groups to reach the desired ratio, but we did
681 * not memorize which entries go to which group. So determine that now.
684 /* Allocate vectors for results */
685 v->spl_left = (OffsetNumber *) palloc(nentries * sizeof(OffsetNumber));
686 v->spl_right = (OffsetNumber *) palloc(nentries * sizeof(OffsetNumber));
690 /* Allocate bounding boxes of left and right groups */
691 leftBox = palloc0(sizeof(BOX));
692 rightBox = palloc0(sizeof(BOX));
695 * Allocate an array for "common entries" - entries which can be placed to
696 * either group without affecting overlap along selected axis.
698 commonEntriesCount = 0;
699 commonEntries = (CommonEntry *) palloc(nentries * sizeof(CommonEntry));
701 /* Helper macros to place an entry in the left or right group */
702 #define PLACE_LEFT(box, off) \
704 if (v->spl_nleft > 0) \
705 adjustBox(leftBox, box); \
708 v->spl_left[v->spl_nleft++] = off; \
711 #define PLACE_RIGHT(box, off) \
713 if (v->spl_nright > 0) \
714 adjustBox(rightBox, box); \
716 *rightBox = *(box); \
717 v->spl_right[v->spl_nright++] = off; \
721 * Distribute entries which can be distributed unambiguously, and collect
724 for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
730 * Get upper and lower bounds along selected axis.
732 box = DatumGetBoxP(entryvec->vector[i].key);
733 if (context.dim == 0)
744 if (upper <= context.leftUpper)
746 /* Fits to the left group */
747 if (lower >= context.rightLower)
749 /* Fits also to the right group, so "common entry" */
750 commonEntries[commonEntriesCount++].index = i;
754 /* Doesn't fit to the right group, so join to the left group */
761 * Each entry should fit on either left or right group. Since this
762 * entry didn't fit on the left group, it better fit in the right
765 Assert(lower >= context.rightLower);
767 /* Doesn't fit to the left group, so join to the right group */
773 * Distribute "common entries", if any.
775 if (commonEntriesCount > 0)
778 * Calculate minimum number of entries that must be placed in both
779 * groups, to reach LIMIT_RATIO.
781 int m = ceil(LIMIT_RATIO * (double) nentries);
784 * Calculate delta between penalties of join "common entries" to
787 for (i = 0; i < commonEntriesCount; i++)
789 box = DatumGetBoxP(entryvec->vector[commonEntries[i].index].key);
790 commonEntries[i].delta = Abs(box_penalty(leftBox, box) -
791 box_penalty(rightBox, box));
795 * Sort "common entries" by calculated deltas in order to distribute
796 * the most ambiguous entries first.
798 qsort(commonEntries, commonEntriesCount, sizeof(CommonEntry), common_entry_cmp);
801 * Distribute "common entries" between groups.
803 for (i = 0; i < commonEntriesCount; i++)
805 box = DatumGetBoxP(entryvec->vector[commonEntries[i].index].key);
808 * Check if we have to place this entry in either group to achieve
811 if (v->spl_nleft + (commonEntriesCount - i) <= m)
812 PLACE_LEFT(box, commonEntries[i].index);
813 else if (v->spl_nright + (commonEntriesCount - i) <= m)
814 PLACE_RIGHT(box, commonEntries[i].index);
817 /* Otherwise select the group by minimal penalty */
818 if (box_penalty(leftBox, box) < box_penalty(rightBox, box))
819 PLACE_LEFT(box, commonEntries[i].index);
821 PLACE_RIGHT(box, commonEntries[i].index);
826 v->spl_ldatum = PointerGetDatum(leftBox);
827 v->spl_rdatum = PointerGetDatum(rightBox);
828 PG_RETURN_POINTER(v);
834 * This is used for boxes, points, circles, and polygons, all of which store
835 * boxes as GiST index entries.
837 * Returns true only when boxes are exactly the same. We can't use fuzzy
838 * comparisons here without breaking index consistency; therefore, this isn't
839 * equivalent to box_same().
842 gist_box_same(PG_FUNCTION_ARGS)
844 BOX *b1 = PG_GETARG_BOX_P(0);
845 BOX *b2 = PG_GETARG_BOX_P(1);
846 bool *result = (bool *) PG_GETARG_POINTER(2);
849 *result = (b1->low.x == b2->low.x && b1->low.y == b2->low.y &&
850 b1->high.x == b2->high.x && b1->high.y == b2->high.y);
852 *result = (b1 == NULL && b2 == NULL);
853 PG_RETURN_POINTER(result);
857 * Leaf-level consistency for boxes: just apply the query operator
860 gist_box_leaf_consistent(BOX *key, BOX *query, StrategyNumber strategy)
866 case RTLeftStrategyNumber:
867 retval = DatumGetBool(DirectFunctionCall2(box_left,
868 PointerGetDatum(key),
869 PointerGetDatum(query)));
871 case RTOverLeftStrategyNumber:
872 retval = DatumGetBool(DirectFunctionCall2(box_overleft,
873 PointerGetDatum(key),
874 PointerGetDatum(query)));
876 case RTOverlapStrategyNumber:
877 retval = DatumGetBool(DirectFunctionCall2(box_overlap,
878 PointerGetDatum(key),
879 PointerGetDatum(query)));
881 case RTOverRightStrategyNumber:
882 retval = DatumGetBool(DirectFunctionCall2(box_overright,
883 PointerGetDatum(key),
884 PointerGetDatum(query)));
886 case RTRightStrategyNumber:
887 retval = DatumGetBool(DirectFunctionCall2(box_right,
888 PointerGetDatum(key),
889 PointerGetDatum(query)));
891 case RTSameStrategyNumber:
892 retval = DatumGetBool(DirectFunctionCall2(box_same,
893 PointerGetDatum(key),
894 PointerGetDatum(query)));
896 case RTContainsStrategyNumber:
897 case RTOldContainsStrategyNumber:
898 retval = DatumGetBool(DirectFunctionCall2(box_contain,
899 PointerGetDatum(key),
900 PointerGetDatum(query)));
902 case RTContainedByStrategyNumber:
903 case RTOldContainedByStrategyNumber:
904 retval = DatumGetBool(DirectFunctionCall2(box_contained,
905 PointerGetDatum(key),
906 PointerGetDatum(query)));
908 case RTOverBelowStrategyNumber:
909 retval = DatumGetBool(DirectFunctionCall2(box_overbelow,
910 PointerGetDatum(key),
911 PointerGetDatum(query)));
913 case RTBelowStrategyNumber:
914 retval = DatumGetBool(DirectFunctionCall2(box_below,
915 PointerGetDatum(key),
916 PointerGetDatum(query)));
918 case RTAboveStrategyNumber:
919 retval = DatumGetBool(DirectFunctionCall2(box_above,
920 PointerGetDatum(key),
921 PointerGetDatum(query)));
923 case RTOverAboveStrategyNumber:
924 retval = DatumGetBool(DirectFunctionCall2(box_overabove,
925 PointerGetDatum(key),
926 PointerGetDatum(query)));
929 elog(ERROR, "unrecognized strategy number: %d", strategy);
930 retval = false; /* keep compiler quiet */
939 if (box->high.x <= box->low.x || box->high.y <= box->low.y)
941 return (box->high.x - box->low.x) * (box->high.y - box->low.y);
944 /*****************************************
945 * Common rtree functions (for boxes, polygons, and circles)
946 *****************************************/
949 * Internal-page consistency for all these types
951 * We can use the same function since all types use bounding boxes as the
952 * internal-page representation.
955 rtree_internal_consistent(BOX *key, BOX *query, StrategyNumber strategy)
961 case RTLeftStrategyNumber:
962 retval = !DatumGetBool(DirectFunctionCall2(box_overright,
963 PointerGetDatum(key),
964 PointerGetDatum(query)));
966 case RTOverLeftStrategyNumber:
967 retval = !DatumGetBool(DirectFunctionCall2(box_right,
968 PointerGetDatum(key),
969 PointerGetDatum(query)));
971 case RTOverlapStrategyNumber:
972 retval = DatumGetBool(DirectFunctionCall2(box_overlap,
973 PointerGetDatum(key),
974 PointerGetDatum(query)));
976 case RTOverRightStrategyNumber:
977 retval = !DatumGetBool(DirectFunctionCall2(box_left,
978 PointerGetDatum(key),
979 PointerGetDatum(query)));
981 case RTRightStrategyNumber:
982 retval = !DatumGetBool(DirectFunctionCall2(box_overleft,
983 PointerGetDatum(key),
984 PointerGetDatum(query)));
986 case RTSameStrategyNumber:
987 case RTContainsStrategyNumber:
988 case RTOldContainsStrategyNumber:
989 retval = DatumGetBool(DirectFunctionCall2(box_contain,
990 PointerGetDatum(key),
991 PointerGetDatum(query)));
993 case RTContainedByStrategyNumber:
994 case RTOldContainedByStrategyNumber:
995 retval = DatumGetBool(DirectFunctionCall2(box_overlap,
996 PointerGetDatum(key),
997 PointerGetDatum(query)));
999 case RTOverBelowStrategyNumber:
1000 retval = !DatumGetBool(DirectFunctionCall2(box_above,
1001 PointerGetDatum(key),
1002 PointerGetDatum(query)));
1004 case RTBelowStrategyNumber:
1005 retval = !DatumGetBool(DirectFunctionCall2(box_overabove,
1006 PointerGetDatum(key),
1007 PointerGetDatum(query)));
1009 case RTAboveStrategyNumber:
1010 retval = !DatumGetBool(DirectFunctionCall2(box_overbelow,
1011 PointerGetDatum(key),
1012 PointerGetDatum(query)));
1014 case RTOverAboveStrategyNumber:
1015 retval = !DatumGetBool(DirectFunctionCall2(box_below,
1016 PointerGetDatum(key),
1017 PointerGetDatum(query)));
1020 elog(ERROR, "unrecognized strategy number: %d", strategy);
1021 retval = false; /* keep compiler quiet */
1027 /**************************************************
1029 **************************************************/
1032 * GiST compress for polygons: represent a polygon by its bounding box
1035 gist_poly_compress(PG_FUNCTION_ARGS)
1037 GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
1042 POLYGON *in = DatumGetPolygonP(entry->key);
1045 r = (BOX *) palloc(sizeof(BOX));
1046 memcpy((void *) r, (void *) &(in->boundbox), sizeof(BOX));
1048 retval = (GISTENTRY *) palloc(sizeof(GISTENTRY));
1049 gistentryinit(*retval, PointerGetDatum(r),
1050 entry->rel, entry->page,
1051 entry->offset, FALSE);
1055 PG_RETURN_POINTER(retval);
1059 * The GiST Consistent method for polygons
1062 gist_poly_consistent(PG_FUNCTION_ARGS)
1064 GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
1065 POLYGON *query = PG_GETARG_POLYGON_P(1);
1066 StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2);
1068 /* Oid subtype = PG_GETARG_OID(3); */
1069 bool *recheck = (bool *) PG_GETARG_POINTER(4);
1072 /* All cases served by this function are inexact */
1075 if (DatumGetBoxP(entry->key) == NULL || query == NULL)
1076 PG_RETURN_BOOL(FALSE);
1079 * Since the operators require recheck anyway, we can just use
1080 * rtree_internal_consistent even at leaf nodes. (This works in part
1081 * because the index entries are bounding boxes not polygons.)
1083 result = rtree_internal_consistent(DatumGetBoxP(entry->key),
1084 &(query->boundbox), strategy);
1086 /* Avoid memory leak if supplied poly is toasted */
1087 PG_FREE_IF_COPY(query, 1);
1089 PG_RETURN_BOOL(result);
1092 /**************************************************
1094 **************************************************/
1097 * GiST compress for circles: represent a circle by its bounding box
1100 gist_circle_compress(PG_FUNCTION_ARGS)
1102 GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
1107 CIRCLE *in = DatumGetCircleP(entry->key);
1110 r = (BOX *) palloc(sizeof(BOX));
1111 r->high.x = in->center.x + in->radius;
1112 r->low.x = in->center.x - in->radius;
1113 r->high.y = in->center.y + in->radius;
1114 r->low.y = in->center.y - in->radius;
1116 retval = (GISTENTRY *) palloc(sizeof(GISTENTRY));
1117 gistentryinit(*retval, PointerGetDatum(r),
1118 entry->rel, entry->page,
1119 entry->offset, FALSE);
1123 PG_RETURN_POINTER(retval);
1127 * The GiST Consistent method for circles
1130 gist_circle_consistent(PG_FUNCTION_ARGS)
1132 GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
1133 CIRCLE *query = PG_GETARG_CIRCLE_P(1);
1134 StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2);
1136 /* Oid subtype = PG_GETARG_OID(3); */
1137 bool *recheck = (bool *) PG_GETARG_POINTER(4);
1141 /* All cases served by this function are inexact */
1144 if (DatumGetBoxP(entry->key) == NULL || query == NULL)
1145 PG_RETURN_BOOL(FALSE);
1148 * Since the operators require recheck anyway, we can just use
1149 * rtree_internal_consistent even at leaf nodes. (This works in part
1150 * because the index entries are bounding boxes not circles.)
1152 bbox.high.x = query->center.x + query->radius;
1153 bbox.low.x = query->center.x - query->radius;
1154 bbox.high.y = query->center.y + query->radius;
1155 bbox.low.y = query->center.y - query->radius;
1157 result = rtree_internal_consistent(DatumGetBoxP(entry->key),
1160 PG_RETURN_BOOL(result);
1163 /**************************************************
1165 **************************************************/
1168 gist_point_compress(PG_FUNCTION_ARGS)
1170 GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
1172 if (entry->leafkey) /* Point, actually */
1174 BOX *box = palloc(sizeof(BOX));
1175 Point *point = DatumGetPointP(entry->key);
1176 GISTENTRY *retval = palloc(sizeof(GISTENTRY));
1178 box->high = box->low = *point;
1180 gistentryinit(*retval, BoxPGetDatum(box),
1181 entry->rel, entry->page, entry->offset, FALSE);
1183 PG_RETURN_POINTER(retval);
1186 PG_RETURN_POINTER(entry);
1189 #define point_point_distance(p1,p2) \
1190 DatumGetFloat8(DirectFunctionCall2(point_distance, \
1191 PointPGetDatum(p1), PointPGetDatum(p2)))
1194 computeDistance(bool isLeaf, BOX *box, Point *point)
1196 double result = 0.0;
1200 /* simple point to point distance */
1201 result = point_point_distance(point, &box->low);
1203 else if (point->x <= box->high.x && point->x >= box->low.x &&
1204 point->y <= box->high.y && point->y >= box->low.y)
1206 /* point inside the box */
1209 else if (point->x <= box->high.x && point->x >= box->low.x)
1211 /* point is over or below box */
1212 Assert(box->low.y <= box->high.y);
1213 if (point->y > box->high.y)
1214 result = point->y - box->high.y;
1215 else if (point->y < box->low.y)
1216 result = box->low.y - point->y;
1218 elog(ERROR, "inconsistent point values");
1220 else if (point->y <= box->high.y && point->y >= box->low.y)
1222 /* point is to left or right of box */
1223 Assert(box->low.x <= box->high.x);
1224 if (point->x > box->high.x)
1225 result = point->x - box->high.x;
1226 else if (point->x < box->low.x)
1227 result = box->low.x - point->x;
1229 elog(ERROR, "inconsistent point values");
1233 /* closest point will be a vertex */
1237 result = point_point_distance(point, &box->low);
1239 subresult = point_point_distance(point, &box->high);
1240 if (result > subresult)
1245 subresult = point_point_distance(point, &p);
1246 if (result > subresult)
1251 subresult = point_point_distance(point, &p);
1252 if (result > subresult)
1260 gist_point_consistent_internal(StrategyNumber strategy,
1261 bool isLeaf, BOX *key, Point *query)
1263 bool result = false;
1267 case RTLeftStrategyNumber:
1268 result = FPlt(key->low.x, query->x);
1270 case RTRightStrategyNumber:
1271 result = FPgt(key->high.x, query->x);
1273 case RTAboveStrategyNumber:
1274 result = FPgt(key->high.y, query->y);
1276 case RTBelowStrategyNumber:
1277 result = FPlt(key->low.y, query->y);
1279 case RTSameStrategyNumber:
1282 /* key.high must equal key.low, so we can disregard it */
1283 result = (FPeq(key->low.x, query->x) &&
1284 FPeq(key->low.y, query->y));
1288 result = (FPle(query->x, key->high.x) &&
1289 FPge(query->x, key->low.x) &&
1290 FPle(query->y, key->high.y) &&
1291 FPge(query->y, key->low.y));
1295 elog(ERROR, "unrecognized strategy number: %d", strategy);
1296 result = false; /* keep compiler quiet */
1303 #define GeoStrategyNumberOffset 20
1304 #define PointStrategyNumberGroup 0
1305 #define BoxStrategyNumberGroup 1
1306 #define PolygonStrategyNumberGroup 2
1307 #define CircleStrategyNumberGroup 3
1310 gist_point_consistent(PG_FUNCTION_ARGS)
1312 GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
1313 StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2);
1314 bool *recheck = (bool *) PG_GETARG_POINTER(4);
1316 StrategyNumber strategyGroup = strategy / GeoStrategyNumberOffset;
1318 switch (strategyGroup)
1320 case PointStrategyNumberGroup:
1321 result = gist_point_consistent_internal(strategy % GeoStrategyNumberOffset,
1323 DatumGetBoxP(entry->key),
1324 PG_GETARG_POINT_P(1));
1327 case BoxStrategyNumberGroup:
1330 * The only operator in this group is point <@ box (on_pb), so
1331 * we needn't examine strategy again.
1333 * For historical reasons, on_pb uses exact rather than fuzzy
1334 * comparisons. We could use box_overlap when at an internal
1335 * page, but that would lead to possibly visiting child pages
1336 * uselessly, because box_overlap uses fuzzy comparisons.
1337 * Instead we write a non-fuzzy overlap test. The same code
1338 * will also serve for leaf-page tests, since leaf keys have
1344 query = PG_GETARG_BOX_P(1);
1345 key = DatumGetBoxP(entry->key);
1347 result = (key->high.x >= query->low.x &&
1348 key->low.x <= query->high.x &&
1349 key->high.y >= query->low.y &&
1350 key->low.y <= query->high.y);
1354 case PolygonStrategyNumberGroup:
1356 POLYGON *query = PG_GETARG_POLYGON_P(1);
1358 result = DatumGetBool(DirectFunctionCall5(
1359 gist_poly_consistent,
1360 PointerGetDatum(entry),
1361 PolygonPGetDatum(query),
1362 Int16GetDatum(RTOverlapStrategyNumber),
1363 0, PointerGetDatum(recheck)));
1365 if (GIST_LEAF(entry) && result)
1368 * We are on leaf page and quick check shows overlapping
1369 * of polygon's bounding box and point
1371 BOX *box = DatumGetBoxP(entry->key);
1373 Assert(box->high.x == box->low.x
1374 && box->high.y == box->low.y);
1375 result = DatumGetBool(DirectFunctionCall2(
1377 PolygonPGetDatum(query),
1378 PointPGetDatum(&box->high)));
1383 case CircleStrategyNumberGroup:
1385 CIRCLE *query = PG_GETARG_CIRCLE_P(1);
1387 result = DatumGetBool(DirectFunctionCall5(
1388 gist_circle_consistent,
1389 PointerGetDatum(entry),
1390 CirclePGetDatum(query),
1391 Int16GetDatum(RTOverlapStrategyNumber),
1392 0, PointerGetDatum(recheck)));
1394 if (GIST_LEAF(entry) && result)
1397 * We are on leaf page and quick check shows overlapping
1398 * of polygon's bounding box and point
1400 BOX *box = DatumGetBoxP(entry->key);
1402 Assert(box->high.x == box->low.x
1403 && box->high.y == box->low.y);
1404 result = DatumGetBool(DirectFunctionCall2(
1406 CirclePGetDatum(query),
1407 PointPGetDatum(&box->high)));
1413 elog(ERROR, "unrecognized strategy number: %d", strategy);
1414 result = false; /* keep compiler quiet */
1418 PG_RETURN_BOOL(result);
1422 gist_point_distance(PG_FUNCTION_ARGS)
1424 GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
1425 StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2);
1427 StrategyNumber strategyGroup = strategy / GeoStrategyNumberOffset;
1429 switch (strategyGroup)
1431 case PointStrategyNumberGroup:
1432 distance = computeDistance(GIST_LEAF(entry),
1433 DatumGetBoxP(entry->key),
1434 PG_GETARG_POINT_P(1));
1437 elog(ERROR, "unrecognized strategy number: %d", strategy);
1438 distance = 0.0; /* keep compiler quiet */
1442 PG_RETURN_FLOAT8(distance);