1 /*-------------------------------------------------------------------------
4 * Support procedures for GiSTs over 2-D objects (boxes, polygons, circles).
6 * This gives R-tree behavior, with Guttman's poly-time split algorithm.
9 * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
10 * Portions Copyright (c) 1994, Regents of the University of California
13 * src/backend/access/gist/gistproc.c
15 *-------------------------------------------------------------------------
19 #include "access/gist.h"
20 #include "access/skey.h"
21 #include "utils/geo_decls.h"
24 static bool gist_box_leaf_consistent(BOX *key, BOX *query,
25 StrategyNumber strategy);
26 static double size_box(Datum dbox);
27 static bool rtree_internal_consistent(BOX *key, BOX *query,
28 StrategyNumber strategy);
31 /**************************************************
33 **************************************************/
36 rt_box_union(PG_FUNCTION_ARGS)
38 BOX *a = PG_GETARG_BOX_P(0);
39 BOX *b = PG_GETARG_BOX_P(1);
42 n = (BOX *) palloc(sizeof(BOX));
44 n->high.x = Max(a->high.x, b->high.x);
45 n->high.y = Max(a->high.y, b->high.y);
46 n->low.x = Min(a->low.x, b->low.x);
47 n->low.y = Min(a->low.y, b->low.y);
53 rt_box_inter(PG_FUNCTION_ARGS)
55 BOX *a = PG_GETARG_BOX_P(0);
56 BOX *b = PG_GETARG_BOX_P(1);
59 n = (BOX *) palloc(sizeof(BOX));
61 n->high.x = Min(a->high.x, b->high.x);
62 n->high.y = Min(a->high.y, b->high.y);
63 n->low.x = Max(a->low.x, b->low.x);
64 n->low.y = Max(a->low.y, b->low.y);
66 if (n->high.x < n->low.x || n->high.y < n->low.y)
69 /* Indicate "no intersection" by returning NULL pointer */
77 * The GiST Consistent method for boxes
79 * Should return false if for all data items x below entry,
80 * the predicate x op query must be FALSE, where op is the oper
81 * corresponding to strategy in the pg_amop table.
84 gist_box_consistent(PG_FUNCTION_ARGS)
86 GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
87 BOX *query = PG_GETARG_BOX_P(1);
88 StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2);
90 /* Oid subtype = PG_GETARG_OID(3); */
91 bool *recheck = (bool *) PG_GETARG_POINTER(4);
93 /* All cases served by this function are exact */
96 if (DatumGetBoxP(entry->key) == NULL || query == NULL)
97 PG_RETURN_BOOL(FALSE);
100 * if entry is not leaf, use rtree_internal_consistent, else use
101 * gist_box_leaf_consistent
103 if (GIST_LEAF(entry))
104 PG_RETURN_BOOL(gist_box_leaf_consistent(DatumGetBoxP(entry->key),
108 PG_RETURN_BOOL(rtree_internal_consistent(DatumGetBoxP(entry->key),
114 adjustBox(BOX *b, BOX *addon)
116 if (b->high.x < addon->high.x)
117 b->high.x = addon->high.x;
118 if (b->low.x > addon->low.x)
119 b->low.x = addon->low.x;
120 if (b->high.y < addon->high.y)
121 b->high.y = addon->high.y;
122 if (b->low.y > addon->low.y)
123 b->low.y = addon->low.y;
127 * The GiST Union method for boxes
129 * returns the minimal bounding box that encloses all the entries in entryvec
132 gist_box_union(PG_FUNCTION_ARGS)
134 GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
135 int *sizep = (int *) PG_GETARG_POINTER(1);
141 numranges = entryvec->n;
142 pageunion = (BOX *) palloc(sizeof(BOX));
143 cur = DatumGetBoxP(entryvec->vector[0].key);
144 memcpy((void *) pageunion, (void *) cur, sizeof(BOX));
146 for (i = 1; i < numranges; i++)
148 cur = DatumGetBoxP(entryvec->vector[i].key);
149 adjustBox(pageunion, cur);
151 *sizep = sizeof(BOX);
153 PG_RETURN_POINTER(pageunion);
157 * GiST Compress methods for boxes
159 * do not do anything.
162 gist_box_compress(PG_FUNCTION_ARGS)
164 PG_RETURN_POINTER(PG_GETARG_POINTER(0));
168 * GiST DeCompress method for boxes (also used for points, polygons
171 * do not do anything --- we just use the stored box as is.
174 gist_box_decompress(PG_FUNCTION_ARGS)
176 PG_RETURN_POINTER(PG_GETARG_POINTER(0));
180 * The GiST Penalty method for boxes (also used for points)
182 * As in the R-tree paper, we use change in area as our penalty metric
185 gist_box_penalty(PG_FUNCTION_ARGS)
187 GISTENTRY *origentry = (GISTENTRY *) PG_GETARG_POINTER(0);
188 GISTENTRY *newentry = (GISTENTRY *) PG_GETARG_POINTER(1);
189 float *result = (float *) PG_GETARG_POINTER(2);
192 ud = DirectFunctionCall2(rt_box_union, origentry->key, newentry->key);
193 *result = (float) (size_box(ud) - size_box(origentry->key));
194 PG_RETURN_POINTER(result);
198 chooseLR(GIST_SPLITVEC *v,
199 OffsetNumber *list1, int nlist1, BOX *union1,
200 OffsetNumber *list2, int nlist2, BOX *union2)
202 bool firstToLeft = true;
204 if (v->spl_ldatum_exists || v->spl_rdatum_exists)
206 if (v->spl_ldatum_exists && v->spl_rdatum_exists)
215 adjustBox(&LRl, DatumGetBoxP(v->spl_ldatum));
216 adjustBox(&LRr, DatumGetBoxP(v->spl_rdatum));
217 adjustBox(&RLl, DatumGetBoxP(v->spl_ldatum));
218 adjustBox(&RLr, DatumGetBoxP(v->spl_rdatum));
220 sizeLR = size_box(DirectFunctionCall2(rt_box_inter, BoxPGetDatum(&LRl), BoxPGetDatum(&LRr)));
221 sizeRL = size_box(DirectFunctionCall2(rt_box_inter, BoxPGetDatum(&RLl), BoxPGetDatum(&RLr)));
234 gistentryinit(oldUnion, (v->spl_ldatum_exists) ? v->spl_ldatum : v->spl_rdatum,
235 NULL, NULL, InvalidOffsetNumber, FALSE);
237 gistentryinit(addon, BoxPGetDatum(union1), NULL, NULL, InvalidOffsetNumber, FALSE);
238 DirectFunctionCall3(gist_box_penalty, PointerGetDatum(&oldUnion), PointerGetDatum(&addon), PointerGetDatum(&p1));
239 gistentryinit(addon, BoxPGetDatum(union2), NULL, NULL, InvalidOffsetNumber, FALSE);
240 DirectFunctionCall3(gist_box_penalty, PointerGetDatum(&oldUnion), PointerGetDatum(&addon), PointerGetDatum(&p2));
242 if ((v->spl_ldatum_exists && p1 > p2) || (v->spl_rdatum_exists && p1 < p2))
250 v->spl_right = list2;
251 v->spl_nleft = nlist1;
252 v->spl_nright = nlist2;
253 if (v->spl_ldatum_exists)
254 adjustBox(union1, DatumGetBoxP(v->spl_ldatum));
255 v->spl_ldatum = BoxPGetDatum(union1);
256 if (v->spl_rdatum_exists)
257 adjustBox(union2, DatumGetBoxP(v->spl_rdatum));
258 v->spl_rdatum = BoxPGetDatum(union2);
263 v->spl_right = list1;
264 v->spl_nleft = nlist2;
265 v->spl_nright = nlist1;
266 if (v->spl_ldatum_exists)
267 adjustBox(union2, DatumGetBoxP(v->spl_ldatum));
268 v->spl_ldatum = BoxPGetDatum(union2);
269 if (v->spl_rdatum_exists)
270 adjustBox(union1, DatumGetBoxP(v->spl_rdatum));
271 v->spl_rdatum = BoxPGetDatum(union1);
274 v->spl_ldatum_exists = v->spl_rdatum_exists = false;
278 * Trivial split: half of entries will be placed on one page
279 * and another half - to another
282 fallbackSplit(GistEntryVector *entryvec, GIST_SPLITVEC *v)
290 maxoff = entryvec->n - 1;
292 nbytes = (maxoff + 2) * sizeof(OffsetNumber);
293 v->spl_left = (OffsetNumber *) palloc(nbytes);
294 v->spl_right = (OffsetNumber *) palloc(nbytes);
295 v->spl_nleft = v->spl_nright = 0;
297 for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
299 BOX *cur = DatumGetBoxP(entryvec->vector[i].key);
301 if (i <= (maxoff - FirstOffsetNumber + 1) / 2)
303 v->spl_left[v->spl_nleft] = i;
306 unionL = (BOX *) palloc(sizeof(BOX));
310 adjustBox(unionL, cur);
316 v->spl_right[v->spl_nright] = i;
319 unionR = (BOX *) palloc(sizeof(BOX));
323 adjustBox(unionR, cur);
329 if (v->spl_ldatum_exists)
330 adjustBox(unionL, DatumGetBoxP(v->spl_ldatum));
331 v->spl_ldatum = BoxPGetDatum(unionL);
333 if (v->spl_rdatum_exists)
334 adjustBox(unionR, DatumGetBoxP(v->spl_rdatum));
335 v->spl_rdatum = BoxPGetDatum(unionR);
337 v->spl_ldatum_exists = v->spl_rdatum_exists = false;
341 * The GiST PickSplit method
343 * New linear algorithm, see 'New Linear Node Splitting Algorithm for R-tree',
344 * C.H.Ang and T.C.Tan
346 * This is used for both boxes and points.
349 gist_box_picksplit(PG_FUNCTION_ARGS)
351 GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
352 GIST_SPLITVEC *v = (GIST_SPLITVEC *) PG_GETARG_POINTER(1);
368 char direction = ' ';
369 bool allisequal = true;
373 posL = posR = posB = posT = 0;
374 maxoff = entryvec->n - 1;
376 cur = DatumGetBoxP(entryvec->vector[FirstOffsetNumber].key);
377 memcpy((void *) &pageunion, (void *) cur, sizeof(BOX));
380 for (i = OffsetNumberNext(FirstOffsetNumber); i <= maxoff; i = OffsetNumberNext(i))
382 cur = DatumGetBoxP(entryvec->vector[i].key);
384 pageunion.high.x != cur->high.x ||
385 pageunion.high.y != cur->high.y ||
386 pageunion.low.x != cur->low.x ||
387 pageunion.low.y != cur->low.y
391 adjustBox(&pageunion, cur);
397 * All entries are the same
399 fallbackSplit(entryvec, v);
400 PG_RETURN_POINTER(v);
403 nbytes = (maxoff + 2) * sizeof(OffsetNumber);
404 listL = (OffsetNumber *) palloc(nbytes);
405 listR = (OffsetNumber *) palloc(nbytes);
406 listB = (OffsetNumber *) palloc(nbytes);
407 listT = (OffsetNumber *) palloc(nbytes);
408 unionL = (BOX *) palloc(sizeof(BOX));
409 unionR = (BOX *) palloc(sizeof(BOX));
410 unionB = (BOX *) palloc(sizeof(BOX));
411 unionT = (BOX *) palloc(sizeof(BOX));
413 #define ADDLIST( list, unionD, pos, num ) do { \
415 if ( (unionD)->high.x < cur->high.x ) (unionD)->high.x = cur->high.x; \
416 if ( (unionD)->low.x > cur->low.x ) (unionD)->low.x = cur->low.x; \
417 if ( (unionD)->high.y < cur->high.y ) (unionD)->high.y = cur->high.y; \
418 if ( (unionD)->low.y > cur->low.y ) (unionD)->low.y = cur->low.y; \
420 memcpy( (void*)(unionD), (void*) cur, sizeof( BOX ) ); \
426 for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
428 cur = DatumGetBoxP(entryvec->vector[i].key);
429 if (cur->low.x - pageunion.low.x < pageunion.high.x - cur->high.x)
430 ADDLIST(listL, unionL, posL, i);
432 ADDLIST(listR, unionR, posR, i);
433 if (cur->low.y - pageunion.low.y < pageunion.high.y - cur->high.y)
434 ADDLIST(listB, unionB, posB, i);
436 ADDLIST(listT, unionT, posT, i);
439 #define LIMIT_RATIO 0.1
440 #define _IS_BADRATIO(x,y) ( (y) == 0 || (float)(x)/(float)(y) < LIMIT_RATIO )
441 #define IS_BADRATIO(x,y) ( _IS_BADRATIO((x),(y)) || _IS_BADRATIO((y),(x)) )
442 /* bad disposition, try to split by centers of boxes */
443 if (IS_BADRATIO(posR, posL) && IS_BADRATIO(posT, posB))
445 double avgCenterX = 0.0,
450 for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
452 cur = DatumGetBoxP(entryvec->vector[i].key);
453 avgCenterX += ((double) cur->high.x + (double) cur->low.x) / 2.0;
454 avgCenterY += ((double) cur->high.y + (double) cur->low.y) / 2.0;
457 avgCenterX /= maxoff;
458 avgCenterY /= maxoff;
460 posL = posR = posB = posT = 0;
461 for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
463 cur = DatumGetBoxP(entryvec->vector[i].key);
465 CenterX = ((double) cur->high.x + (double) cur->low.x) / 2.0;
466 CenterY = ((double) cur->high.y + (double) cur->low.y) / 2.0;
468 if (CenterX < avgCenterX)
469 ADDLIST(listL, unionL, posL, i);
470 else if (CenterX == avgCenterX)
473 ADDLIST(listR, unionR, posR, i);
475 ADDLIST(listL, unionL, posL, i);
478 ADDLIST(listR, unionR, posR, i);
480 if (CenterY < avgCenterY)
481 ADDLIST(listB, unionB, posB, i);
482 else if (CenterY == avgCenterY)
485 ADDLIST(listT, unionT, posT, i);
487 ADDLIST(listB, unionB, posB, i);
490 ADDLIST(listT, unionT, posT, i);
493 if (IS_BADRATIO(posR, posL) && IS_BADRATIO(posT, posB))
495 fallbackSplit(entryvec, v);
496 PG_RETURN_POINTER(v);
500 /* which split more optimal? */
501 if (Max(posL, posR) < Max(posB, posT))
503 else if (Max(posL, posR) > Max(posB, posT))
507 Datum interLR = DirectFunctionCall2(rt_box_inter,
508 BoxPGetDatum(unionL),
509 BoxPGetDatum(unionR));
510 Datum interBT = DirectFunctionCall2(rt_box_inter,
511 BoxPGetDatum(unionB),
512 BoxPGetDatum(unionT));
516 sizeLR = size_box(interLR);
517 sizeBT = size_box(interBT);
525 if (direction == 'x')
528 listR, posR, unionR);
532 listT, posT, unionT);
534 PG_RETURN_POINTER(v);
540 * This is used for both boxes and points.
543 gist_box_same(PG_FUNCTION_ARGS)
545 BOX *b1 = PG_GETARG_BOX_P(0);
546 BOX *b2 = PG_GETARG_BOX_P(1);
547 bool *result = (bool *) PG_GETARG_POINTER(2);
550 *result = DatumGetBool(DirectFunctionCall2(box_same,
552 PointerGetDatum(b2)));
554 *result = (b1 == NULL && b2 == NULL) ? TRUE : FALSE;
555 PG_RETURN_POINTER(result);
559 * Leaf-level consistency for boxes: just apply the query operator
562 gist_box_leaf_consistent(BOX *key, BOX *query, StrategyNumber strategy)
568 case RTLeftStrategyNumber:
569 retval = DatumGetBool(DirectFunctionCall2(box_left,
570 PointerGetDatum(key),
571 PointerGetDatum(query)));
573 case RTOverLeftStrategyNumber:
574 retval = DatumGetBool(DirectFunctionCall2(box_overleft,
575 PointerGetDatum(key),
576 PointerGetDatum(query)));
578 case RTOverlapStrategyNumber:
579 retval = DatumGetBool(DirectFunctionCall2(box_overlap,
580 PointerGetDatum(key),
581 PointerGetDatum(query)));
583 case RTOverRightStrategyNumber:
584 retval = DatumGetBool(DirectFunctionCall2(box_overright,
585 PointerGetDatum(key),
586 PointerGetDatum(query)));
588 case RTRightStrategyNumber:
589 retval = DatumGetBool(DirectFunctionCall2(box_right,
590 PointerGetDatum(key),
591 PointerGetDatum(query)));
593 case RTSameStrategyNumber:
594 retval = DatumGetBool(DirectFunctionCall2(box_same,
595 PointerGetDatum(key),
596 PointerGetDatum(query)));
598 case RTContainsStrategyNumber:
599 case RTOldContainsStrategyNumber:
600 retval = DatumGetBool(DirectFunctionCall2(box_contain,
601 PointerGetDatum(key),
602 PointerGetDatum(query)));
604 case RTContainedByStrategyNumber:
605 case RTOldContainedByStrategyNumber:
606 retval = DatumGetBool(DirectFunctionCall2(box_contained,
607 PointerGetDatum(key),
608 PointerGetDatum(query)));
610 case RTOverBelowStrategyNumber:
611 retval = DatumGetBool(DirectFunctionCall2(box_overbelow,
612 PointerGetDatum(key),
613 PointerGetDatum(query)));
615 case RTBelowStrategyNumber:
616 retval = DatumGetBool(DirectFunctionCall2(box_below,
617 PointerGetDatum(key),
618 PointerGetDatum(query)));
620 case RTAboveStrategyNumber:
621 retval = DatumGetBool(DirectFunctionCall2(box_above,
622 PointerGetDatum(key),
623 PointerGetDatum(query)));
625 case RTOverAboveStrategyNumber:
626 retval = DatumGetBool(DirectFunctionCall2(box_overabove,
627 PointerGetDatum(key),
628 PointerGetDatum(query)));
639 BOX *box = DatumGetBoxP(dbox);
641 if (box == NULL || box->high.x <= box->low.x || box->high.y <= box->low.y)
643 return (box->high.x - box->low.x) * (box->high.y - box->low.y);
646 /*****************************************
647 * Common rtree functions (for boxes, polygons, and circles)
648 *****************************************/
651 * Internal-page consistency for all these types
653 * We can use the same function since all types use bounding boxes as the
654 * internal-page representation.
657 rtree_internal_consistent(BOX *key, BOX *query, StrategyNumber strategy)
663 case RTLeftStrategyNumber:
664 retval = !DatumGetBool(DirectFunctionCall2(box_overright,
665 PointerGetDatum(key),
666 PointerGetDatum(query)));
668 case RTOverLeftStrategyNumber:
669 retval = !DatumGetBool(DirectFunctionCall2(box_right,
670 PointerGetDatum(key),
671 PointerGetDatum(query)));
673 case RTOverlapStrategyNumber:
674 retval = DatumGetBool(DirectFunctionCall2(box_overlap,
675 PointerGetDatum(key),
676 PointerGetDatum(query)));
678 case RTOverRightStrategyNumber:
679 retval = !DatumGetBool(DirectFunctionCall2(box_left,
680 PointerGetDatum(key),
681 PointerGetDatum(query)));
683 case RTRightStrategyNumber:
684 retval = !DatumGetBool(DirectFunctionCall2(box_overleft,
685 PointerGetDatum(key),
686 PointerGetDatum(query)));
688 case RTSameStrategyNumber:
689 case RTContainsStrategyNumber:
690 case RTOldContainsStrategyNumber:
691 retval = DatumGetBool(DirectFunctionCall2(box_contain,
692 PointerGetDatum(key),
693 PointerGetDatum(query)));
695 case RTContainedByStrategyNumber:
696 case RTOldContainedByStrategyNumber:
697 retval = DatumGetBool(DirectFunctionCall2(box_overlap,
698 PointerGetDatum(key),
699 PointerGetDatum(query)));
701 case RTOverBelowStrategyNumber:
702 retval = !DatumGetBool(DirectFunctionCall2(box_above,
703 PointerGetDatum(key),
704 PointerGetDatum(query)));
706 case RTBelowStrategyNumber:
707 retval = !DatumGetBool(DirectFunctionCall2(box_overabove,
708 PointerGetDatum(key),
709 PointerGetDatum(query)));
711 case RTAboveStrategyNumber:
712 retval = !DatumGetBool(DirectFunctionCall2(box_overbelow,
713 PointerGetDatum(key),
714 PointerGetDatum(query)));
716 case RTOverAboveStrategyNumber:
717 retval = !DatumGetBool(DirectFunctionCall2(box_below,
718 PointerGetDatum(key),
719 PointerGetDatum(query)));
727 /**************************************************
729 **************************************************/
732 * GiST compress for polygons: represent a polygon by its bounding box
735 gist_poly_compress(PG_FUNCTION_ARGS)
737 GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
742 retval = palloc(sizeof(GISTENTRY));
743 if (DatumGetPointer(entry->key) != NULL)
745 POLYGON *in = DatumGetPolygonP(entry->key);
748 r = (BOX *) palloc(sizeof(BOX));
749 memcpy((void *) r, (void *) &(in->boundbox), sizeof(BOX));
750 gistentryinit(*retval, PointerGetDatum(r),
751 entry->rel, entry->page,
752 entry->offset, FALSE);
757 gistentryinit(*retval, (Datum) 0,
758 entry->rel, entry->page,
759 entry->offset, FALSE);
764 PG_RETURN_POINTER(retval);
768 * The GiST Consistent method for polygons
771 gist_poly_consistent(PG_FUNCTION_ARGS)
773 GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
774 POLYGON *query = PG_GETARG_POLYGON_P(1);
775 StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2);
777 /* Oid subtype = PG_GETARG_OID(3); */
778 bool *recheck = (bool *) PG_GETARG_POINTER(4);
781 /* All cases served by this function are inexact */
784 if (DatumGetBoxP(entry->key) == NULL || query == NULL)
785 PG_RETURN_BOOL(FALSE);
788 * Since the operators require recheck anyway, we can just use
789 * rtree_internal_consistent even at leaf nodes. (This works in part
790 * because the index entries are bounding boxes not polygons.)
792 result = rtree_internal_consistent(DatumGetBoxP(entry->key),
793 &(query->boundbox), strategy);
795 /* Avoid memory leak if supplied poly is toasted */
796 PG_FREE_IF_COPY(query, 1);
798 PG_RETURN_BOOL(result);
801 /**************************************************
803 **************************************************/
806 * GiST compress for circles: represent a circle by its bounding box
809 gist_circle_compress(PG_FUNCTION_ARGS)
811 GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
816 retval = palloc(sizeof(GISTENTRY));
817 if (DatumGetCircleP(entry->key) != NULL)
819 CIRCLE *in = DatumGetCircleP(entry->key);
822 r = (BOX *) palloc(sizeof(BOX));
823 r->high.x = in->center.x + in->radius;
824 r->low.x = in->center.x - in->radius;
825 r->high.y = in->center.y + in->radius;
826 r->low.y = in->center.y - in->radius;
827 gistentryinit(*retval, PointerGetDatum(r),
828 entry->rel, entry->page,
829 entry->offset, FALSE);
834 gistentryinit(*retval, (Datum) 0,
835 entry->rel, entry->page,
836 entry->offset, FALSE);
841 PG_RETURN_POINTER(retval);
845 * The GiST Consistent method for circles
848 gist_circle_consistent(PG_FUNCTION_ARGS)
850 GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
851 CIRCLE *query = PG_GETARG_CIRCLE_P(1);
852 StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2);
854 /* Oid subtype = PG_GETARG_OID(3); */
855 bool *recheck = (bool *) PG_GETARG_POINTER(4);
859 /* All cases served by this function are inexact */
862 if (DatumGetBoxP(entry->key) == NULL || query == NULL)
863 PG_RETURN_BOOL(FALSE);
866 * Since the operators require recheck anyway, we can just use
867 * rtree_internal_consistent even at leaf nodes. (This works in part
868 * because the index entries are bounding boxes not circles.)
870 bbox.high.x = query->center.x + query->radius;
871 bbox.low.x = query->center.x - query->radius;
872 bbox.high.y = query->center.y + query->radius;
873 bbox.low.y = query->center.y - query->radius;
875 result = rtree_internal_consistent(DatumGetBoxP(entry->key),
878 PG_RETURN_BOOL(result);
881 /**************************************************
883 **************************************************/
886 gist_point_compress(PG_FUNCTION_ARGS)
888 GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
890 if (entry->leafkey) /* Point, actually */
892 BOX *box = palloc(sizeof(BOX));
893 Point *point = DatumGetPointP(entry->key);
894 GISTENTRY *retval = palloc(sizeof(GISTENTRY));
896 box->high = box->low = *point;
898 gistentryinit(*retval, BoxPGetDatum(box),
899 entry->rel, entry->page, entry->offset, FALSE);
901 PG_RETURN_POINTER(retval);
904 PG_RETURN_POINTER(entry);
907 #define point_point_distance(p1,p2) \
908 DatumGetFloat8(DirectFunctionCall2(point_distance, \
909 PointPGetDatum(p1), PointPGetDatum(p2)))
912 computeDistance(bool isLeaf, BOX *box, Point *point)
918 /* simple point to point distance */
919 result = point_point_distance(point, &box->low);
921 else if (point->x <= box->high.x && point->x >= box->low.x &&
922 point->y <= box->high.y && point->y >= box->low.y)
924 /* point inside the box */
927 else if (point->x <= box->high.x && point->x >= box->low.x)
929 /* point is over or below box */
930 Assert(box->low.y <= box->high.y);
931 if (point->y > box->high.y)
932 result = point->y - box->high.y;
933 else if (point->y < box->low.y)
934 result = box->low.y - point->y;
936 elog(ERROR, "inconsistent point values");
938 else if (point->y <= box->high.y && point->y >= box->low.y)
940 /* point is to left or right of box */
941 Assert(box->low.x <= box->high.x);
942 if (point->x > box->high.x)
943 result = point->x - box->high.x;
944 else if (point->x < box->low.x)
945 result = box->low.x - point->x;
947 elog(ERROR, "inconsistent point values");
951 /* closest point will be a vertex */
955 result = point_point_distance(point, &box->low);
957 subresult = point_point_distance(point, &box->high);
958 if (result > subresult)
963 subresult = point_point_distance(point, &p);
964 if (result > subresult)
969 subresult = point_point_distance(point, &p);
970 if (result > subresult)
978 gist_point_consistent_internal(StrategyNumber strategy,
979 bool isLeaf, BOX *key, Point *query)
985 case RTLeftStrategyNumber:
986 result = FPlt(key->low.x, query->x);
988 case RTRightStrategyNumber:
989 result = FPgt(key->high.x, query->x);
991 case RTAboveStrategyNumber:
992 result = FPgt(key->high.y, query->y);
994 case RTBelowStrategyNumber:
995 result = FPlt(key->low.y, query->y);
997 case RTSameStrategyNumber:
1000 result = FPeq(key->low.x, query->x)
1001 && FPeq(key->low.y, query->y);
1005 result = (query->x <= key->high.x && query->x >= key->low.x &&
1006 query->y <= key->high.y && query->y >= key->low.y);
1010 elog(ERROR, "unknown strategy number: %d", strategy);
1016 #define GeoStrategyNumberOffset 20
1017 #define PointStrategyNumberGroup 0
1018 #define BoxStrategyNumberGroup 1
1019 #define PolygonStrategyNumberGroup 2
1020 #define CircleStrategyNumberGroup 3
1023 gist_point_consistent(PG_FUNCTION_ARGS)
1025 GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
1026 StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2);
1027 bool *recheck = (bool *) PG_GETARG_POINTER(4);
1029 StrategyNumber strategyGroup = strategy / GeoStrategyNumberOffset;
1031 switch (strategyGroup)
1033 case PointStrategyNumberGroup:
1034 result = gist_point_consistent_internal(strategy % GeoStrategyNumberOffset,
1036 DatumGetBoxP(entry->key),
1037 PG_GETARG_POINT_P(1));
1040 case BoxStrategyNumberGroup:
1041 result = DatumGetBool(DirectFunctionCall5(
1042 gist_box_consistent,
1043 PointerGetDatum(entry),
1045 Int16GetDatum(RTOverlapStrategyNumber),
1046 0, PointerGetDatum(recheck)));
1048 case PolygonStrategyNumberGroup:
1050 POLYGON *query = PG_GETARG_POLYGON_P(1);
1052 result = DatumGetBool(DirectFunctionCall5(
1053 gist_poly_consistent,
1054 PointerGetDatum(entry),
1055 PolygonPGetDatum(query),
1056 Int16GetDatum(RTOverlapStrategyNumber),
1057 0, PointerGetDatum(recheck)));
1059 if (GIST_LEAF(entry) && result)
1062 * We are on leaf page and quick check shows overlapping
1063 * of polygon's bounding box and point
1065 BOX *box = DatumGetBoxP(entry->key);
1067 Assert(box->high.x == box->low.x
1068 && box->high.y == box->low.y);
1069 result = DatumGetBool(DirectFunctionCall2(
1071 PolygonPGetDatum(query),
1072 PointPGetDatum(&box->high)));
1077 case CircleStrategyNumberGroup:
1079 CIRCLE *query = PG_GETARG_CIRCLE_P(1);
1081 result = DatumGetBool(DirectFunctionCall5(
1082 gist_circle_consistent,
1083 PointerGetDatum(entry),
1084 CirclePGetDatum(query),
1085 Int16GetDatum(RTOverlapStrategyNumber),
1086 0, PointerGetDatum(recheck)));
1088 if (GIST_LEAF(entry) && result)
1091 * We are on leaf page and quick check shows overlapping
1092 * of polygon's bounding box and point
1094 BOX *box = DatumGetBoxP(entry->key);
1096 Assert(box->high.x == box->low.x
1097 && box->high.y == box->low.y);
1098 result = DatumGetBool(DirectFunctionCall2(
1100 CirclePGetDatum(query),
1101 PointPGetDatum(&box->high)));
1107 elog(ERROR, "unknown strategy number: %d", strategy);
1108 result = false; /* keep compiler quiet */
1111 PG_RETURN_BOOL(result);
1115 gist_point_distance(PG_FUNCTION_ARGS)
1117 GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
1118 StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2);
1120 StrategyNumber strategyGroup = strategy / GeoStrategyNumberOffset;
1122 switch (strategyGroup)
1124 case PointStrategyNumberGroup:
1125 distance = computeDistance(GIST_LEAF(entry),
1126 DatumGetBoxP(entry->key),
1127 PG_GETARG_POINT_P(1));
1130 elog(ERROR, "unknown strategy number: %d", strategy);
1131 distance = 0.0; /* keep compiler quiet */
1134 PG_RETURN_FLOAT8(distance);