1 : /*-------------------------------------------------------------------------
2 : *
3 : * gistproc.c
4 : * Support procedures for GiSTs over 2-D objects (boxes, polygons, circles).
5 : *
6 : * This gives R-tree behavior, with Guttman's poly-time split algorithm.
7 : *
8 : *
9 : * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
10 : * Portions Copyright (c) 1994, Regents of the University of California
11 : *
12 : * IDENTIFICATION
13 : * $PostgreSQL: pgsql/src/backend/access/gist/gistproc.c,v 1.12 2007/11/15 21:14:31 momjian Exp $
14 : *
15 : *-------------------------------------------------------------------------
16 : */
17 : #include "postgres.h"
18 :
19 : #include "access/gist.h"
20 : #include "access/skey.h"
21 : #include "utils/geo_decls.h"
22 :
23 :
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);
29 :
30 :
31 : /**************************************************
32 : * Box ops
33 : **************************************************/
34 :
35 : static Datum
36 : rt_box_union(PG_FUNCTION_ARGS)
37 94306 : {
38 94306 : BOX *a = PG_GETARG_BOX_P(0);
39 94306 : BOX *b = PG_GETARG_BOX_P(1);
40 : BOX *n;
41 :
42 94306 : n = (BOX *) palloc(sizeof(BOX));
43 :
44 94306 : n->high.x = Max(a->high.x, b->high.x);
45 94306 : n->high.y = Max(a->high.y, b->high.y);
46 94306 : n->low.x = Min(a->low.x, b->low.x);
47 94306 : n->low.y = Min(a->low.y, b->low.y);
48 :
49 94306 : PG_RETURN_BOX_P(n);
50 : }
51 :
52 : static Datum
53 : rt_box_inter(PG_FUNCTION_ARGS)
54 8 : {
55 8 : BOX *a = PG_GETARG_BOX_P(0);
56 8 : BOX *b = PG_GETARG_BOX_P(1);
57 : BOX *n;
58 :
59 8 : n = (BOX *) palloc(sizeof(BOX));
60 :
61 8 : n->high.x = Min(a->high.x, b->high.x);
62 8 : n->high.y = Min(a->high.y, b->high.y);
63 8 : n->low.x = Max(a->low.x, b->low.x);
64 8 : n->low.y = Max(a->low.y, b->low.y);
65 :
66 8 : if (n->high.x < n->low.x || n->high.y < n->low.y)
67 : {
68 8 : pfree(n);
69 : /* Indicate "no intersection" by returning NULL pointer */
70 8 : n = NULL;
71 : }
72 :
73 8 : PG_RETURN_BOX_P(n);
74 : }
75 :
76 : /*
77 : * The GiST Consistent method for boxes
78 : *
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.
82 : */
83 : Datum
84 : gist_box_consistent(PG_FUNCTION_ARGS)
85 260 : {
86 260 : GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
87 260 : BOX *query = PG_GETARG_BOX_P(1);
88 260 : StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2);
89 :
90 260 : if (DatumGetBoxP(entry->key) == NULL || query == NULL)
91 0 : PG_RETURN_BOOL(FALSE);
92 :
93 : /*
94 : * if entry is not leaf, use rtree_internal_consistent, else use
95 : * gist_box_leaf_consistent
96 : */
97 260 : if (GIST_LEAF(entry))
98 198 : PG_RETURN_BOOL(gist_box_leaf_consistent(DatumGetBoxP(entry->key),
99 : query,
100 : strategy));
101 : else
102 62 : PG_RETURN_BOOL(rtree_internal_consistent(DatumGetBoxP(entry->key),
103 : query,
104 : strategy));
105 : }
106 :
107 : static void
108 : adjustBox(BOX *b, BOX *addon)
109 23905 : {
110 23905 : if (b->high.x < addon->high.x)
111 688 : b->high.x = addon->high.x;
112 23905 : if (b->low.x > addon->low.x)
113 576 : b->low.x = addon->low.x;
114 23905 : if (b->high.y < addon->high.y)
115 638 : b->high.y = addon->high.y;
116 23905 : if (b->low.y > addon->low.y)
117 596 : b->low.y = addon->low.y;
118 23905 : }
119 :
120 : /*
121 : * The GiST Union method for boxes
122 : *
123 : * returns the minimal bounding box that encloses all the entries in entryvec
124 : */
125 : Datum
126 : gist_box_union(PG_FUNCTION_ARGS)
127 8814 : {
128 8814 : GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
129 8814 : int *sizep = (int *) PG_GETARG_POINTER(1);
130 : int numranges,
131 : i;
132 : BOX *cur,
133 : *pageunion;
134 :
135 8814 : numranges = entryvec->n;
136 8814 : pageunion = (BOX *) palloc(sizeof(BOX));
137 8814 : cur = DatumGetBoxP(entryvec->vector[0].key);
138 8814 : memcpy((void *) pageunion, (void *) cur, sizeof(BOX));
139 :
140 18111 : for (i = 1; i < numranges; i++)
141 : {
142 9297 : cur = DatumGetBoxP(entryvec->vector[i].key);
143 9297 : adjustBox(pageunion, cur);
144 : }
145 8814 : *sizep = sizeof(BOX);
146 :
147 8814 : PG_RETURN_POINTER(pageunion);
148 : }
149 :
150 : /*
151 : * GiST Compress methods for boxes
152 : *
153 : * do not do anything.
154 : */
155 : Datum
156 : gist_box_compress(PG_FUNCTION_ARGS)
157 3299 : {
158 3299 : PG_RETURN_POINTER(PG_GETARG_POINTER(0));
159 : }
160 :
161 : /*
162 : * GiST DeCompress method for boxes (also used for polygons and circles)
163 : *
164 : * do not do anything --- we just use the stored box as is.
165 : */
166 : Datum
167 : gist_box_decompress(PG_FUNCTION_ARGS)
168 137379 : {
169 137379 : PG_RETURN_POINTER(PG_GETARG_POINTER(0));
170 : }
171 :
172 : /*
173 : * The GiST Penalty method for boxes
174 : *
175 : * As in the R-tree paper, we use change in area as our penalty metric
176 : */
177 : Datum
178 : gist_box_penalty(PG_FUNCTION_ARGS)
179 94306 : {
180 94306 : GISTENTRY *origentry = (GISTENTRY *) PG_GETARG_POINTER(0);
181 94306 : GISTENTRY *newentry = (GISTENTRY *) PG_GETARG_POINTER(1);
182 94306 : float *result = (float *) PG_GETARG_POINTER(2);
183 : Datum ud;
184 :
185 94306 : ud = DirectFunctionCall2(rt_box_union, origentry->key, newentry->key);
186 94306 : *result = (float) (size_box(ud) - size_box(origentry->key));
187 94306 : PG_RETURN_POINTER(result);
188 : }
189 :
190 : static void
191 : chooseLR(GIST_SPLITVEC *v,
192 : OffsetNumber *list1, int nlist1, BOX *union1,
193 : OffsetNumber *list2, int nlist2, BOX *union2)
194 88 : {
195 88 : bool firstToLeft = true;
196 :
197 88 : if (v->spl_ldatum_exists || v->spl_rdatum_exists)
198 : {
199 0 : if (v->spl_ldatum_exists && v->spl_rdatum_exists)
200 : {
201 0 : BOX LRl = *union1,
202 0 : LRr = *union2;
203 0 : BOX RLl = *union2,
204 0 : RLr = *union1;
205 : double sizeLR,
206 : sizeRL;
207 :
208 0 : adjustBox(&LRl, DatumGetBoxP(v->spl_ldatum));
209 0 : adjustBox(&LRr, DatumGetBoxP(v->spl_rdatum));
210 0 : adjustBox(&RLl, DatumGetBoxP(v->spl_ldatum));
211 0 : adjustBox(&RLr, DatumGetBoxP(v->spl_rdatum));
212 :
213 0 : sizeLR = size_box(DirectFunctionCall2(rt_box_inter, BoxPGetDatum(&LRl), BoxPGetDatum(&LRr)));
214 0 : sizeRL = size_box(DirectFunctionCall2(rt_box_inter, BoxPGetDatum(&RLl), BoxPGetDatum(&RLr)));
215 :
216 0 : if (sizeLR > sizeRL)
217 0 : firstToLeft = false;
218 :
219 : }
220 : else
221 : {
222 : float p1,
223 : p2;
224 : GISTENTRY oldUnion,
225 : addon;
226 :
227 0 : gistentryinit(oldUnion, (v->spl_ldatum_exists) ? v->spl_ldatum : v->spl_rdatum,
228 : NULL, NULL, InvalidOffsetNumber, FALSE);
229 :
230 0 : gistentryinit(addon, BoxPGetDatum(union1), NULL, NULL, InvalidOffsetNumber, FALSE);
231 0 : DirectFunctionCall3(gist_box_penalty, PointerGetDatum(&oldUnion), PointerGetDatum(&union1), PointerGetDatum(&p1));
232 0 : gistentryinit(addon, BoxPGetDatum(union2), NULL, NULL, InvalidOffsetNumber, FALSE);
233 0 : DirectFunctionCall3(gist_box_penalty, PointerGetDatum(&oldUnion), PointerGetDatum(&union2), PointerGetDatum(&p2));
234 :
235 0 : if ((v->spl_ldatum_exists && p1 > p2) || (v->spl_rdatum_exists && p1 < p2))
236 0 : firstToLeft = false;
237 : }
238 : }
239 :
240 88 : if (firstToLeft)
241 : {
242 88 : v->spl_left = list1;
243 88 : v->spl_right = list2;
244 88 : v->spl_nleft = nlist1;
245 88 : v->spl_nright = nlist2;
246 88 : if (v->spl_ldatum_exists)
247 0 : adjustBox(union1, DatumGetBoxP(v->spl_ldatum));
248 88 : v->spl_ldatum = BoxPGetDatum(union1);
249 88 : if (v->spl_rdatum_exists)
250 0 : adjustBox(union2, DatumGetBoxP(v->spl_rdatum));
251 88 : v->spl_rdatum = BoxPGetDatum(union2);
252 : }
253 : else
254 : {
255 0 : v->spl_left = list2;
256 0 : v->spl_right = list1;
257 0 : v->spl_nleft = nlist2;
258 0 : v->spl_nright = nlist1;
259 0 : if (v->spl_ldatum_exists)
260 0 : adjustBox(union2, DatumGetBoxP(v->spl_ldatum));
261 0 : v->spl_ldatum = BoxPGetDatum(union2);
262 0 : if (v->spl_rdatum_exists)
263 0 : adjustBox(union1, DatumGetBoxP(v->spl_rdatum));
264 0 : v->spl_rdatum = BoxPGetDatum(union1);
265 : }
266 :
267 88 : v->spl_ldatum_exists = v->spl_rdatum_exists = false;
268 88 : }
269 :
270 : /*
271 : * The GiST PickSplit method
272 : *
273 : * New linear algorithm, see 'New Linear Node Splitting Algorithm for R-tree',
274 : * C.H.Ang and T.C.Tan
275 : */
276 : Datum
277 : gist_box_picksplit(PG_FUNCTION_ARGS)
278 88 : {
279 88 : GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
280 88 : GIST_SPLITVEC *v = (GIST_SPLITVEC *) PG_GETARG_POINTER(1);
281 : OffsetNumber i;
282 : OffsetNumber *listL,
283 : *listR,
284 : *listB,
285 : *listT;
286 : BOX *unionL,
287 : *unionR,
288 : *unionB,
289 : *unionT;
290 : int posL,
291 : posR,
292 : posB,
293 : posT;
294 : BOX pageunion;
295 : BOX *cur;
296 88 : char direction = ' ';
297 88 : bool allisequal = true;
298 : OffsetNumber maxoff;
299 : int nbytes;
300 :
301 88 : posL = posR = posB = posT = 0;
302 88 : maxoff = entryvec->n - 1;
303 :
304 88 : cur = DatumGetBoxP(entryvec->vector[FirstOffsetNumber].key);
305 88 : memcpy((void *) &pageunion, (void *) cur, sizeof(BOX));
306 :
307 : /* find MBR */
308 14696 : for (i = OffsetNumberNext(FirstOffsetNumber); i <= maxoff; i = OffsetNumberNext(i))
309 : {
310 14608 : cur = DatumGetBoxP(entryvec->vector[i].key);
311 14608 : if (allisequal == true && (
312 : pageunion.high.x != cur->high.x ||
313 : pageunion.high.y != cur->high.y ||
314 : pageunion.low.x != cur->low.x ||
315 : pageunion.low.y != cur->low.y
316 : ))
317 88 : allisequal = false;
318 :
319 14608 : adjustBox(&pageunion, cur);
320 : }
321 :
322 88 : nbytes = (maxoff + 2) * sizeof(OffsetNumber);
323 88 : listL = (OffsetNumber *) palloc(nbytes);
324 88 : listR = (OffsetNumber *) palloc(nbytes);
325 88 : unionL = (BOX *) palloc(sizeof(BOX));
326 88 : unionR = (BOX *) palloc(sizeof(BOX));
327 88 : if (allisequal)
328 : {
329 0 : cur = DatumGetBoxP(entryvec->vector[OffsetNumberNext(FirstOffsetNumber)].key);
330 0 : if (memcmp((void *) cur, (void *) &pageunion, sizeof(BOX)) == 0)
331 : {
332 0 : v->spl_left = listL;
333 0 : v->spl_right = listR;
334 0 : v->spl_nleft = v->spl_nright = 0;
335 0 : memcpy((void *) unionL, (void *) &pageunion, sizeof(BOX));
336 0 : memcpy((void *) unionR, (void *) &pageunion, sizeof(BOX));
337 :
338 0 : for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
339 : {
340 0 : if (i <= (maxoff - FirstOffsetNumber + 1) / 2)
341 : {
342 0 : v->spl_left[v->spl_nleft] = i;
343 0 : v->spl_nleft++;
344 : }
345 : else
346 : {
347 0 : v->spl_right[v->spl_nright] = i;
348 0 : v->spl_nright++;
349 : }
350 : }
351 :
352 0 : if (v->spl_ldatum_exists)
353 0 : adjustBox(unionL, DatumGetBoxP(v->spl_ldatum));
354 0 : v->spl_ldatum = BoxPGetDatum(unionL);
355 :
356 0 : if (v->spl_rdatum_exists)
357 0 : adjustBox(unionR, DatumGetBoxP(v->spl_rdatum));
358 0 : v->spl_rdatum = BoxPGetDatum(unionR);
359 :
360 0 : v->spl_ldatum_exists = v->spl_rdatum_exists = false;
361 :
362 0 : PG_RETURN_POINTER(v);
363 : }
364 : }
365 :
366 88 : listB = (OffsetNumber *) palloc(nbytes);
367 88 : listT = (OffsetNumber *) palloc(nbytes);
368 88 : unionB = (BOX *) palloc(sizeof(BOX));
369 88 : unionT = (BOX *) palloc(sizeof(BOX));
370 :
371 : #define ADDLIST( list, unionD, pos, num ) do { \
372 : if ( pos ) { \
373 : if ( (unionD)->high.x < cur->high.x ) (unionD)->high.x = cur->high.x; \
374 : if ( (unionD)->low.x > cur->low.x ) (unionD)->low.x = cur->low.x; \
375 : if ( (unionD)->high.y < cur->high.y ) (unionD)->high.y = cur->high.y; \
376 : if ( (unionD)->low.y > cur->low.y ) (unionD)->low.y = cur->low.y; \
377 : } else { \
378 : memcpy( (void*)(unionD), (void*) cur, sizeof( BOX ) ); \
379 : } \
380 : (list)[pos] = num; \
381 : (pos)++; \
382 : } while(0)
383 :
384 14784 : for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
385 : {
386 14696 : cur = DatumGetBoxP(entryvec->vector[i].key);
387 14696 : if (cur->low.x - pageunion.low.x < pageunion.high.x - cur->high.x)
388 7277 : ADDLIST(listL, unionL, posL, i);
389 : else
390 7419 : ADDLIST(listR, unionR, posR, i);
391 14696 : if (cur->low.y - pageunion.low.y < pageunion.high.y - cur->high.y)
392 7059 : ADDLIST(listB, unionB, posB, i);
393 : else
394 7637 : ADDLIST(listT, unionT, posT, i);
395 : }
396 :
397 : #define LIMIT_RATIO 0.1
398 : #define _IS_BADRATIO(x,y) ( (y) == 0 || (float)(x)/(float)(y) < LIMIT_RATIO )
399 : #define IS_BADRATIO(x,y) ( _IS_BADRATIO((x),(y)) || _IS_BADRATIO((y),(x)) )
400 : /* bad disposition, try to split by centers of boxes */
401 88 : if (IS_BADRATIO(posR, posL) && IS_BADRATIO(posT, posB))
402 : {
403 0 : double avgCenterX = 0.0,
404 0 : avgCenterY = 0.0;
405 : double CenterX,
406 : CenterY;
407 :
408 0 : for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
409 : {
410 0 : cur = DatumGetBoxP(entryvec->vector[i].key);
411 0 : avgCenterX += ((double) cur->high.x + (double) cur->low.x) / 2.0;
412 0 : avgCenterY += ((double) cur->high.y + (double) cur->low.y) / 2.0;
413 : }
414 :
415 0 : avgCenterX /= maxoff;
416 0 : avgCenterY /= maxoff;
417 :
418 0 : posL = posR = posB = posT = 0;
419 0 : for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
420 : {
421 0 : cur = DatumGetBoxP(entryvec->vector[i].key);
422 :
423 0 : CenterX = ((double) cur->high.x + (double) cur->low.x) / 2.0;
424 0 : CenterY = ((double) cur->high.y + (double) cur->low.y) / 2.0;
425 :
426 0 : if (CenterX < avgCenterX)
427 0 : ADDLIST(listL, unionL, posL, i);
428 0 : else if (CenterX == avgCenterX)
429 : {
430 0 : if (posL > posR)
431 0 : ADDLIST(listR, unionR, posR, i);
432 : else
433 0 : ADDLIST(listL, unionL, posL, i);
434 : }
435 : else
436 0 : ADDLIST(listR, unionR, posR, i);
437 :
438 0 : if (CenterY < avgCenterY)
439 0 : ADDLIST(listB, unionB, posB, i);
440 0 : else if (CenterY == avgCenterY)
441 : {
442 0 : if (posB > posT)
443 0 : ADDLIST(listT, unionT, posT, i);
444 : else
445 0 : ADDLIST(listB, unionB, posB, i);
446 : }
447 : else
448 0 : ADDLIST(listT, unionT, posT, i);
449 : }
450 : }
451 :
452 : /* which split more optimal? */
453 88 : if (Max(posL, posR) < Max(posB, posT))
454 48 : direction = 'x';
455 40 : else if (Max(posL, posR) > Max(posB, posT))
456 36 : direction = 'y';
457 : else
458 : {
459 : Datum interLR = DirectFunctionCall2(rt_box_inter,
460 : BoxPGetDatum(unionL),
461 4 : BoxPGetDatum(unionR));
462 : Datum interBT = DirectFunctionCall2(rt_box_inter,
463 : BoxPGetDatum(unionB),
464 4 : BoxPGetDatum(unionT));
465 : double sizeLR,
466 : sizeBT;
467 :
468 4 : sizeLR = size_box(interLR);
469 4 : sizeBT = size_box(interBT);
470 :
471 4 : if (sizeLR < sizeBT)
472 0 : direction = 'x';
473 : else
474 4 : direction = 'y';
475 : }
476 :
477 88 : if (direction == 'x')
478 48 : chooseLR(v,
479 : listL, posL, unionL,
480 : listR, posR, unionR);
481 : else
482 40 : chooseLR(v,
483 : listB, posB, unionB,
484 : listT, posT, unionT);
485 :
486 88 : PG_RETURN_POINTER(v);
487 : }
488 :
489 : /*
490 : * Equality method
491 : */
492 : Datum
493 : gist_box_same(PG_FUNCTION_ARGS)
494 8723 : {
495 8723 : BOX *b1 = PG_GETARG_BOX_P(0);
496 8723 : BOX *b2 = PG_GETARG_BOX_P(1);
497 8723 : bool *result = (bool *) PG_GETARG_POINTER(2);
498 :
499 8723 : if (b1 && b2)
500 8723 : *result = DatumGetBool(DirectFunctionCall2(box_same,
501 : PointerGetDatum(b1),
502 : PointerGetDatum(b2)));
503 : else
504 0 : *result = (b1 == NULL && b2 == NULL) ? TRUE : FALSE;
505 8723 : PG_RETURN_POINTER(result);
506 : }
507 :
508 : /*
509 : * Leaf-level consistency for boxes: just apply the query operator
510 : */
511 : static bool
512 : gist_box_leaf_consistent(BOX *key, BOX *query, StrategyNumber strategy)
513 198 : {
514 : bool retval;
515 :
516 198 : switch (strategy)
517 : {
518 : case RTLeftStrategyNumber:
519 0 : retval = DatumGetBool(DirectFunctionCall2(box_left,
520 : PointerGetDatum(key),
521 : PointerGetDatum(query)));
522 0 : break;
523 : case RTOverLeftStrategyNumber:
524 0 : retval = DatumGetBool(DirectFunctionCall2(box_overleft,
525 : PointerGetDatum(key),
526 : PointerGetDatum(query)));
527 0 : break;
528 : case RTOverlapStrategyNumber:
529 99 : retval = DatumGetBool(DirectFunctionCall2(box_overlap,
530 : PointerGetDatum(key),
531 : PointerGetDatum(query)));
532 99 : break;
533 : case RTOverRightStrategyNumber:
534 0 : retval = DatumGetBool(DirectFunctionCall2(box_overright,
535 : PointerGetDatum(key),
536 : PointerGetDatum(query)));
537 0 : break;
538 : case RTRightStrategyNumber:
539 0 : retval = DatumGetBool(DirectFunctionCall2(box_right,
540 : PointerGetDatum(key),
541 : PointerGetDatum(query)));
542 0 : break;
543 : case RTSameStrategyNumber:
544 0 : retval = DatumGetBool(DirectFunctionCall2(box_same,
545 : PointerGetDatum(key),
546 : PointerGetDatum(query)));
547 0 : break;
548 : case RTContainsStrategyNumber:
549 : case RTOldContainsStrategyNumber:
550 0 : retval = DatumGetBool(DirectFunctionCall2(box_contain,
551 : PointerGetDatum(key),
552 : PointerGetDatum(query)));
553 0 : break;
554 : case RTContainedByStrategyNumber:
555 : case RTOldContainedByStrategyNumber:
556 99 : retval = DatumGetBool(DirectFunctionCall2(box_contained,
557 : PointerGetDatum(key),
558 : PointerGetDatum(query)));
559 99 : break;
560 : case RTOverBelowStrategyNumber:
561 0 : retval = DatumGetBool(DirectFunctionCall2(box_overbelow,
562 : PointerGetDatum(key),
563 : PointerGetDatum(query)));
564 0 : break;
565 : case RTBelowStrategyNumber:
566 0 : retval = DatumGetBool(DirectFunctionCall2(box_below,
567 : PointerGetDatum(key),
568 : PointerGetDatum(query)));
569 0 : break;
570 : case RTAboveStrategyNumber:
571 0 : retval = DatumGetBool(DirectFunctionCall2(box_above,
572 : PointerGetDatum(key),
573 : PointerGetDatum(query)));
574 0 : break;
575 : case RTOverAboveStrategyNumber:
576 0 : retval = DatumGetBool(DirectFunctionCall2(box_overabove,
577 : PointerGetDatum(key),
578 : PointerGetDatum(query)));
579 0 : break;
580 : default:
581 0 : retval = FALSE;
582 : }
583 198 : return retval;
584 : }
585 :
586 : static double
587 : size_box(Datum dbox)
588 188620 : {
589 188620 : BOX *box = DatumGetBoxP(dbox);
590 :
591 188620 : if (box == NULL || box->high.x <= box->low.x || box->high.y <= box->low.y)
592 8 : return 0.0;
593 188612 : return (box->high.x - box->low.x) * (box->high.y - box->low.y);
594 : }
595 :
596 : /*****************************************
597 : * Common rtree functions (for boxes, polygons, and circles)
598 : *****************************************/
599 :
600 : /*
601 : * Internal-page consistency for all these types
602 : *
603 : * We can use the same function since all types use bounding boxes as the
604 : * internal-page representation.
605 : */
606 : static bool
607 : rtree_internal_consistent(BOX *key, BOX *query, StrategyNumber strategy)
608 768 : {
609 : bool retval;
610 :
611 768 : switch (strategy)
612 : {
613 : case RTLeftStrategyNumber:
614 0 : retval = !DatumGetBool(DirectFunctionCall2(box_overright,
615 : PointerGetDatum(key),
616 : PointerGetDatum(query)));
617 0 : break;
618 : case RTOverLeftStrategyNumber:
619 0 : retval = !DatumGetBool(DirectFunctionCall2(box_right,
620 : PointerGetDatum(key),
621 : PointerGetDatum(query)));
622 0 : break;
623 : case RTOverlapStrategyNumber:
624 733 : retval = DatumGetBool(DirectFunctionCall2(box_overlap,
625 : PointerGetDatum(key),
626 : PointerGetDatum(query)));
627 733 : break;
628 : case RTOverRightStrategyNumber:
629 0 : retval = !DatumGetBool(DirectFunctionCall2(box_left,
630 : PointerGetDatum(key),
631 : PointerGetDatum(query)));
632 0 : break;
633 : case RTRightStrategyNumber:
634 0 : retval = !DatumGetBool(DirectFunctionCall2(box_overleft,
635 : PointerGetDatum(key),
636 : PointerGetDatum(query)));
637 0 : break;
638 : case RTSameStrategyNumber:
639 : case RTContainsStrategyNumber:
640 : case RTOldContainsStrategyNumber:
641 4 : retval = DatumGetBool(DirectFunctionCall2(box_contain,
642 : PointerGetDatum(key),
643 : PointerGetDatum(query)));
644 4 : break;
645 : case RTContainedByStrategyNumber:
646 : case RTOldContainedByStrategyNumber:
647 31 : retval = DatumGetBool(DirectFunctionCall2(box_overlap,
648 : PointerGetDatum(key),
649 : PointerGetDatum(query)));
650 31 : break;
651 : case RTOverBelowStrategyNumber:
652 0 : retval = !DatumGetBool(DirectFunctionCall2(box_above,
653 : PointerGetDatum(key),
654 : PointerGetDatum(query)));
655 0 : break;
656 : case RTBelowStrategyNumber:
657 0 : retval = !DatumGetBool(DirectFunctionCall2(box_overabove,
658 : PointerGetDatum(key),
659 : PointerGetDatum(query)));
660 0 : break;
661 : case RTAboveStrategyNumber:
662 0 : retval = !DatumGetBool(DirectFunctionCall2(box_overbelow,
663 : PointerGetDatum(key),
664 : PointerGetDatum(query)));
665 0 : break;
666 : case RTOverAboveStrategyNumber:
667 0 : retval = !DatumGetBool(DirectFunctionCall2(box_below,
668 : PointerGetDatum(key),
669 : PointerGetDatum(query)));
670 0 : break;
671 : default:
672 0 : retval = FALSE;
673 : }
674 768 : return retval;
675 : }
676 :
677 : /**************************************************
678 : * Polygon ops
679 : **************************************************/
680 :
681 : /*
682 : * GiST compress for polygons: represent a polygon by its bounding box
683 : */
684 : Datum
685 : gist_poly_compress(PG_FUNCTION_ARGS)
686 3303 : {
687 3303 : GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
688 : GISTENTRY *retval;
689 :
690 3303 : if (entry->leafkey)
691 : {
692 3104 : retval = palloc(sizeof(GISTENTRY));
693 3104 : if (DatumGetPointer(entry->key) != NULL)
694 : {
695 3104 : POLYGON *in = DatumGetPolygonP(entry->key);
696 : BOX *r;
697 :
698 3104 : r = (BOX *) palloc(sizeof(BOX));
699 3104 : memcpy((void *) r, (void *) &(in->boundbox), sizeof(BOX));
700 3104 : gistentryinit(*retval, PointerGetDatum(r),
701 : entry->rel, entry->page,
702 : entry->offset, FALSE);
703 :
704 : }
705 : else
706 : {
707 0 : gistentryinit(*retval, (Datum) 0,
708 : entry->rel, entry->page,
709 : entry->offset, FALSE);
710 : }
711 : }
712 : else
713 199 : retval = entry;
714 3303 : PG_RETURN_POINTER(retval);
715 : }
716 :
717 : /*
718 : * The GiST Consistent method for polygons
719 : */
720 : Datum
721 : gist_poly_consistent(PG_FUNCTION_ARGS)
722 134 : {
723 134 : GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
724 134 : POLYGON *query = PG_GETARG_POLYGON_P(1);
725 134 : StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2);
726 : bool result;
727 :
728 134 : if (DatumGetBoxP(entry->key) == NULL || query == NULL)
729 0 : PG_RETURN_BOOL(FALSE);
730 :
731 : /*
732 : * Since the operators are marked lossy anyway, we can just use
733 : * rtree_internal_consistent even at leaf nodes. (This works in part
734 : * because the index entries are bounding boxes not polygons.)
735 : */
736 134 : result = rtree_internal_consistent(DatumGetBoxP(entry->key),
737 : &(query->boundbox), strategy);
738 :
739 : /* Avoid memory leak if supplied poly is toasted */
740 134 : PG_FREE_IF_COPY(query, 1);
741 :
742 134 : PG_RETURN_BOOL(result);
743 : }
744 :
745 : /**************************************************
746 : * Circle ops
747 : **************************************************/
748 :
749 : /*
750 : * GiST compress for circles: represent a circle by its bounding box
751 : */
752 : Datum
753 : gist_circle_compress(PG_FUNCTION_ARGS)
754 3378 : {
755 3378 : GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
756 : GISTENTRY *retval;
757 :
758 3378 : if (entry->leafkey)
759 : {
760 3106 : retval = palloc(sizeof(GISTENTRY));
761 3106 : if (DatumGetCircleP(entry->key) != NULL)
762 : {
763 3106 : CIRCLE *in = DatumGetCircleP(entry->key);
764 : BOX *r;
765 :
766 3106 : r = (BOX *) palloc(sizeof(BOX));
767 3106 : r->high.x = in->center.x + in->radius;
768 3106 : r->low.x = in->center.x - in->radius;
769 3106 : r->high.y = in->center.y + in->radius;
770 3106 : r->low.y = in->center.y - in->radius;
771 3106 : gistentryinit(*retval, PointerGetDatum(r),
772 : entry->rel, entry->page,
773 : entry->offset, FALSE);
774 :
775 : }
776 : else
777 : {
778 0 : gistentryinit(*retval, (Datum) 0,
779 : entry->rel, entry->page,
780 : entry->offset, FALSE);
781 : }
782 : }
783 : else
784 272 : retval = entry;
785 3378 : PG_RETURN_POINTER(retval);
786 : }
787 :
788 : /*
789 : * The GiST Consistent method for circles
790 : */
791 : Datum
792 : gist_circle_consistent(PG_FUNCTION_ARGS)
793 572 : {
794 572 : GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
795 572 : CIRCLE *query = PG_GETARG_CIRCLE_P(1);
796 572 : StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2);
797 : BOX bbox;
798 : bool result;
799 :
800 572 : if (DatumGetBoxP(entry->key) == NULL || query == NULL)
801 0 : PG_RETURN_BOOL(FALSE);
802 :
803 : /*
804 : * Since the operators are marked lossy anyway, we can just use
805 : * rtree_internal_consistent even at leaf nodes. (This works in part
806 : * because the index entries are bounding boxes not circles.)
807 : */
808 572 : bbox.high.x = query->center.x + query->radius;
809 572 : bbox.low.x = query->center.x - query->radius;
810 572 : bbox.high.y = query->center.y + query->radius;
811 572 : bbox.low.y = query->center.y - query->radius;
812 :
813 572 : result = rtree_internal_consistent(DatumGetBoxP(entry->key),
814 : &bbox, strategy);
815 :
816 572 : PG_RETURN_BOOL(result);
817 : }
|