blob: 2ebb3d1bd711760bde531e15c4308aa722e71dcd [file] [log] [blame]
/*-------------------------------------------------------------------------
*
* gistproc.c
* Support procedures for GiSTs over 2-D objects (boxes, polygons, circles).
*
* This gives R-tree behavior, with Guttman's poly-time split algorithm.
*
*
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/access/gist/gistproc.c,v 1.9.2.1 2007/09/07 17:04:46 teodor Exp $
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "access/gist.h"
#include "access/skey.h"
#include "utils/geo_decls.h"
static bool gist_box_leaf_consistent(BOX *key, BOX *query,
StrategyNumber strategy);
static double size_box(Datum dbox);
static bool rtree_internal_consistent(BOX *key, BOX *query,
StrategyNumber strategy);
/**************************************************
* Box ops
**************************************************/
static Datum
rt_box_union(PG_FUNCTION_ARGS)
{
BOX *a = PG_GETARG_BOX_P(0);
BOX *b = PG_GETARG_BOX_P(1);
BOX *n;
n = (BOX *) palloc(sizeof(BOX));
n->high.x = Max(a->high.x, b->high.x);
n->high.y = Max(a->high.y, b->high.y);
n->low.x = Min(a->low.x, b->low.x);
n->low.y = Min(a->low.y, b->low.y);
PG_RETURN_BOX_P(n);
}
static Datum
rt_box_inter(PG_FUNCTION_ARGS)
{
BOX *a = PG_GETARG_BOX_P(0);
BOX *b = PG_GETARG_BOX_P(1);
BOX *n;
n = (BOX *) palloc(sizeof(BOX));
n->high.x = Min(a->high.x, b->high.x);
n->high.y = Min(a->high.y, b->high.y);
n->low.x = Max(a->low.x, b->low.x);
n->low.y = Max(a->low.y, b->low.y);
if (n->high.x < n->low.x || n->high.y < n->low.y)
{
pfree(n);
/* Indicate "no intersection" by returning NULL pointer */
n = NULL;
}
PG_RETURN_BOX_P(n);
}
/*
* The GiST Consistent method for boxes
*
* Should return false if for all data items x below entry,
* the predicate x op query must be FALSE, where op is the oper
* corresponding to strategy in the pg_amop table.
*/
Datum
gist_box_consistent(PG_FUNCTION_ARGS)
{
GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
BOX *query = PG_GETARG_BOX_P(1);
StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2);
if (DatumGetBoxP(entry->key) == NULL || query == NULL)
PG_RETURN_BOOL(FALSE);
/*
* if entry is not leaf, use rtree_internal_consistent, else use
* gist_box_leaf_consistent
*/
if (GIST_LEAF(entry))
PG_RETURN_BOOL(gist_box_leaf_consistent(DatumGetBoxP(entry->key),
query,
strategy));
else
PG_RETURN_BOOL(rtree_internal_consistent(DatumGetBoxP(entry->key),
query,
strategy));
}
static void
adjustBox(BOX *b, BOX *addon)
{
if (b->high.x < addon->high.x)
b->high.x = addon->high.x;
if (b->low.x > addon->low.x)
b->low.x = addon->low.x;
if (b->high.y < addon->high.y)
b->high.y = addon->high.y;
if (b->low.y > addon->low.y)
b->low.y = addon->low.y;
}
/*
* The GiST Union method for boxes
*
* returns the minimal bounding box that encloses all the entries in entryvec
*/
Datum
gist_box_union(PG_FUNCTION_ARGS)
{
GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
int *sizep = (int *) PG_GETARG_POINTER(1);
int numranges,
i;
BOX *cur,
*pageunion;
numranges = entryvec->n;
pageunion = (BOX *) palloc(sizeof(BOX));
cur = DatumGetBoxP(entryvec->vector[0].key);
memcpy((void *) pageunion, (void *) cur, sizeof(BOX));
for (i = 1; i < numranges; i++)
{
cur = DatumGetBoxP(entryvec->vector[i].key);
adjustBox(pageunion, cur);
}
*sizep = sizeof(BOX);
PG_RETURN_POINTER(pageunion);
}
/*
* GiST Compress methods for boxes
*
* do not do anything.
*/
Datum
gist_box_compress(PG_FUNCTION_ARGS)
{
PG_RETURN_POINTER(PG_GETARG_POINTER(0));
}
/*
* GiST DeCompress method for boxes (also used for polygons and circles)
*
* do not do anything --- we just use the stored box as is.
*/
Datum
gist_box_decompress(PG_FUNCTION_ARGS)
{
PG_RETURN_POINTER(PG_GETARG_POINTER(0));
}
/*
* The GiST Penalty method for boxes
*
* As in the R-tree paper, we use change in area as our penalty metric
*/
Datum
gist_box_penalty(PG_FUNCTION_ARGS)
{
GISTENTRY *origentry = (GISTENTRY *) PG_GETARG_POINTER(0);
GISTENTRY *newentry = (GISTENTRY *) PG_GETARG_POINTER(1);
float *result = (float *) PG_GETARG_POINTER(2);
Datum ud;
ud = DirectFunctionCall2(rt_box_union, origentry->key, newentry->key);
*result = (float) (size_box(ud) - size_box(origentry->key));
PG_RETURN_POINTER(result);
}
static void
chooseLR(GIST_SPLITVEC *v,
OffsetNumber *list1, int nlist1, BOX *union1,
OffsetNumber *list2, int nlist2, BOX *union2)
{
bool firstToLeft = true;
if (v->spl_ldatum_exists || v->spl_rdatum_exists)
{
if (v->spl_ldatum_exists && v->spl_rdatum_exists)
{
BOX LRl = *union1,
LRr = *union2;
BOX RLl = *union2,
RLr = *union1;
double sizeLR,
sizeRL;
adjustBox(&LRl, DatumGetBoxP(v->spl_ldatum));
adjustBox(&LRr, DatumGetBoxP(v->spl_rdatum));
adjustBox(&RLl, DatumGetBoxP(v->spl_ldatum));
adjustBox(&RLr, DatumGetBoxP(v->spl_rdatum));
sizeLR = size_box(DirectFunctionCall2(rt_box_inter, BoxPGetDatum(&LRl), BoxPGetDatum(&LRr)));
sizeRL = size_box(DirectFunctionCall2(rt_box_inter, BoxPGetDatum(&RLl), BoxPGetDatum(&RLr)));
if (sizeLR > sizeRL)
firstToLeft = false;
}
else
{
float p1,
p2;
GISTENTRY oldUnion,
addon;
gistentryinit(oldUnion, (v->spl_ldatum_exists) ? v->spl_ldatum : v->spl_rdatum,
NULL, NULL, InvalidOffsetNumber, FALSE);
gistentryinit(addon, BoxPGetDatum(union1), NULL, NULL, InvalidOffsetNumber, FALSE);
DirectFunctionCall3(gist_box_penalty, PointerGetDatum(&oldUnion), PointerGetDatum(&union1), PointerGetDatum(&p1));
gistentryinit(addon, BoxPGetDatum(union2), NULL, NULL, InvalidOffsetNumber, FALSE);
DirectFunctionCall3(gist_box_penalty, PointerGetDatum(&oldUnion), PointerGetDatum(&union2), PointerGetDatum(&p2));
if ((v->spl_ldatum_exists && p1 > p2) || (v->spl_rdatum_exists && p1 < p2))
firstToLeft = false;
}
}
if (firstToLeft)
{
v->spl_left = list1;
v->spl_right = list2;
v->spl_nleft = nlist1;
v->spl_nright = nlist2;
if (v->spl_ldatum_exists)
adjustBox(union1, DatumGetBoxP(v->spl_ldatum));
v->spl_ldatum = BoxPGetDatum(union1);
if (v->spl_rdatum_exists)
adjustBox(union2, DatumGetBoxP(v->spl_rdatum));
v->spl_rdatum = BoxPGetDatum(union2);
}
else
{
v->spl_left = list2;
v->spl_right = list1;
v->spl_nleft = nlist2;
v->spl_nright = nlist1;
if (v->spl_ldatum_exists)
adjustBox(union2, DatumGetBoxP(v->spl_ldatum));
v->spl_ldatum = BoxPGetDatum(union2);
if (v->spl_rdatum_exists)
adjustBox(union1, DatumGetBoxP(v->spl_rdatum));
v->spl_rdatum = BoxPGetDatum(union1);
}
v->spl_ldatum_exists = v->spl_rdatum_exists = false;
}
/*
* The GiST PickSplit method
*
* New linear algorithm, see 'New Linear Node Splitting Algorithm for R-tree',
* C.H.Ang and T.C.Tan
*/
Datum
gist_box_picksplit(PG_FUNCTION_ARGS)
{
GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
GIST_SPLITVEC *v = (GIST_SPLITVEC *) PG_GETARG_POINTER(1);
OffsetNumber i;
OffsetNumber *listL,
*listR,
*listB,
*listT;
BOX *unionL,
*unionR,
*unionB,
*unionT;
int posL,
posR,
posB,
posT;
BOX pageunion;
BOX *cur;
char direction = ' ';
bool allisequal = true;
OffsetNumber maxoff;
int nbytes;
posL = posR = posB = posT = 0;
maxoff = entryvec->n - 1;
cur = DatumGetBoxP(entryvec->vector[FirstOffsetNumber].key);
memcpy((void *) &pageunion, (void *) cur, sizeof(BOX));
/* find MBR */
for (i = OffsetNumberNext(FirstOffsetNumber); i <= maxoff; i = OffsetNumberNext(i))
{
cur = DatumGetBoxP(entryvec->vector[i].key);
if (allisequal == true && (
pageunion.high.x != cur->high.x ||
pageunion.high.y != cur->high.y ||
pageunion.low.x != cur->low.x ||
pageunion.low.y != cur->low.y
))
allisequal = false;
adjustBox(&pageunion, cur);
}
nbytes = (maxoff + 2) * sizeof(OffsetNumber);
listL = (OffsetNumber *) palloc(nbytes);
listR = (OffsetNumber *) palloc(nbytes);
unionL = (BOX *) palloc(sizeof(BOX));
unionR = (BOX *) palloc(sizeof(BOX));
if (allisequal)
{
cur = DatumGetBoxP(entryvec->vector[OffsetNumberNext(FirstOffsetNumber)].key);
if (memcmp((void *) cur, (void *) &pageunion, sizeof(BOX)) == 0)
{
v->spl_left = listL;
v->spl_right = listR;
v->spl_nleft = v->spl_nright = 0;
memcpy((void *) unionL, (void *) &pageunion, sizeof(BOX));
memcpy((void *) unionR, (void *) &pageunion, sizeof(BOX));
for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
{
if (i <= (maxoff - FirstOffsetNumber + 1) / 2)
{
v->spl_left[v->spl_nleft] = i;
v->spl_nleft++;
}
else
{
v->spl_right[v->spl_nright] = i;
v->spl_nright++;
}
}
if (v->spl_ldatum_exists)
adjustBox(unionL, DatumGetBoxP(v->spl_ldatum));
v->spl_ldatum = BoxPGetDatum(unionL);
if (v->spl_rdatum_exists)
adjustBox(unionR, DatumGetBoxP(v->spl_rdatum));
v->spl_rdatum = BoxPGetDatum(unionR);
v->spl_ldatum_exists = v->spl_rdatum_exists = false;
PG_RETURN_POINTER(v);
}
}
listB = (OffsetNumber *) palloc(nbytes);
listT = (OffsetNumber *) palloc(nbytes);
unionB = (BOX *) palloc(sizeof(BOX));
unionT = (BOX *) palloc(sizeof(BOX));
#define ADDLIST( list, unionD, pos, num ) do { \
if ( pos ) { \
if ( (unionD)->high.x < cur->high.x ) (unionD)->high.x = cur->high.x; \
if ( (unionD)->low.x > cur->low.x ) (unionD)->low.x = cur->low.x; \
if ( (unionD)->high.y < cur->high.y ) (unionD)->high.y = cur->high.y; \
if ( (unionD)->low.y > cur->low.y ) (unionD)->low.y = cur->low.y; \
} else { \
memcpy( (void*)(unionD), (void*) cur, sizeof( BOX ) ); \
} \
(list)[pos] = num; \
(pos)++; \
} while(0)
for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
{
cur = DatumGetBoxP(entryvec->vector[i].key);
if (cur->low.x - pageunion.low.x < pageunion.high.x - cur->high.x)
ADDLIST(listL, unionL, posL, i);
else
ADDLIST(listR, unionR, posR, i);
if (cur->low.y - pageunion.low.y < pageunion.high.y - cur->high.y)
ADDLIST(listB, unionB, posB, i);
else
ADDLIST(listT, unionT, posT, i);
}
#define LIMIT_RATIO 0.1
#define _IS_BADRATIO(x,y) ( (y) == 0 || (float)(x)/(float)(y) < LIMIT_RATIO )
#define IS_BADRATIO(x,y) ( _IS_BADRATIO((x),(y)) || _IS_BADRATIO((y),(x)) )
/* bad disposition, try to split by centers of boxes */
if ( IS_BADRATIO(posR, posL) && IS_BADRATIO(posT, posB) )
{
double avgCenterX=0.0, avgCenterY=0.0;
double CenterX, CenterY;
for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
{
cur = DatumGetBoxP(entryvec->vector[i].key);
avgCenterX += ((double)cur->high.x + (double)cur->low.x)/2.0;
avgCenterY += ((double)cur->high.y + (double)cur->low.y)/2.0;
}
avgCenterX /= maxoff;
avgCenterY /= maxoff;
posL = posR = posB = posT = 0;
for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
{
cur = DatumGetBoxP(entryvec->vector[i].key);
CenterX = ((double)cur->high.x + (double)cur->low.x)/2.0;
CenterY = ((double)cur->high.y + (double)cur->low.y)/2.0;
if (CenterX < avgCenterX)
ADDLIST(listL, unionL, posL, i);
else if (CenterX == avgCenterX)
{
if (posL > posR)
ADDLIST(listR, unionR, posR, i);
else
ADDLIST(listL, unionL, posL, i);
}
else
ADDLIST(listR, unionR, posR, i);
if (CenterY < avgCenterY)
ADDLIST(listB, unionB, posB, i);
else if (CenterY == avgCenterY)
{
if (posB > posT)
ADDLIST(listT, unionT, posT, i);
else
ADDLIST(listB, unionB, posB, i);
}
else
ADDLIST(listT, unionT, posT, i);
}
}
/* which split more optimal? */
if (Max(posL, posR) < Max(posB, posT))
direction = 'x';
else if (Max(posL, posR) > Max(posB, posT))
direction = 'y';
else
{
Datum interLR = DirectFunctionCall2(rt_box_inter,
BoxPGetDatum(unionL),
BoxPGetDatum(unionR));
Datum interBT = DirectFunctionCall2(rt_box_inter,
BoxPGetDatum(unionB),
BoxPGetDatum(unionT));
double sizeLR,
sizeBT;
sizeLR = size_box(interLR);
sizeBT = size_box(interBT);
if (sizeLR < sizeBT)
direction = 'x';
else
direction = 'y';
}
if (direction == 'x')
chooseLR(v,
listL, posL, unionL,
listR, posR, unionR);
else
chooseLR(v,
listB, posB, unionB,
listT, posT, unionT);
PG_RETURN_POINTER(v);
}
/*
* Equality method
*/
Datum
gist_box_same(PG_FUNCTION_ARGS)
{
BOX *b1 = PG_GETARG_BOX_P(0);
BOX *b2 = PG_GETARG_BOX_P(1);
bool *result = (bool *) PG_GETARG_POINTER(2);
if (b1 && b2)
*result = DatumGetBool(DirectFunctionCall2(box_same,
PointerGetDatum(b1),
PointerGetDatum(b2)));
else
*result = (b1 == NULL && b2 == NULL) ? TRUE : FALSE;
PG_RETURN_POINTER(result);
}
/*
* Leaf-level consistency for boxes: just apply the query operator
*/
static bool
gist_box_leaf_consistent(BOX *key, BOX *query, StrategyNumber strategy)
{
bool retval;
switch (strategy)
{
case RTLeftStrategyNumber:
retval = DatumGetBool(DirectFunctionCall2(box_left,
PointerGetDatum(key),
PointerGetDatum(query)));
break;
case RTOverLeftStrategyNumber:
retval = DatumGetBool(DirectFunctionCall2(box_overleft,
PointerGetDatum(key),
PointerGetDatum(query)));
break;
case RTOverlapStrategyNumber:
retval = DatumGetBool(DirectFunctionCall2(box_overlap,
PointerGetDatum(key),
PointerGetDatum(query)));
break;
case RTOverRightStrategyNumber:
retval = DatumGetBool(DirectFunctionCall2(box_overright,
PointerGetDatum(key),
PointerGetDatum(query)));
break;
case RTRightStrategyNumber:
retval = DatumGetBool(DirectFunctionCall2(box_right,
PointerGetDatum(key),
PointerGetDatum(query)));
break;
case RTSameStrategyNumber:
retval = DatumGetBool(DirectFunctionCall2(box_same,
PointerGetDatum(key),
PointerGetDatum(query)));
break;
case RTContainsStrategyNumber:
case RTOldContainsStrategyNumber:
retval = DatumGetBool(DirectFunctionCall2(box_contain,
PointerGetDatum(key),
PointerGetDatum(query)));
break;
case RTContainedByStrategyNumber:
case RTOldContainedByStrategyNumber:
retval = DatumGetBool(DirectFunctionCall2(box_contained,
PointerGetDatum(key),
PointerGetDatum(query)));
break;
case RTOverBelowStrategyNumber:
retval = DatumGetBool(DirectFunctionCall2(box_overbelow,
PointerGetDatum(key),
PointerGetDatum(query)));
break;
case RTBelowStrategyNumber:
retval = DatumGetBool(DirectFunctionCall2(box_below,
PointerGetDatum(key),
PointerGetDatum(query)));
break;
case RTAboveStrategyNumber:
retval = DatumGetBool(DirectFunctionCall2(box_above,
PointerGetDatum(key),
PointerGetDatum(query)));
break;
case RTOverAboveStrategyNumber:
retval = DatumGetBool(DirectFunctionCall2(box_overabove,
PointerGetDatum(key),
PointerGetDatum(query)));
break;
default:
retval = FALSE;
}
return retval;
}
static double
size_box(Datum dbox)
{
BOX *box = DatumGetBoxP(dbox);
if (box == NULL || box->high.x <= box->low.x || box->high.y <= box->low.y)
return 0.0;
return (box->high.x - box->low.x) * (box->high.y - box->low.y);
}
/*****************************************
* Common rtree functions (for boxes, polygons, and circles)
*****************************************/
/*
* Internal-page consistency for all these types
*
* We can use the same function since all types use bounding boxes as the
* internal-page representation.
*/
static bool
rtree_internal_consistent(BOX *key, BOX *query, StrategyNumber strategy)
{
bool retval;
switch (strategy)
{
case RTLeftStrategyNumber:
retval = !DatumGetBool(DirectFunctionCall2(box_overright,
PointerGetDatum(key),
PointerGetDatum(query)));
break;
case RTOverLeftStrategyNumber:
retval = !DatumGetBool(DirectFunctionCall2(box_right,
PointerGetDatum(key),
PointerGetDatum(query)));
break;
case RTOverlapStrategyNumber:
retval = DatumGetBool(DirectFunctionCall2(box_overlap,
PointerGetDatum(key),
PointerGetDatum(query)));
break;
case RTOverRightStrategyNumber:
retval = !DatumGetBool(DirectFunctionCall2(box_left,
PointerGetDatum(key),
PointerGetDatum(query)));
break;
case RTRightStrategyNumber:
retval = !DatumGetBool(DirectFunctionCall2(box_overleft,
PointerGetDatum(key),
PointerGetDatum(query)));
break;
case RTSameStrategyNumber:
case RTContainsStrategyNumber:
case RTOldContainsStrategyNumber:
retval = DatumGetBool(DirectFunctionCall2(box_contain,
PointerGetDatum(key),
PointerGetDatum(query)));
break;
case RTContainedByStrategyNumber:
case RTOldContainedByStrategyNumber:
retval = DatumGetBool(DirectFunctionCall2(box_overlap,
PointerGetDatum(key),
PointerGetDatum(query)));
break;
case RTOverBelowStrategyNumber:
retval = !DatumGetBool(DirectFunctionCall2(box_above,
PointerGetDatum(key),
PointerGetDatum(query)));
break;
case RTBelowStrategyNumber:
retval = !DatumGetBool(DirectFunctionCall2(box_overabove,
PointerGetDatum(key),
PointerGetDatum(query)));
break;
case RTAboveStrategyNumber:
retval = !DatumGetBool(DirectFunctionCall2(box_overbelow,
PointerGetDatum(key),
PointerGetDatum(query)));
break;
case RTOverAboveStrategyNumber:
retval = !DatumGetBool(DirectFunctionCall2(box_below,
PointerGetDatum(key),
PointerGetDatum(query)));
break;
default:
retval = FALSE;
}
return retval;
}
/**************************************************
* Polygon ops
**************************************************/
/*
* GiST compress for polygons: represent a polygon by its bounding box
*/
Datum
gist_poly_compress(PG_FUNCTION_ARGS)
{
GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
GISTENTRY *retval;
if (entry->leafkey)
{
retval = palloc(sizeof(GISTENTRY));
if (DatumGetPointer(entry->key) != NULL)
{
POLYGON *in = DatumGetPolygonP(entry->key);
BOX *r;
r = (BOX *) palloc(sizeof(BOX));
memcpy((void *) r, (void *) &(in->boundbox), sizeof(BOX));
gistentryinit(*retval, PointerGetDatum(r),
entry->rel, entry->page,
entry->offset, FALSE);
}
else
{
gistentryinit(*retval, (Datum) 0,
entry->rel, entry->page,
entry->offset, FALSE);
}
}
else
retval = entry;
PG_RETURN_POINTER(retval);
}
/*
* The GiST Consistent method for polygons
*/
Datum
gist_poly_consistent(PG_FUNCTION_ARGS)
{
GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
POLYGON *query = PG_GETARG_POLYGON_P(1);
StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2);
bool result;
if (DatumGetBoxP(entry->key) == NULL || query == NULL)
PG_RETURN_BOOL(FALSE);
/*
* Since the operators are marked lossy anyway, we can just use
* rtree_internal_consistent even at leaf nodes. (This works in part
* because the index entries are bounding boxes not polygons.)
*/
result = rtree_internal_consistent(DatumGetBoxP(entry->key),
&(query->boundbox), strategy);
/* Avoid memory leak if supplied poly is toasted */
PG_FREE_IF_COPY(query, 1);
PG_RETURN_BOOL(result);
}
/**************************************************
* Circle ops
**************************************************/
/*
* GiST compress for circles: represent a circle by its bounding box
*/
Datum
gist_circle_compress(PG_FUNCTION_ARGS)
{
GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
GISTENTRY *retval;
if (entry->leafkey)
{
retval = palloc(sizeof(GISTENTRY));
if (DatumGetCircleP(entry->key) != NULL)
{
CIRCLE *in = DatumGetCircleP(entry->key);
BOX *r;
r = (BOX *) palloc(sizeof(BOX));
r->high.x = in->center.x + in->radius;
r->low.x = in->center.x - in->radius;
r->high.y = in->center.y + in->radius;
r->low.y = in->center.y - in->radius;
gistentryinit(*retval, PointerGetDatum(r),
entry->rel, entry->page,
entry->offset, FALSE);
}
else
{
gistentryinit(*retval, (Datum) 0,
entry->rel, entry->page,
entry->offset, FALSE);
}
}
else
retval = entry;
PG_RETURN_POINTER(retval);
}
/*
* The GiST Consistent method for circles
*/
Datum
gist_circle_consistent(PG_FUNCTION_ARGS)
{
GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
CIRCLE *query = PG_GETARG_CIRCLE_P(1);
StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2);
BOX bbox;
bool result;
if (DatumGetBoxP(entry->key) == NULL || query == NULL)
PG_RETURN_BOOL(FALSE);
/*
* Since the operators are marked lossy anyway, we can just use
* rtree_internal_consistent even at leaf nodes. (This works in part
* because the index entries are bounding boxes not circles.)
*/
bbox.high.x = query->center.x + query->radius;
bbox.low.x = query->center.x - query->radius;
bbox.high.y = query->center.y + query->radius;
bbox.low.y = query->center.y - query->radius;
result = rtree_internal_consistent(DatumGetBoxP(entry->key),
&bbox, strategy);
PG_RETURN_BOOL(result);
}