1
/*-------------------------------------------------------------------------
4
* pg_amproc entries for GiSTs over 2-D boxes.
5
* This gives R-tree behavior, with Guttman's poly-time split algorithm.
10
* $PostgreSQL: pgsql/contrib/rtree_gist/rtree_gist.c,v 1.10 2004-08-29 05:06:37 momjian Exp $
12
*-------------------------------------------------------------------------
16
#include "access/gist.h"
17
#include "access/itup.h"
18
#include "access/rtree.h"
19
#include "utils/geo_decls.h"
21
typedef Datum (*RDF) (PG_FUNCTION_ARGS);
22
typedef Datum (*BINARY_UNION) (Datum, Datum, int *);
23
typedef float (*SIZE_BOX) (Datum);
28
PG_FUNCTION_INFO_V1(gbox_compress);
29
PG_FUNCTION_INFO_V1(gbox_union);
30
PG_FUNCTION_INFO_V1(gbox_picksplit);
31
PG_FUNCTION_INFO_V1(gbox_consistent);
32
PG_FUNCTION_INFO_V1(gbox_penalty);
33
PG_FUNCTION_INFO_V1(gbox_same);
35
Datum gbox_compress(PG_FUNCTION_ARGS);
36
Datum gbox_union(PG_FUNCTION_ARGS);
37
Datum gbox_picksplit(PG_FUNCTION_ARGS);
38
Datum gbox_consistent(PG_FUNCTION_ARGS);
39
Datum gbox_penalty(PG_FUNCTION_ARGS);
40
Datum gbox_same(PG_FUNCTION_ARGS);
42
static bool gbox_leaf_consistent(BOX *key, BOX *query, StrategyNumber strategy);
43
static float size_box(Datum box);
48
PG_FUNCTION_INFO_V1(gpoly_compress);
49
PG_FUNCTION_INFO_V1(gpoly_consistent);
51
Datum gpoly_compress(PG_FUNCTION_ARGS);
52
Datum gpoly_consistent(PG_FUNCTION_ARGS);
55
** Common rtree-function (for all ops)
57
static bool rtree_internal_consistent(BOX *key, BOX *query, StrategyNumber strategy);
59
PG_FUNCTION_INFO_V1(rtree_decompress);
61
Datum rtree_decompress(PG_FUNCTION_ARGS);
63
/**************************************************
65
**************************************************/
68
** The GiST Consistent method for boxes
69
** Should return false if for all data items x below entry,
70
** the predicate x op query == FALSE, where op is the oper
71
** corresponding to strategy in the pg_amop table.
74
gbox_consistent(PG_FUNCTION_ARGS)
76
GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
77
BOX *query = (BOX *) PG_GETARG_POINTER(1);
78
StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2);
81
* * if entry is not leaf, use gbox_internal_consistent, * else use
82
* gbox_leaf_consistent
84
if (!(DatumGetPointer(entry->key) != NULL && query))
85
PG_RETURN_BOOL(FALSE);
88
PG_RETURN_BOOL(gbox_leaf_consistent((BOX *) DatumGetPointer(entry->key), query, strategy));
90
PG_RETURN_BOOL(rtree_internal_consistent((BOX *) DatumGetPointer(entry->key), query, strategy));
95
** The GiST Union method for boxes
96
** returns the minimal bounding box that encloses all the entries in entryvec
99
gbox_union(PG_FUNCTION_ARGS)
101
GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
102
int *sizep = (int *) PG_GETARG_POINTER(1);
108
numranges = entryvec->n;
109
pageunion = (BOX *) palloc(sizeof(BOX));
110
cur = DatumGetBoxP(entryvec->vector[0].key);
111
memcpy((void *) pageunion, (void *) cur, sizeof(BOX));
113
for (i = 1; i < numranges; i++)
115
cur = DatumGetBoxP(entryvec->vector[i].key);
116
if (pageunion->high.x < cur->high.x)
117
pageunion->high.x = cur->high.x;
118
if (pageunion->low.x > cur->low.x)
119
pageunion->low.x = cur->low.x;
120
if (pageunion->high.y < cur->high.y)
121
pageunion->high.y = cur->high.y;
122
if (pageunion->low.y > cur->low.y)
123
pageunion->low.y = cur->low.y;
125
*sizep = sizeof(BOX);
127
PG_RETURN_POINTER(pageunion);
131
** GiST Compress methods for boxes
132
** do not do anything.
135
gbox_compress(PG_FUNCTION_ARGS)
137
PG_RETURN_POINTER(PG_GETARG_POINTER(0));
141
** The GiST Penalty method for boxes
142
** As in the R-tree paper, we use change in area as our penalty metric
145
gbox_penalty(PG_FUNCTION_ARGS)
147
GISTENTRY *origentry = (GISTENTRY *) PG_GETARG_POINTER(0);
148
GISTENTRY *newentry = (GISTENTRY *) PG_GETARG_POINTER(1);
149
float *result = (float *) PG_GETARG_POINTER(2);
153
ud = DirectFunctionCall2(rt_box_union, origentry->key, newentry->key);
155
if (DatumGetPointer(ud) != NULL)
156
pfree(DatumGetPointer(ud));
158
*result = tmp1 - size_box(origentry->key);
159
PG_RETURN_POINTER(result);
169
compare_KB(const void *a, const void *b)
171
BOX *abox = ((KBsort *) a)->key;
172
BOX *bbox = ((KBsort *) b)->key;
173
float sa = (abox->high.x - abox->low.x) * (abox->high.y - abox->low.y);
174
float sb = (bbox->high.x - bbox->low.x) * (bbox->high.y - bbox->low.y);
178
return (sa > sb) ? 1 : -1;
182
** The GiST PickSplit method
183
** New linear algorithm, see 'New Linear Node Splitting Algorithm for R-tree',
184
** C.H.Ang and T.C.Tan
187
gbox_picksplit(PG_FUNCTION_ARGS)
189
GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
190
GIST_SPLITVEC *v = (GIST_SPLITVEC *) PG_GETARG_POINTER(1);
206
char direction = ' ';
207
bool allisequal = true;
211
posL = posR = posB = posT = 0;
212
maxoff = entryvec->n - 1;
214
cur = DatumGetBoxP(entryvec->vector[FirstOffsetNumber].key);
215
memcpy((void *) &pageunion, (void *) cur, sizeof(BOX));
218
for (i = OffsetNumberNext(FirstOffsetNumber); i <= maxoff; i = OffsetNumberNext(i))
220
cur = DatumGetBoxP(entryvec->vector[i].key);
221
if (allisequal == true && (
222
pageunion.high.x != cur->high.x ||
223
pageunion.high.y != cur->high.y ||
224
pageunion.low.x != cur->low.x ||
225
pageunion.low.y != cur->low.y
229
if (pageunion.high.x < cur->high.x)
230
pageunion.high.x = cur->high.x;
231
if (pageunion.low.x > cur->low.x)
232
pageunion.low.x = cur->low.x;
233
if (pageunion.high.y < cur->high.y)
234
pageunion.high.y = cur->high.y;
235
if (pageunion.low.y > cur->low.y)
236
pageunion.low.y = cur->low.y;
239
nbytes = (maxoff + 2) * sizeof(OffsetNumber);
240
listL = (OffsetNumber *) palloc(nbytes);
241
listR = (OffsetNumber *) palloc(nbytes);
242
unionL = (BOX *) palloc(sizeof(BOX));
243
unionR = (BOX *) palloc(sizeof(BOX));
246
cur = DatumGetBoxP(entryvec->vector[OffsetNumberNext(FirstOffsetNumber)].key);
247
if (memcmp((void *) cur, (void *) &pageunion, sizeof(BOX)) == 0)
250
v->spl_right = listR;
251
v->spl_nleft = v->spl_nright = 0;
252
memcpy((void *) unionL, (void *) &pageunion, sizeof(BOX));
253
memcpy((void *) unionR, (void *) &pageunion, sizeof(BOX));
255
for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
257
if (i <= (maxoff - FirstOffsetNumber + 1) / 2)
259
v->spl_left[v->spl_nleft] = i;
264
v->spl_right[v->spl_nright] = i;
268
v->spl_ldatum = BoxPGetDatum(unionL);
269
v->spl_rdatum = BoxPGetDatum(unionR);
271
PG_RETURN_POINTER(v);
275
listB = (OffsetNumber *) palloc(nbytes);
276
listT = (OffsetNumber *) palloc(nbytes);
277
unionB = (BOX *) palloc(sizeof(BOX));
278
unionT = (BOX *) palloc(sizeof(BOX));
280
#define ADDLIST( list, unionD, pos, num ) do { \
282
if ( unionD->high.x < cur->high.x ) unionD->high.x = cur->high.x; \
283
if ( unionD->low.x > cur->low.x ) unionD->low.x = cur->low.x; \
284
if ( unionD->high.y < cur->high.y ) unionD->high.y = cur->high.y; \
285
if ( unionD->low.y > cur->low.y ) unionD->low.y = cur->low.y; \
287
memcpy( (void*)unionD, (void*) cur, sizeof( BOX ) ); \
293
for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
295
cur = DatumGetBoxP(entryvec->vector[i].key);
296
if (cur->low.x - pageunion.low.x < pageunion.high.x - cur->high.x)
297
ADDLIST(listL, unionL, posL, i);
299
ADDLIST(listR, unionR, posR, i);
300
if (cur->low.y - pageunion.low.y < pageunion.high.y - cur->high.y)
301
ADDLIST(listB, unionB, posB, i);
303
ADDLIST(listT, unionT, posT, i);
306
/* bad disposition, sort by ascending and resplit */
307
if ((posR == 0 || posL == 0) && (posT == 0 || posB == 0))
309
KBsort *arr = (KBsort *) palloc(sizeof(KBsort) * maxoff);
311
posL = posR = posB = posT = 0;
312
for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
314
arr[i - 1].key = DatumGetBoxP(entryvec->vector[i].key);
317
qsort(arr, maxoff, sizeof(KBsort), compare_KB);
318
for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
320
cur = arr[i - 1].key;
321
if (cur->low.x - pageunion.low.x < pageunion.high.x - cur->high.x)
322
ADDLIST(listL, unionL, posL, arr[i - 1].pos);
323
else if (cur->low.x - pageunion.low.x == pageunion.high.x - cur->high.x)
326
ADDLIST(listR, unionR, posR, arr[i - 1].pos);
328
ADDLIST(listL, unionL, posL, arr[i - 1].pos);
331
ADDLIST(listR, unionR, posR, arr[i - 1].pos);
333
if (cur->low.y - pageunion.low.y < pageunion.high.y - cur->high.y)
334
ADDLIST(listB, unionB, posB, arr[i - 1].pos);
335
else if (cur->low.y - pageunion.low.y == pageunion.high.y - cur->high.y)
338
ADDLIST(listT, unionT, posT, arr[i - 1].pos);
340
ADDLIST(listB, unionB, posB, arr[i - 1].pos);
343
ADDLIST(listT, unionT, posT, arr[i - 1].pos);
348
/* which split more optimal? */
349
if (Max(posL, posR) < Max(posB, posT))
351
else if (Max(posL, posR) > Max(posB, posT))
355
Datum interLR = DirectFunctionCall2(rt_box_inter,
356
BoxPGetDatum(unionL),
357
BoxPGetDatum(unionR));
358
Datum interBT = DirectFunctionCall2(rt_box_inter,
359
BoxPGetDatum(unionB),
360
BoxPGetDatum(unionT));
364
sizeLR = size_box(interLR);
365
sizeBT = size_box(interBT);
373
if (direction == 'x')
381
v->spl_right = listR;
383
v->spl_nright = posR;
384
v->spl_ldatum = BoxPGetDatum(unionL);
385
v->spl_rdatum = BoxPGetDatum(unionR);
395
v->spl_right = listT;
397
v->spl_nright = posT;
398
v->spl_ldatum = BoxPGetDatum(unionB);
399
v->spl_rdatum = BoxPGetDatum(unionT);
402
PG_RETURN_POINTER(v);
409
gbox_same(PG_FUNCTION_ARGS)
411
BOX *b1 = (BOX *) PG_GETARG_POINTER(0);
412
BOX *b2 = (BOX *) PG_GETARG_POINTER(1);
413
bool *result = (bool *) PG_GETARG_POINTER(2);
416
*result = DatumGetBool(DirectFunctionCall2(box_same, PointerGetDatum(b1), PointerGetDatum(b2)));
418
*result = (b1 == NULL && b2 == NULL) ? TRUE : FALSE;
419
PG_RETURN_POINTER(result);
423
** SUPPORT ROUTINES for boxes
426
gbox_leaf_consistent(BOX *key,
428
StrategyNumber strategy)
434
case RTLeftStrategyNumber:
435
retval = DatumGetBool(DirectFunctionCall2(box_left, PointerGetDatum(key), PointerGetDatum(query)));
437
case RTOverLeftStrategyNumber:
438
retval = DatumGetBool(DirectFunctionCall2(box_overleft, PointerGetDatum(key), PointerGetDatum(query)));
440
case RTOverlapStrategyNumber:
441
retval = DatumGetBool(DirectFunctionCall2(box_overlap, PointerGetDatum(key), PointerGetDatum(query)));
443
case RTOverRightStrategyNumber:
444
retval = DatumGetBool(DirectFunctionCall2(box_overright, PointerGetDatum(key), PointerGetDatum(query)));
446
case RTRightStrategyNumber:
447
retval = DatumGetBool(DirectFunctionCall2(box_right, PointerGetDatum(key), PointerGetDatum(query)));
449
case RTSameStrategyNumber:
450
retval = DatumGetBool(DirectFunctionCall2(box_same, PointerGetDatum(key), PointerGetDatum(query)));
452
case RTContainsStrategyNumber:
453
retval = DatumGetBool(DirectFunctionCall2(box_contain, PointerGetDatum(key), PointerGetDatum(query)));
455
case RTContainedByStrategyNumber:
456
retval = DatumGetBool(DirectFunctionCall2(box_contained, PointerGetDatum(key), PointerGetDatum(query)));
467
if (DatumGetPointer(box) != NULL)
471
DirectFunctionCall2(rt_box_size,
472
box, PointerGetDatum(&size));
479
/**************************************************
481
**************************************************/
484
gpoly_compress(PG_FUNCTION_ARGS)
486
GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
491
retval = palloc(sizeof(GISTENTRY));
492
if (DatumGetPointer(entry->key) != NULL)
497
in = (POLYGON *) PG_DETOAST_DATUM(entry->key);
498
r = (BOX *) palloc(sizeof(BOX));
499
memcpy((void *) r, (void *) &(in->boundbox), sizeof(BOX));
500
if (in != (POLYGON *) DatumGetPointer(entry->key))
503
gistentryinit(*retval, PointerGetDatum(r),
504
entry->rel, entry->page,
505
entry->offset, sizeof(BOX), FALSE);
510
gistentryinit(*retval, (Datum) 0,
511
entry->rel, entry->page,
512
entry->offset, 0, FALSE);
517
PG_RETURN_POINTER(retval);
521
gpoly_consistent(PG_FUNCTION_ARGS)
523
GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
524
POLYGON *query = (POLYGON *) PG_DETOAST_DATUM(PG_GETARG_POINTER(1));
525
StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2);
529
* * if entry is not leaf, use gbox_internal_consistent, * else use
530
* gbox_leaf_consistent
532
if (!(DatumGetPointer(entry->key) != NULL && query))
533
PG_RETURN_BOOL(FALSE);
535
result = rtree_internal_consistent((BOX *) DatumGetPointer(entry->key),
536
&(query->boundbox), strategy);
538
PG_FREE_IF_COPY(query, 1);
539
PG_RETURN_BOOL(result);
542
/*****************************************
543
* Common rtree-function (for all ops)
544
*****************************************/
547
rtree_internal_consistent(BOX *key,
549
StrategyNumber strategy)
555
case RTLeftStrategyNumber:
556
case RTOverLeftStrategyNumber:
557
retval = DatumGetBool(DirectFunctionCall2(box_overleft, PointerGetDatum(key), PointerGetDatum(query)));
559
case RTOverlapStrategyNumber:
560
retval = DatumGetBool(DirectFunctionCall2(box_overlap, PointerGetDatum(key), PointerGetDatum(query)));
562
case RTOverRightStrategyNumber:
563
case RTRightStrategyNumber:
564
retval = DatumGetBool(DirectFunctionCall2(box_right, PointerGetDatum(key), PointerGetDatum(query)));
566
case RTSameStrategyNumber:
567
case RTContainsStrategyNumber:
568
retval = DatumGetBool(DirectFunctionCall2(box_contain, PointerGetDatum(key), PointerGetDatum(query)));
570
case RTContainedByStrategyNumber:
571
retval = DatumGetBool(DirectFunctionCall2(box_overlap, PointerGetDatum(key), PointerGetDatum(query)));
580
** GiST DeCompress methods
581
** do not do anything.
584
rtree_decompress(PG_FUNCTION_ARGS)
586
PG_RETURN_POINTER(PG_GETARG_POINTER(0));