]> granicus.if.org Git - postgresql/blob - src/backend/utils/adt/geo_spgist.c
Update copyright for 2019
[postgresql] / src / backend / utils / adt / geo_spgist.c
1 /*-------------------------------------------------------------------------
2  *
3  * geo_spgist.c
4  *        SP-GiST implementation of 4-dimensional quad tree over boxes
5  *
6  * This module provides SP-GiST implementation for boxes using quad tree
7  * analogy in 4-dimensional space.  SP-GiST doesn't allow indexing of
8  * overlapping objects.  We are making 2D objects never-overlapping in
9  * 4D space.  This technique has some benefits compared to traditional
10  * R-Tree which is implemented as GiST.  The performance tests reveal
11  * that this technique especially beneficial with too much overlapping
12  * objects, so called "spaghetti data".
13  *
14  * Unlike the original quad tree, we are splitting the tree into 16
15  * quadrants in 4D space.  It is easier to imagine it as splitting space
16  * two times into 4:
17  *
18  *                              |          |
19  *                              |          |
20  *                              | -----+-----
21  *                              |          |
22  *                              |          |
23  * -------------+-------------
24  *                              |
25  *                              |
26  *                              |
27  *                              |
28  *                              |
29  *
30  * We are using box datatype as the prefix, but we are treating them
31  * as points in 4-dimensional space, because 2D boxes are not enough
32  * to represent the quadrant boundaries in 4D space.  They however are
33  * sufficient to point out the additional boundaries of the next
34  * quadrant.
35  *
36  * We are using traversal values provided by SP-GiST to calculate and
37  * to store the bounds of the quadrants, while traversing into the tree.
38  * Traversal value has all the boundaries in the 4D space, and is capable
39  * of transferring the required boundaries to the following traversal
40  * values.  In conclusion, three things are necessary to calculate the
41  * next traversal value:
42  *
43  *      (1) the traversal value of the parent
44  *      (2) the quadrant of the current node
45  *      (3) the prefix of the current node
46  *
47  * If we visualize them on our simplified drawing (see the drawing above);
48  * transferred boundaries of (1) would be the outer axis, relevant part
49  * of (2) would be the up right part of the other axis, and (3) would be
50  * the inner axis.
51  *
52  * For example, consider the case of overlapping.  When recursion
53  * descends deeper and deeper down the tree, all quadrants in
54  * the current node will be checked for overlapping.  The boundaries
55  * will be re-calculated for all quadrants.  Overlap check answers
56  * the question: can any box from this quadrant overlap with the given
57  * box?  If yes, then this quadrant will be walked.  If no, then this
58  * quadrant will be skipped.
59  *
60  * This method provides restrictions for minimum and maximum values of
61  * every dimension of every corner of the box on every level of the tree
62  * except the root.  For the root node, we are setting the boundaries
63  * that we don't yet have as infinity.
64  *
65  * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
66  * Portions Copyright (c) 1994, Regents of the University of California
67  *
68  * IDENTIFICATION
69  *                      src/backend/utils/adt/geo_spgist.c
70  *
71  *-------------------------------------------------------------------------
72  */
73
74 #include "postgres.h"
75
76 #include "access/spgist.h"
77 #include "access/spgist_private.h"
78 #include "access/stratnum.h"
79 #include "catalog/pg_type.h"
80 #include "utils/float.h"
81 #include "utils/fmgroids.h"
82 #include "utils/fmgrprotos.h"
83 #include "utils/geo_decls.h"
84
85 /*
86  * Comparator for qsort
87  *
88  * We don't need to use the floating point macros in here, because this
89  * is only going to be used in a place to effect the performance
90  * of the index, not the correctness.
91  */
92 static int
93 compareDoubles(const void *a, const void *b)
94 {
95         float8          x = *(float8 *) a;
96         float8          y = *(float8 *) b;
97
98         if (x == y)
99                 return 0;
100         return (x > y) ? 1 : -1;
101 }
102
103 typedef struct
104 {
105         float8          low;
106         float8          high;
107 } Range;
108
109 typedef struct
110 {
111         Range           left;
112         Range           right;
113 } RangeBox;
114
115 typedef struct
116 {
117         RangeBox        range_box_x;
118         RangeBox        range_box_y;
119 } RectBox;
120
121 /*
122  * Calculate the quadrant
123  *
124  * The quadrant is 8 bit unsigned integer with 4 least bits in use.
125  * This function accepts BOXes as input.  They are not casted to
126  * RangeBoxes, yet.  All 4 bits are set by comparing a corner of the box.
127  * This makes 16 quadrants in total.
128  */
129 static uint8
130 getQuadrant(BOX *centroid, BOX *inBox)
131 {
132         uint8           quadrant = 0;
133
134         if (inBox->low.x > centroid->low.x)
135                 quadrant |= 0x8;
136
137         if (inBox->high.x > centroid->high.x)
138                 quadrant |= 0x4;
139
140         if (inBox->low.y > centroid->low.y)
141                 quadrant |= 0x2;
142
143         if (inBox->high.y > centroid->high.y)
144                 quadrant |= 0x1;
145
146         return quadrant;
147 }
148
149 /*
150  * Get RangeBox using BOX
151  *
152  * We are turning the BOX to our structures to emphasize their function
153  * of representing points in 4D space.  It also is more convenient to
154  * access the values with this structure.
155  */
156 static RangeBox *
157 getRangeBox(BOX *box)
158 {
159         RangeBox   *range_box = (RangeBox *) palloc(sizeof(RangeBox));
160
161         range_box->left.low = box->low.x;
162         range_box->left.high = box->high.x;
163
164         range_box->right.low = box->low.y;
165         range_box->right.high = box->high.y;
166
167         return range_box;
168 }
169
170 /*
171  * Initialize the traversal value
172  *
173  * In the beginning, we don't have any restrictions.  We have to
174  * initialize the struct to cover the whole 4D space.
175  */
176 static RectBox *
177 initRectBox(void)
178 {
179         RectBox    *rect_box = (RectBox *) palloc(sizeof(RectBox));
180         float8          infinity = get_float8_infinity();
181
182         rect_box->range_box_x.left.low = -infinity;
183         rect_box->range_box_x.left.high = infinity;
184
185         rect_box->range_box_x.right.low = -infinity;
186         rect_box->range_box_x.right.high = infinity;
187
188         rect_box->range_box_y.left.low = -infinity;
189         rect_box->range_box_y.left.high = infinity;
190
191         rect_box->range_box_y.right.low = -infinity;
192         rect_box->range_box_y.right.high = infinity;
193
194         return rect_box;
195 }
196
197 /*
198  * Calculate the next traversal value
199  *
200  * All centroids are bounded by RectBox, but SP-GiST only keeps
201  * boxes.  When we are traversing the tree, we must calculate RectBox,
202  * using centroid and quadrant.
203  */
204 static RectBox *
205 nextRectBox(RectBox *rect_box, RangeBox *centroid, uint8 quadrant)
206 {
207         RectBox    *next_rect_box = (RectBox *) palloc(sizeof(RectBox));
208
209         memcpy(next_rect_box, rect_box, sizeof(RectBox));
210
211         if (quadrant & 0x8)
212                 next_rect_box->range_box_x.left.low = centroid->left.low;
213         else
214                 next_rect_box->range_box_x.left.high = centroid->left.low;
215
216         if (quadrant & 0x4)
217                 next_rect_box->range_box_x.right.low = centroid->left.high;
218         else
219                 next_rect_box->range_box_x.right.high = centroid->left.high;
220
221         if (quadrant & 0x2)
222                 next_rect_box->range_box_y.left.low = centroid->right.low;
223         else
224                 next_rect_box->range_box_y.left.high = centroid->right.low;
225
226         if (quadrant & 0x1)
227                 next_rect_box->range_box_y.right.low = centroid->right.high;
228         else
229                 next_rect_box->range_box_y.right.high = centroid->right.high;
230
231         return next_rect_box;
232 }
233
234 /* Can any range from range_box overlap with this argument? */
235 static bool
236 overlap2D(RangeBox *range_box, Range *query)
237 {
238         return FPge(range_box->right.high, query->low) &&
239                 FPle(range_box->left.low, query->high);
240 }
241
242 /* Can any rectangle from rect_box overlap with this argument? */
243 static bool
244 overlap4D(RectBox *rect_box, RangeBox *query)
245 {
246         return overlap2D(&rect_box->range_box_x, &query->left) &&
247                 overlap2D(&rect_box->range_box_y, &query->right);
248 }
249
250 /* Can any range from range_box contain this argument? */
251 static bool
252 contain2D(RangeBox *range_box, Range *query)
253 {
254         return FPge(range_box->right.high, query->high) &&
255                 FPle(range_box->left.low, query->low);
256 }
257
258 /* Can any rectangle from rect_box contain this argument? */
259 static bool
260 contain4D(RectBox *rect_box, RangeBox *query)
261 {
262         return contain2D(&rect_box->range_box_x, &query->left) &&
263                 contain2D(&rect_box->range_box_y, &query->right);
264 }
265
266 /* Can any range from range_box be contained by this argument? */
267 static bool
268 contained2D(RangeBox *range_box, Range *query)
269 {
270         return FPle(range_box->left.low, query->high) &&
271                 FPge(range_box->left.high, query->low) &&
272                 FPle(range_box->right.low, query->high) &&
273                 FPge(range_box->right.high, query->low);
274 }
275
276 /* Can any rectangle from rect_box be contained by this argument? */
277 static bool
278 contained4D(RectBox *rect_box, RangeBox *query)
279 {
280         return contained2D(&rect_box->range_box_x, &query->left) &&
281                 contained2D(&rect_box->range_box_y, &query->right);
282 }
283
284 /* Can any range from range_box to be lower than this argument? */
285 static bool
286 lower2D(RangeBox *range_box, Range *query)
287 {
288         return FPlt(range_box->left.low, query->low) &&
289                 FPlt(range_box->right.low, query->low);
290 }
291
292 /* Can any range from range_box not extend to the right side of the query? */
293 static bool
294 overLower2D(RangeBox *range_box, Range *query)
295 {
296         return FPle(range_box->left.low, query->high) &&
297                 FPle(range_box->right.low, query->high);
298 }
299
300 /* Can any range from range_box to be higher than this argument? */
301 static bool
302 higher2D(RangeBox *range_box, Range *query)
303 {
304         return FPgt(range_box->left.high, query->high) &&
305                 FPgt(range_box->right.high, query->high);
306 }
307
308 /* Can any range from range_box not extend to the left side of the query? */
309 static bool
310 overHigher2D(RangeBox *range_box, Range *query)
311 {
312         return FPge(range_box->left.high, query->low) &&
313                 FPge(range_box->right.high, query->low);
314 }
315
316 /* Can any rectangle from rect_box be left of this argument? */
317 static bool
318 left4D(RectBox *rect_box, RangeBox *query)
319 {
320         return lower2D(&rect_box->range_box_x, &query->left);
321 }
322
323 /* Can any rectangle from rect_box does not extend the right of this argument? */
324 static bool
325 overLeft4D(RectBox *rect_box, RangeBox *query)
326 {
327         return overLower2D(&rect_box->range_box_x, &query->left);
328 }
329
330 /* Can any rectangle from rect_box be right of this argument? */
331 static bool
332 right4D(RectBox *rect_box, RangeBox *query)
333 {
334         return higher2D(&rect_box->range_box_x, &query->left);
335 }
336
337 /* Can any rectangle from rect_box does not extend the left of this argument? */
338 static bool
339 overRight4D(RectBox *rect_box, RangeBox *query)
340 {
341         return overHigher2D(&rect_box->range_box_x, &query->left);
342 }
343
344 /* Can any rectangle from rect_box be below of this argument? */
345 static bool
346 below4D(RectBox *rect_box, RangeBox *query)
347 {
348         return lower2D(&rect_box->range_box_y, &query->right);
349 }
350
351 /* Can any rectangle from rect_box does not extend above this argument? */
352 static bool
353 overBelow4D(RectBox *rect_box, RangeBox *query)
354 {
355         return overLower2D(&rect_box->range_box_y, &query->right);
356 }
357
358 /* Can any rectangle from rect_box be above of this argument? */
359 static bool
360 above4D(RectBox *rect_box, RangeBox *query)
361 {
362         return higher2D(&rect_box->range_box_y, &query->right);
363 }
364
365 /* Can any rectangle from rect_box does not extend below of this argument? */
366 static bool
367 overAbove4D(RectBox *rect_box, RangeBox *query)
368 {
369         return overHigher2D(&rect_box->range_box_y, &query->right);
370 }
371
372 /* Lower bound for the distance between point and rect_box */
373 static double
374 pointToRectBoxDistance(Point *point, RectBox *rect_box)
375 {
376         double          dx;
377         double          dy;
378
379         if (point->x < rect_box->range_box_x.left.low)
380                 dx = rect_box->range_box_x.left.low - point->x;
381         else if (point->x > rect_box->range_box_x.right.high)
382                 dx = point->x - rect_box->range_box_x.right.high;
383         else
384                 dx = 0;
385
386         if (point->y < rect_box->range_box_y.left.low)
387                 dy = rect_box->range_box_y.left.low - point->y;
388         else if (point->y > rect_box->range_box_y.right.high)
389                 dy = point->y - rect_box->range_box_y.right.high;
390         else
391                 dy = 0;
392
393         return HYPOT(dx, dy);
394 }
395
396
397 /*
398  * SP-GiST config function
399  */
400 Datum
401 spg_box_quad_config(PG_FUNCTION_ARGS)
402 {
403         spgConfigOut *cfg = (spgConfigOut *) PG_GETARG_POINTER(1);
404
405         cfg->prefixType = BOXOID;
406         cfg->labelType = VOIDOID;       /* We don't need node labels. */
407         cfg->canReturnData = true;
408         cfg->longValuesOK = false;
409
410         PG_RETURN_VOID();
411 }
412
413 /*
414  * SP-GiST choose function
415  */
416 Datum
417 spg_box_quad_choose(PG_FUNCTION_ARGS)
418 {
419         spgChooseIn *in = (spgChooseIn *) PG_GETARG_POINTER(0);
420         spgChooseOut *out = (spgChooseOut *) PG_GETARG_POINTER(1);
421         BOX                *centroid = DatumGetBoxP(in->prefixDatum),
422                            *box = DatumGetBoxP(in->leafDatum);
423
424         out->resultType = spgMatchNode;
425         out->result.matchNode.restDatum = BoxPGetDatum(box);
426
427         /* nodeN will be set by core, when allTheSame. */
428         if (!in->allTheSame)
429                 out->result.matchNode.nodeN = getQuadrant(centroid, box);
430
431         PG_RETURN_VOID();
432 }
433
434 /*
435  * SP-GiST pick-split function
436  *
437  * It splits a list of boxes into quadrants by choosing a central 4D
438  * point as the median of the coordinates of the boxes.
439  */
440 Datum
441 spg_box_quad_picksplit(PG_FUNCTION_ARGS)
442 {
443         spgPickSplitIn *in = (spgPickSplitIn *) PG_GETARG_POINTER(0);
444         spgPickSplitOut *out = (spgPickSplitOut *) PG_GETARG_POINTER(1);
445         BOX                *centroid;
446         int                     median,
447                                 i;
448         float8     *lowXs = palloc(sizeof(float8) * in->nTuples);
449         float8     *highXs = palloc(sizeof(float8) * in->nTuples);
450         float8     *lowYs = palloc(sizeof(float8) * in->nTuples);
451         float8     *highYs = palloc(sizeof(float8) * in->nTuples);
452
453         /* Calculate median of all 4D coordinates */
454         for (i = 0; i < in->nTuples; i++)
455         {
456                 BOX                *box = DatumGetBoxP(in->datums[i]);
457
458                 lowXs[i] = box->low.x;
459                 highXs[i] = box->high.x;
460                 lowYs[i] = box->low.y;
461                 highYs[i] = box->high.y;
462         }
463
464         qsort(lowXs, in->nTuples, sizeof(float8), compareDoubles);
465         qsort(highXs, in->nTuples, sizeof(float8), compareDoubles);
466         qsort(lowYs, in->nTuples, sizeof(float8), compareDoubles);
467         qsort(highYs, in->nTuples, sizeof(float8), compareDoubles);
468
469         median = in->nTuples / 2;
470
471         centroid = palloc(sizeof(BOX));
472
473         centroid->low.x = lowXs[median];
474         centroid->high.x = highXs[median];
475         centroid->low.y = lowYs[median];
476         centroid->high.y = highYs[median];
477
478         /* Fill the output */
479         out->hasPrefix = true;
480         out->prefixDatum = BoxPGetDatum(centroid);
481
482         out->nNodes = 16;
483         out->nodeLabels = NULL;         /* We don't need node labels. */
484
485         out->mapTuplesToNodes = palloc(sizeof(int) * in->nTuples);
486         out->leafTupleDatums = palloc(sizeof(Datum) * in->nTuples);
487
488         /*
489          * Assign ranges to corresponding nodes according to quadrants relative to
490          * the "centroid" range
491          */
492         for (i = 0; i < in->nTuples; i++)
493         {
494                 BOX                *box = DatumGetBoxP(in->datums[i]);
495                 uint8           quadrant = getQuadrant(centroid, box);
496
497                 out->leafTupleDatums[i] = BoxPGetDatum(box);
498                 out->mapTuplesToNodes[i] = quadrant;
499         }
500
501         PG_RETURN_VOID();
502 }
503
504 /*
505  * Check if result of consistent method based on bounding box is exact.
506  */
507 static bool
508 is_bounding_box_test_exact(StrategyNumber strategy)
509 {
510         switch (strategy)
511         {
512                 case RTLeftStrategyNumber:
513                 case RTOverLeftStrategyNumber:
514                 case RTOverRightStrategyNumber:
515                 case RTRightStrategyNumber:
516                 case RTOverBelowStrategyNumber:
517                 case RTBelowStrategyNumber:
518                 case RTAboveStrategyNumber:
519                 case RTOverAboveStrategyNumber:
520                         return true;
521
522                 default:
523                         return false;
524         }
525 }
526
527 /*
528  * Get bounding box for ScanKey.
529  */
530 static BOX *
531 spg_box_quad_get_scankey_bbox(ScanKey sk, bool *recheck)
532 {
533         switch (sk->sk_subtype)
534         {
535                 case BOXOID:
536                         return DatumGetBoxP(sk->sk_argument);
537
538                 case POLYGONOID:
539                         if (recheck && !is_bounding_box_test_exact(sk->sk_strategy))
540                                 *recheck = true;
541                         return &DatumGetPolygonP(sk->sk_argument)->boundbox;
542
543                 default:
544                         elog(ERROR, "unrecognized scankey subtype: %d", sk->sk_subtype);
545                         return NULL;
546         }
547 }
548
549 /*
550  * SP-GiST inner consistent function
551  */
552 Datum
553 spg_box_quad_inner_consistent(PG_FUNCTION_ARGS)
554 {
555         spgInnerConsistentIn *in = (spgInnerConsistentIn *) PG_GETARG_POINTER(0);
556         spgInnerConsistentOut *out = (spgInnerConsistentOut *) PG_GETARG_POINTER(1);
557         int                     i;
558         MemoryContext old_ctx;
559         RectBox    *rect_box;
560         uint8           quadrant;
561         RangeBox   *centroid,
562                           **queries;
563
564         /*
565          * We are saving the traversal value or initialize it an unbounded one, if
566          * we have just begun to walk the tree.
567          */
568         if (in->traversalValue)
569                 rect_box = in->traversalValue;
570         else
571                 rect_box = initRectBox();
572
573         if (in->allTheSame)
574         {
575                 /* Report that all nodes should be visited */
576                 out->nNodes = in->nNodes;
577                 out->nodeNumbers = (int *) palloc(sizeof(int) * in->nNodes);
578                 for (i = 0; i < in->nNodes; i++)
579                         out->nodeNumbers[i] = i;
580
581                 if (in->norderbys > 0 && in->nNodes > 0)
582                 {
583                         double     *distances = palloc(sizeof(double) * in->norderbys);
584                         int                     j;
585
586                         for (j = 0; j < in->norderbys; j++)
587                         {
588                                 Point      *pt = DatumGetPointP(in->orderbys[j].sk_argument);
589
590                                 distances[j] = pointToRectBoxDistance(pt, rect_box);
591                         }
592
593                         out->distances = (double **) palloc(sizeof(double *) * in->nNodes);
594                         out->distances[0] = distances;
595
596                         for (i = 1; i < in->nNodes; i++)
597                         {
598                                 out->distances[i] = palloc(sizeof(double) * in->norderbys);
599                                 memcpy(out->distances[i], distances,
600                                            sizeof(double) * in->norderbys);
601                         }
602                 }
603
604                 PG_RETURN_VOID();
605         }
606
607         /*
608          * We are casting the prefix and queries to RangeBoxes for ease of the
609          * following operations.
610          */
611         centroid = getRangeBox(DatumGetBoxP(in->prefixDatum));
612         queries = (RangeBox **) palloc(in->nkeys * sizeof(RangeBox *));
613         for (i = 0; i < in->nkeys; i++)
614         {
615                 BOX                *box = spg_box_quad_get_scankey_bbox(&in->scankeys[i], NULL);
616
617                 queries[i] = getRangeBox(box);
618         }
619
620         /* Allocate enough memory for nodes */
621         out->nNodes = 0;
622         out->nodeNumbers = (int *) palloc(sizeof(int) * in->nNodes);
623         out->traversalValues = (void **) palloc(sizeof(void *) * in->nNodes);
624         if (in->norderbys > 0)
625                 out->distances = (double **) palloc(sizeof(double *) * in->nNodes);
626
627         /*
628          * We switch memory context, because we want to allocate memory for new
629          * traversal values (next_rect_box) and pass these pieces of memory to
630          * further call of this function.
631          */
632         old_ctx = MemoryContextSwitchTo(in->traversalMemoryContext);
633
634         for (quadrant = 0; quadrant < in->nNodes; quadrant++)
635         {
636                 RectBox    *next_rect_box = nextRectBox(rect_box, centroid, quadrant);
637                 bool            flag = true;
638
639                 for (i = 0; i < in->nkeys; i++)
640                 {
641                         StrategyNumber strategy = in->scankeys[i].sk_strategy;
642
643                         switch (strategy)
644                         {
645                                 case RTOverlapStrategyNumber:
646                                         flag = overlap4D(next_rect_box, queries[i]);
647                                         break;
648
649                                 case RTContainsStrategyNumber:
650                                         flag = contain4D(next_rect_box, queries[i]);
651                                         break;
652
653                                 case RTSameStrategyNumber:
654                                 case RTContainedByStrategyNumber:
655                                         flag = contained4D(next_rect_box, queries[i]);
656                                         break;
657
658                                 case RTLeftStrategyNumber:
659                                         flag = left4D(next_rect_box, queries[i]);
660                                         break;
661
662                                 case RTOverLeftStrategyNumber:
663                                         flag = overLeft4D(next_rect_box, queries[i]);
664                                         break;
665
666                                 case RTRightStrategyNumber:
667                                         flag = right4D(next_rect_box, queries[i]);
668                                         break;
669
670                                 case RTOverRightStrategyNumber:
671                                         flag = overRight4D(next_rect_box, queries[i]);
672                                         break;
673
674                                 case RTAboveStrategyNumber:
675                                         flag = above4D(next_rect_box, queries[i]);
676                                         break;
677
678                                 case RTOverAboveStrategyNumber:
679                                         flag = overAbove4D(next_rect_box, queries[i]);
680                                         break;
681
682                                 case RTBelowStrategyNumber:
683                                         flag = below4D(next_rect_box, queries[i]);
684                                         break;
685
686                                 case RTOverBelowStrategyNumber:
687                                         flag = overBelow4D(next_rect_box, queries[i]);
688                                         break;
689
690                                 default:
691                                         elog(ERROR, "unrecognized strategy: %d", strategy);
692                         }
693
694                         /* If any check is failed, we have found our answer. */
695                         if (!flag)
696                                 break;
697                 }
698
699                 if (flag)
700                 {
701                         out->traversalValues[out->nNodes] = next_rect_box;
702                         out->nodeNumbers[out->nNodes] = quadrant;
703
704                         if (in->norderbys > 0)
705                         {
706                                 double     *distances = palloc(sizeof(double) * in->norderbys);
707                                 int                     j;
708
709                                 out->distances[out->nNodes] = distances;
710
711                                 for (j = 0; j < in->norderbys; j++)
712                                 {
713                                         Point      *pt = DatumGetPointP(in->orderbys[j].sk_argument);
714
715                                         distances[j] = pointToRectBoxDistance(pt, next_rect_box);
716                                 }
717                         }
718
719                         out->nNodes++;
720                 }
721                 else
722                 {
723                         /*
724                          * If this node is not selected, we don't need to keep the next
725                          * traversal value in the memory context.
726                          */
727                         pfree(next_rect_box);
728                 }
729         }
730
731         /* Switch back */
732         MemoryContextSwitchTo(old_ctx);
733
734         PG_RETURN_VOID();
735 }
736
737 /*
738  * SP-GiST inner consistent function
739  */
740 Datum
741 spg_box_quad_leaf_consistent(PG_FUNCTION_ARGS)
742 {
743         spgLeafConsistentIn *in = (spgLeafConsistentIn *) PG_GETARG_POINTER(0);
744         spgLeafConsistentOut *out = (spgLeafConsistentOut *) PG_GETARG_POINTER(1);
745         Datum           leaf = in->leafDatum;
746         bool            flag = true;
747         int                     i;
748
749         /* All tests are exact. */
750         out->recheck = false;
751
752         /* leafDatum is what it is... */
753         out->leafValue = in->leafDatum;
754
755         /* Perform the required comparison(s) */
756         for (i = 0; i < in->nkeys; i++)
757         {
758                 StrategyNumber strategy = in->scankeys[i].sk_strategy;
759                 BOX                *box = spg_box_quad_get_scankey_bbox(&in->scankeys[i],
760                                                                                                                 &out->recheck);
761                 Datum           query = BoxPGetDatum(box);
762
763                 switch (strategy)
764                 {
765                         case RTOverlapStrategyNumber:
766                                 flag = DatumGetBool(DirectFunctionCall2(box_overlap, leaf,
767                                                                                                                 query));
768                                 break;
769
770                         case RTContainsStrategyNumber:
771                                 flag = DatumGetBool(DirectFunctionCall2(box_contain, leaf,
772                                                                                                                 query));
773                                 break;
774
775                         case RTContainedByStrategyNumber:
776                                 flag = DatumGetBool(DirectFunctionCall2(box_contained, leaf,
777                                                                                                                 query));
778                                 break;
779
780                         case RTSameStrategyNumber:
781                                 flag = DatumGetBool(DirectFunctionCall2(box_same, leaf,
782                                                                                                                 query));
783                                 break;
784
785                         case RTLeftStrategyNumber:
786                                 flag = DatumGetBool(DirectFunctionCall2(box_left, leaf,
787                                                                                                                 query));
788                                 break;
789
790                         case RTOverLeftStrategyNumber:
791                                 flag = DatumGetBool(DirectFunctionCall2(box_overleft, leaf,
792                                                                                                                 query));
793                                 break;
794
795                         case RTRightStrategyNumber:
796                                 flag = DatumGetBool(DirectFunctionCall2(box_right, leaf,
797                                                                                                                 query));
798                                 break;
799
800                         case RTOverRightStrategyNumber:
801                                 flag = DatumGetBool(DirectFunctionCall2(box_overright, leaf,
802                                                                                                                 query));
803                                 break;
804
805                         case RTAboveStrategyNumber:
806                                 flag = DatumGetBool(DirectFunctionCall2(box_above, leaf,
807                                                                                                                 query));
808                                 break;
809
810                         case RTOverAboveStrategyNumber:
811                                 flag = DatumGetBool(DirectFunctionCall2(box_overabove, leaf,
812                                                                                                                 query));
813                                 break;
814
815                         case RTBelowStrategyNumber:
816                                 flag = DatumGetBool(DirectFunctionCall2(box_below, leaf,
817                                                                                                                 query));
818                                 break;
819
820                         case RTOverBelowStrategyNumber:
821                                 flag = DatumGetBool(DirectFunctionCall2(box_overbelow, leaf,
822                                                                                                                 query));
823                                 break;
824
825                         default:
826                                 elog(ERROR, "unrecognized strategy: %d", strategy);
827                 }
828
829                 /* If any check is failed, we have found our answer. */
830                 if (!flag)
831                         break;
832         }
833
834         if (flag && in->norderbys > 0)
835         {
836                 Oid                     distfnoid = in->orderbys[0].sk_func.fn_oid;
837
838                 out->distances = spg_key_orderbys_distances(leaf, false,
839                                                                                                         in->orderbys, in->norderbys);
840
841                 /* Recheck is necessary when computing distance to polygon */
842                 out->recheckDistances = distfnoid == F_DIST_POLYP;
843         }
844
845         PG_RETURN_BOOL(flag);
846 }
847
848
849 /*
850  * SP-GiST config function for 2-D types that are lossy represented by their
851  * bounding boxes
852  */
853 Datum
854 spg_bbox_quad_config(PG_FUNCTION_ARGS)
855 {
856         spgConfigOut *cfg = (spgConfigOut *) PG_GETARG_POINTER(1);
857
858         cfg->prefixType = BOXOID;       /* A type represented by its bounding box */
859         cfg->labelType = VOIDOID;       /* We don't need node labels. */
860         cfg->leafType = BOXOID;
861         cfg->canReturnData = false;
862         cfg->longValuesOK = false;
863
864         PG_RETURN_VOID();
865 }
866
867 /*
868  * SP-GiST compress function for polygons
869  */
870 Datum
871 spg_poly_quad_compress(PG_FUNCTION_ARGS)
872 {
873         POLYGON    *polygon = PG_GETARG_POLYGON_P(0);
874         BOX                *box;
875
876         box = (BOX *) palloc(sizeof(BOX));
877         *box = polygon->boundbox;
878
879         PG_RETURN_BOX_P(box);
880 }