1 /*-------------------------------------------------------------------------
2  *
3  * gistproc.c
4  *	  Support procedures for GiSTs over 2-D objects (boxes, polygons, circles,
5  *	  points).
6  *
7  * This gives R-tree behavior, with Guttman's poly-time split algorithm.
8  *
9  *
10  * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group
11  * Portions Copyright (c) 1994, Regents of the University of California
12  *
13  * IDENTIFICATION
14  *	src/backend/access/gist/gistproc.c
15  *
16  *-------------------------------------------------------------------------
17  */
18 #include "postgres.h"
19 
20 #include <float.h>
21 #include <math.h>
22 
23 #include "access/gist.h"
24 #include "access/stratnum.h"
25 #include "utils/builtins.h"
26 #include "utils/geo_decls.h"
27 
28 
29 static bool gist_box_leaf_consistent(BOX *key, BOX *query,
30 						 StrategyNumber strategy);
31 static bool rtree_internal_consistent(BOX *key, BOX *query,
32 						  StrategyNumber strategy);
33 
34 /* Minimum accepted ratio of split */
35 #define LIMIT_RATIO 0.3
36 
37 /* Convenience macros for NaN-aware comparisons */
38 #define FLOAT8_EQ(a,b)	(float8_cmp_internal(a, b) == 0)
39 #define FLOAT8_LT(a,b)	(float8_cmp_internal(a, b) < 0)
40 #define FLOAT8_LE(a,b)	(float8_cmp_internal(a, b) <= 0)
41 #define FLOAT8_GT(a,b)	(float8_cmp_internal(a, b) > 0)
42 #define FLOAT8_GE(a,b)	(float8_cmp_internal(a, b) >= 0)
43 #define FLOAT8_MAX(a,b)  (FLOAT8_GT(a, b) ? (a) : (b))
44 #define FLOAT8_MIN(a,b)  (FLOAT8_LT(a, b) ? (a) : (b))
45 
46 
47 /**************************************************
48  * Box ops
49  **************************************************/
50 
51 /*
52  * Calculates union of two boxes, a and b. The result is stored in *n.
53  */
54 static void
rt_box_union(BOX * n,const BOX * a,const BOX * b)55 rt_box_union(BOX *n, const BOX *a, const BOX *b)
56 {
57 	n->high.x = FLOAT8_MAX(a->high.x, b->high.x);
58 	n->high.y = FLOAT8_MAX(a->high.y, b->high.y);
59 	n->low.x = FLOAT8_MIN(a->low.x, b->low.x);
60 	n->low.y = FLOAT8_MIN(a->low.y, b->low.y);
61 }
62 
63 /*
64  * Size of a BOX for penalty-calculation purposes.
65  * The result can be +Infinity, but not NaN.
66  */
67 static double
size_box(const BOX * box)68 size_box(const BOX *box)
69 {
70 	/*
71 	 * Check for zero-width cases.  Note that we define the size of a zero-
72 	 * by-infinity box as zero.  It's important to special-case this somehow,
73 	 * as naively multiplying infinity by zero will produce NaN.
74 	 *
75 	 * The less-than cases should not happen, but if they do, say "zero".
76 	 */
77 	if (FLOAT8_LE(box->high.x, box->low.x) ||
78 		FLOAT8_LE(box->high.y, box->low.y))
79 		return 0.0;
80 
81 	/*
82 	 * We treat NaN as larger than +Infinity, so any distance involving a NaN
83 	 * and a non-NaN is infinite.  Note the previous check eliminated the
84 	 * possibility that the low fields are NaNs.
85 	 */
86 	if (isnan(box->high.x) || isnan(box->high.y))
87 		return get_float8_infinity();
88 	return (box->high.x - box->low.x) * (box->high.y - box->low.y);
89 }
90 
91 /*
92  * Return amount by which the union of the two boxes is larger than
93  * the original BOX's area.  The result can be +Infinity, but not NaN.
94  */
95 static double
box_penalty(const BOX * original,const BOX * new)96 box_penalty(const BOX *original, const BOX *new)
97 {
98 	BOX			unionbox;
99 
100 	rt_box_union(&unionbox, original, new);
101 	return size_box(&unionbox) - size_box(original);
102 }
103 
104 /*
105  * The GiST Consistent method for boxes
106  *
107  * Should return false if for all data items x below entry,
108  * the predicate x op query must be false, where op is the oper
109  * corresponding to strategy in the pg_amop table.
110  */
111 Datum
gist_box_consistent(PG_FUNCTION_ARGS)112 gist_box_consistent(PG_FUNCTION_ARGS)
113 {
114 	GISTENTRY  *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
115 	BOX		   *query = PG_GETARG_BOX_P(1);
116 	StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2);
117 
118 	/* Oid		subtype = PG_GETARG_OID(3); */
119 	bool	   *recheck = (bool *) PG_GETARG_POINTER(4);
120 
121 	/* All cases served by this function are exact */
122 	*recheck = false;
123 
124 	if (DatumGetBoxP(entry->key) == NULL || query == NULL)
125 		PG_RETURN_BOOL(false);
126 
127 	/*
128 	 * if entry is not leaf, use rtree_internal_consistent, else use
129 	 * gist_box_leaf_consistent
130 	 */
131 	if (GIST_LEAF(entry))
132 		PG_RETURN_BOOL(gist_box_leaf_consistent(DatumGetBoxP(entry->key),
133 												query,
134 												strategy));
135 	else
136 		PG_RETURN_BOOL(rtree_internal_consistent(DatumGetBoxP(entry->key),
137 												 query,
138 												 strategy));
139 }
140 
141 /*
142  * Increase BOX b to include addon.
143  */
144 static void
adjustBox(BOX * b,const BOX * addon)145 adjustBox(BOX *b, const BOX *addon)
146 {
147 	if (FLOAT8_LT(b->high.x, addon->high.x))
148 		b->high.x = addon->high.x;
149 	if (FLOAT8_GT(b->low.x, addon->low.x))
150 		b->low.x = addon->low.x;
151 	if (FLOAT8_LT(b->high.y, addon->high.y))
152 		b->high.y = addon->high.y;
153 	if (FLOAT8_GT(b->low.y, addon->low.y))
154 		b->low.y = addon->low.y;
155 }
156 
157 /*
158  * The GiST Union method for boxes
159  *
160  * returns the minimal bounding box that encloses all the entries in entryvec
161  */
162 Datum
gist_box_union(PG_FUNCTION_ARGS)163 gist_box_union(PG_FUNCTION_ARGS)
164 {
165 	GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
166 	int		   *sizep = (int *) PG_GETARG_POINTER(1);
167 	int			numranges,
168 				i;
169 	BOX		   *cur,
170 			   *pageunion;
171 
172 	numranges = entryvec->n;
173 	pageunion = (BOX *) palloc(sizeof(BOX));
174 	cur = DatumGetBoxP(entryvec->vector[0].key);
175 	memcpy((void *) pageunion, (void *) cur, sizeof(BOX));
176 
177 	for (i = 1; i < numranges; i++)
178 	{
179 		cur = DatumGetBoxP(entryvec->vector[i].key);
180 		adjustBox(pageunion, cur);
181 	}
182 	*sizep = sizeof(BOX);
183 
184 	PG_RETURN_POINTER(pageunion);
185 }
186 
187 /*
188  * We store boxes as boxes in GiST indexes, so we do not need
189  * compress, decompress, or fetch functions.
190  */
191 
192 /*
193  * The GiST Penalty method for boxes (also used for points)
194  *
195  * As in the R-tree paper, we use change in area as our penalty metric
196  */
197 Datum
gist_box_penalty(PG_FUNCTION_ARGS)198 gist_box_penalty(PG_FUNCTION_ARGS)
199 {
200 	GISTENTRY  *origentry = (GISTENTRY *) PG_GETARG_POINTER(0);
201 	GISTENTRY  *newentry = (GISTENTRY *) PG_GETARG_POINTER(1);
202 	float	   *result = (float *) PG_GETARG_POINTER(2);
203 	BOX		   *origbox = DatumGetBoxP(origentry->key);
204 	BOX		   *newbox = DatumGetBoxP(newentry->key);
205 
206 	*result = (float) box_penalty(origbox, newbox);
207 	PG_RETURN_POINTER(result);
208 }
209 
210 /*
211  * Trivial split: half of entries will be placed on one page
212  * and another half - to another
213  */
214 static void
fallbackSplit(GistEntryVector * entryvec,GIST_SPLITVEC * v)215 fallbackSplit(GistEntryVector *entryvec, GIST_SPLITVEC *v)
216 {
217 	OffsetNumber i,
218 				maxoff;
219 	BOX		   *unionL = NULL,
220 			   *unionR = NULL;
221 	int			nbytes;
222 
223 	maxoff = entryvec->n - 1;
224 
225 	nbytes = (maxoff + 2) * sizeof(OffsetNumber);
226 	v->spl_left = (OffsetNumber *) palloc(nbytes);
227 	v->spl_right = (OffsetNumber *) palloc(nbytes);
228 	v->spl_nleft = v->spl_nright = 0;
229 
230 	for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
231 	{
232 		BOX		   *cur = DatumGetBoxP(entryvec->vector[i].key);
233 
234 		if (i <= (maxoff - FirstOffsetNumber + 1) / 2)
235 		{
236 			v->spl_left[v->spl_nleft] = i;
237 			if (unionL == NULL)
238 			{
239 				unionL = (BOX *) palloc(sizeof(BOX));
240 				*unionL = *cur;
241 			}
242 			else
243 				adjustBox(unionL, cur);
244 
245 			v->spl_nleft++;
246 		}
247 		else
248 		{
249 			v->spl_right[v->spl_nright] = i;
250 			if (unionR == NULL)
251 			{
252 				unionR = (BOX *) palloc(sizeof(BOX));
253 				*unionR = *cur;
254 			}
255 			else
256 				adjustBox(unionR, cur);
257 
258 			v->spl_nright++;
259 		}
260 	}
261 
262 	v->spl_ldatum = BoxPGetDatum(unionL);
263 	v->spl_rdatum = BoxPGetDatum(unionR);
264 }
265 
266 /*
267  * Represents information about an entry that can be placed to either group
268  * without affecting overlap over selected axis ("common entry").
269  */
270 typedef struct
271 {
272 	/* Index of entry in the initial array */
273 	int			index;
274 	/* Delta between penalties of entry insertion into different groups */
275 	double		delta;
276 } CommonEntry;
277 
278 /*
279  * Context for g_box_consider_split. Contains information about currently
280  * selected split and some general information.
281  */
282 typedef struct
283 {
284 	int			entriesCount;	/* total number of entries being split */
285 	BOX			boundingBox;	/* minimum bounding box across all entries */
286 
287 	/* Information about currently selected split follows */
288 
289 	bool		first;			/* true if no split was selected yet */
290 
291 	double		leftUpper;		/* upper bound of left interval */
292 	double		rightLower;		/* lower bound of right interval */
293 
294 	float4		ratio;
295 	float4		overlap;
296 	int			dim;			/* axis of this split */
297 	double		range;			/* width of general MBR projection to the
298 								 * selected axis */
299 } ConsiderSplitContext;
300 
301 /*
302  * Interval represents projection of box to axis.
303  */
304 typedef struct
305 {
306 	double		lower,
307 				upper;
308 } SplitInterval;
309 
310 /*
311  * Interval comparison function by lower bound of the interval;
312  */
313 static int
interval_cmp_lower(const void * i1,const void * i2)314 interval_cmp_lower(const void *i1, const void *i2)
315 {
316 	double		lower1 = ((const SplitInterval *) i1)->lower,
317 				lower2 = ((const SplitInterval *) i2)->lower;
318 
319 	return float8_cmp_internal(lower1, lower2);
320 }
321 
322 /*
323  * Interval comparison function by upper bound of the interval;
324  */
325 static int
interval_cmp_upper(const void * i1,const void * i2)326 interval_cmp_upper(const void *i1, const void *i2)
327 {
328 	double		upper1 = ((const SplitInterval *) i1)->upper,
329 				upper2 = ((const SplitInterval *) i2)->upper;
330 
331 	return float8_cmp_internal(upper1, upper2);
332 }
333 
334 /*
335  * Replace negative (or NaN) value with zero.
336  */
337 static inline float
non_negative(float val)338 non_negative(float val)
339 {
340 	if (val >= 0.0f)
341 		return val;
342 	else
343 		return 0.0f;
344 }
345 
346 /*
347  * Consider replacement of currently selected split with the better one.
348  */
349 static inline void
g_box_consider_split(ConsiderSplitContext * context,int dimNum,double rightLower,int minLeftCount,double leftUpper,int maxLeftCount)350 g_box_consider_split(ConsiderSplitContext *context, int dimNum,
351 					 double rightLower, int minLeftCount,
352 					 double leftUpper, int maxLeftCount)
353 {
354 	int			leftCount,
355 				rightCount;
356 	float4		ratio,
357 				overlap;
358 	double		range;
359 
360 	/*
361 	 * Calculate entries distribution ratio assuming most uniform distribution
362 	 * of common entries.
363 	 */
364 	if (minLeftCount >= (context->entriesCount + 1) / 2)
365 	{
366 		leftCount = minLeftCount;
367 	}
368 	else
369 	{
370 		if (maxLeftCount <= context->entriesCount / 2)
371 			leftCount = maxLeftCount;
372 		else
373 			leftCount = context->entriesCount / 2;
374 	}
375 	rightCount = context->entriesCount - leftCount;
376 
377 	/*
378 	 * Ratio of split - quotient between size of lesser group and total
379 	 * entries count.
380 	 */
381 	ratio = ((float4) Min(leftCount, rightCount)) /
382 		((float4) context->entriesCount);
383 
384 	if (ratio > LIMIT_RATIO)
385 	{
386 		bool		selectthis = false;
387 
388 		/*
389 		 * The ratio is acceptable, so compare current split with previously
390 		 * selected one. Between splits of one dimension we search for minimal
391 		 * overlap (allowing negative values) and minimal ration (between same
392 		 * overlaps. We switch dimension if find less overlap (non-negative)
393 		 * or less range with same overlap.
394 		 */
395 		if (dimNum == 0)
396 			range = context->boundingBox.high.x - context->boundingBox.low.x;
397 		else
398 			range = context->boundingBox.high.y - context->boundingBox.low.y;
399 
400 		overlap = (leftUpper - rightLower) / range;
401 
402 		/* If there is no previous selection, select this */
403 		if (context->first)
404 			selectthis = true;
405 		else if (context->dim == dimNum)
406 		{
407 			/*
408 			 * Within the same dimension, choose the new split if it has a
409 			 * smaller overlap, or same overlap but better ratio.
410 			 */
411 			if (overlap < context->overlap ||
412 				(overlap == context->overlap && ratio > context->ratio))
413 				selectthis = true;
414 		}
415 		else
416 		{
417 			/*
418 			 * Across dimensions, choose the new split if it has a smaller
419 			 * *non-negative* overlap, or same *non-negative* overlap but
420 			 * bigger range. This condition differs from the one described in
421 			 * the article. On the datasets where leaf MBRs don't overlap
422 			 * themselves, non-overlapping splits (i.e. splits which have zero
423 			 * *non-negative* overlap) are frequently possible. In this case
424 			 * splits tends to be along one dimension, because most distant
425 			 * non-overlapping splits (i.e. having lowest negative overlap)
426 			 * appears to be in the same dimension as in the previous split.
427 			 * Therefore MBRs appear to be very prolonged along another
428 			 * dimension, which leads to bad search performance. Using range
429 			 * as the second split criteria makes MBRs more quadratic. Using
430 			 * *non-negative* overlap instead of overlap as the first split
431 			 * criteria gives to range criteria a chance to matter, because
432 			 * non-overlapping splits are equivalent in this criteria.
433 			 */
434 			if (non_negative(overlap) < non_negative(context->overlap) ||
435 				(range > context->range &&
436 				 non_negative(overlap) <= non_negative(context->overlap)))
437 				selectthis = true;
438 		}
439 
440 		if (selectthis)
441 		{
442 			/* save information about selected split */
443 			context->first = false;
444 			context->ratio = ratio;
445 			context->range = range;
446 			context->overlap = overlap;
447 			context->rightLower = rightLower;
448 			context->leftUpper = leftUpper;
449 			context->dim = dimNum;
450 		}
451 	}
452 }
453 
454 /*
455  * Compare common entries by their deltas.
456  * (We assume the deltas can't be NaN.)
457  */
458 static int
common_entry_cmp(const void * i1,const void * i2)459 common_entry_cmp(const void *i1, const void *i2)
460 {
461 	double		delta1 = ((const CommonEntry *) i1)->delta,
462 				delta2 = ((const CommonEntry *) i2)->delta;
463 
464 	if (delta1 < delta2)
465 		return -1;
466 	else if (delta1 > delta2)
467 		return 1;
468 	else
469 		return 0;
470 }
471 
472 /*
473  * --------------------------------------------------------------------------
474  * Double sorting split algorithm. This is used for both boxes and points.
475  *
476  * The algorithm finds split of boxes by considering splits along each axis.
477  * Each entry is first projected as an interval on the X-axis, and different
478  * ways to split the intervals into two groups are considered, trying to
479  * minimize the overlap of the groups. Then the same is repeated for the
480  * Y-axis, and the overall best split is chosen. The quality of a split is
481  * determined by overlap along that axis and some other criteria (see
482  * g_box_consider_split).
483  *
484  * After that, all the entries are divided into three groups:
485  *
486  * 1) Entries which should be placed to the left group
487  * 2) Entries which should be placed to the right group
488  * 3) "Common entries" which can be placed to any of groups without affecting
489  *	  of overlap along selected axis.
490  *
491  * The common entries are distributed by minimizing penalty.
492  *
493  * For details see:
494  * "A new double sorting-based node splitting algorithm for R-tree", A. Korotkov
495  * http://syrcose.ispras.ru/2011/files/SYRCoSE2011_Proceedings.pdf#page=36
496  * --------------------------------------------------------------------------
497  */
498 Datum
gist_box_picksplit(PG_FUNCTION_ARGS)499 gist_box_picksplit(PG_FUNCTION_ARGS)
500 {
501 	GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
502 	GIST_SPLITVEC *v = (GIST_SPLITVEC *) PG_GETARG_POINTER(1);
503 	OffsetNumber i,
504 				maxoff;
505 	ConsiderSplitContext context;
506 	BOX		   *box,
507 			   *leftBox,
508 			   *rightBox;
509 	int			dim,
510 				commonEntriesCount;
511 	SplitInterval *intervalsLower,
512 			   *intervalsUpper;
513 	CommonEntry *commonEntries;
514 	int			nentries;
515 
516 	memset(&context, 0, sizeof(ConsiderSplitContext));
517 
518 	maxoff = entryvec->n - 1;
519 	nentries = context.entriesCount = maxoff - FirstOffsetNumber + 1;
520 
521 	/* Allocate arrays for intervals along axes */
522 	intervalsLower = (SplitInterval *) palloc(nentries * sizeof(SplitInterval));
523 	intervalsUpper = (SplitInterval *) palloc(nentries * sizeof(SplitInterval));
524 
525 	/*
526 	 * Calculate the overall minimum bounding box over all the entries.
527 	 */
528 	for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
529 	{
530 		box = DatumGetBoxP(entryvec->vector[i].key);
531 		if (i == FirstOffsetNumber)
532 			context.boundingBox = *box;
533 		else
534 			adjustBox(&context.boundingBox, box);
535 	}
536 
537 	/*
538 	 * Iterate over axes for optimal split searching.
539 	 */
540 	context.first = true;		/* nothing selected yet */
541 	for (dim = 0; dim < 2; dim++)
542 	{
543 		double		leftUpper,
544 					rightLower;
545 		int			i1,
546 					i2;
547 
548 		/* Project each entry as an interval on the selected axis. */
549 		for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
550 		{
551 			box = DatumGetBoxP(entryvec->vector[i].key);
552 			if (dim == 0)
553 			{
554 				intervalsLower[i - FirstOffsetNumber].lower = box->low.x;
555 				intervalsLower[i - FirstOffsetNumber].upper = box->high.x;
556 			}
557 			else
558 			{
559 				intervalsLower[i - FirstOffsetNumber].lower = box->low.y;
560 				intervalsLower[i - FirstOffsetNumber].upper = box->high.y;
561 			}
562 		}
563 
564 		/*
565 		 * Make two arrays of intervals: one sorted by lower bound and another
566 		 * sorted by upper bound.
567 		 */
568 		memcpy(intervalsUpper, intervalsLower,
569 			   sizeof(SplitInterval) * nentries);
570 		qsort(intervalsLower, nentries, sizeof(SplitInterval),
571 			  interval_cmp_lower);
572 		qsort(intervalsUpper, nentries, sizeof(SplitInterval),
573 			  interval_cmp_upper);
574 
575 		/*----
576 		 * The goal is to form a left and right interval, so that every entry
577 		 * interval is contained by either left or right interval (or both).
578 		 *
579 		 * For example, with the intervals (0,1), (1,3), (2,3), (2,4):
580 		 *
581 		 * 0 1 2 3 4
582 		 * +-+
583 		 *	 +---+
584 		 *	   +-+
585 		 *	   +---+
586 		 *
587 		 * The left and right intervals are of the form (0,a) and (b,4).
588 		 * We first consider splits where b is the lower bound of an entry.
589 		 * We iterate through all entries, and for each b, calculate the
590 		 * smallest possible a. Then we consider splits where a is the
591 		 * upper bound of an entry, and for each a, calculate the greatest
592 		 * possible b.
593 		 *
594 		 * In the above example, the first loop would consider splits:
595 		 * b=0: (0,1)-(0,4)
596 		 * b=1: (0,1)-(1,4)
597 		 * b=2: (0,3)-(2,4)
598 		 *
599 		 * And the second loop:
600 		 * a=1: (0,1)-(1,4)
601 		 * a=3: (0,3)-(2,4)
602 		 * a=4: (0,4)-(2,4)
603 		 */
604 
605 		/*
606 		 * Iterate over lower bound of right group, finding smallest possible
607 		 * upper bound of left group.
608 		 */
609 		i1 = 0;
610 		i2 = 0;
611 		rightLower = intervalsLower[i1].lower;
612 		leftUpper = intervalsUpper[i2].lower;
613 		while (true)
614 		{
615 			/*
616 			 * Find next lower bound of right group.
617 			 */
618 			while (i1 < nentries &&
619 				   FLOAT8_EQ(rightLower, intervalsLower[i1].lower))
620 			{
621 				if (FLOAT8_LT(leftUpper, intervalsLower[i1].upper))
622 					leftUpper = intervalsLower[i1].upper;
623 				i1++;
624 			}
625 			if (i1 >= nentries)
626 				break;
627 			rightLower = intervalsLower[i1].lower;
628 
629 			/*
630 			 * Find count of intervals which anyway should be placed to the
631 			 * left group.
632 			 */
633 			while (i2 < nentries &&
634 				   FLOAT8_LE(intervalsUpper[i2].upper, leftUpper))
635 				i2++;
636 
637 			/*
638 			 * Consider found split.
639 			 */
640 			g_box_consider_split(&context, dim, rightLower, i1, leftUpper, i2);
641 		}
642 
643 		/*
644 		 * Iterate over upper bound of left group finding greatest possible
645 		 * lower bound of right group.
646 		 */
647 		i1 = nentries - 1;
648 		i2 = nentries - 1;
649 		rightLower = intervalsLower[i1].upper;
650 		leftUpper = intervalsUpper[i2].upper;
651 		while (true)
652 		{
653 			/*
654 			 * Find next upper bound of left group.
655 			 */
656 			while (i2 >= 0 && FLOAT8_EQ(leftUpper, intervalsUpper[i2].upper))
657 			{
658 				if (FLOAT8_GT(rightLower, intervalsUpper[i2].lower))
659 					rightLower = intervalsUpper[i2].lower;
660 				i2--;
661 			}
662 			if (i2 < 0)
663 				break;
664 			leftUpper = intervalsUpper[i2].upper;
665 
666 			/*
667 			 * Find count of intervals which anyway should be placed to the
668 			 * right group.
669 			 */
670 			while (i1 >= 0 && FLOAT8_GE(intervalsLower[i1].lower, rightLower))
671 				i1--;
672 
673 			/*
674 			 * Consider found split.
675 			 */
676 			g_box_consider_split(&context, dim,
677 								 rightLower, i1 + 1, leftUpper, i2 + 1);
678 		}
679 	}
680 
681 	/*
682 	 * If we failed to find any acceptable splits, use trivial split.
683 	 */
684 	if (context.first)
685 	{
686 		fallbackSplit(entryvec, v);
687 		PG_RETURN_POINTER(v);
688 	}
689 
690 	/*
691 	 * Ok, we have now selected the split across one axis.
692 	 *
693 	 * While considering the splits, we already determined that there will be
694 	 * enough entries in both groups to reach the desired ratio, but we did
695 	 * not memorize which entries go to which group. So determine that now.
696 	 */
697 
698 	/* Allocate vectors for results */
699 	v->spl_left = (OffsetNumber *) palloc(nentries * sizeof(OffsetNumber));
700 	v->spl_right = (OffsetNumber *) palloc(nentries * sizeof(OffsetNumber));
701 	v->spl_nleft = 0;
702 	v->spl_nright = 0;
703 
704 	/* Allocate bounding boxes of left and right groups */
705 	leftBox = palloc0(sizeof(BOX));
706 	rightBox = palloc0(sizeof(BOX));
707 
708 	/*
709 	 * Allocate an array for "common entries" - entries which can be placed to
710 	 * either group without affecting overlap along selected axis.
711 	 */
712 	commonEntriesCount = 0;
713 	commonEntries = (CommonEntry *) palloc(nentries * sizeof(CommonEntry));
714 
715 	/* Helper macros to place an entry in the left or right group */
716 #define PLACE_LEFT(box, off)					\
717 	do {										\
718 		if (v->spl_nleft > 0)					\
719 			adjustBox(leftBox, box);			\
720 		else									\
721 			*leftBox = *(box);					\
722 		v->spl_left[v->spl_nleft++] = off;		\
723 	} while(0)
724 
725 #define PLACE_RIGHT(box, off)					\
726 	do {										\
727 		if (v->spl_nright > 0)					\
728 			adjustBox(rightBox, box);			\
729 		else									\
730 			*rightBox = *(box);					\
731 		v->spl_right[v->spl_nright++] = off;	\
732 	} while(0)
733 
734 	/*
735 	 * Distribute entries which can be distributed unambiguously, and collect
736 	 * common entries.
737 	 */
738 	for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
739 	{
740 		double		lower,
741 					upper;
742 
743 		/*
744 		 * Get upper and lower bounds along selected axis.
745 		 */
746 		box = DatumGetBoxP(entryvec->vector[i].key);
747 		if (context.dim == 0)
748 		{
749 			lower = box->low.x;
750 			upper = box->high.x;
751 		}
752 		else
753 		{
754 			lower = box->low.y;
755 			upper = box->high.y;
756 		}
757 
758 		if (FLOAT8_LE(upper, context.leftUpper))
759 		{
760 			/* Fits to the left group */
761 			if (FLOAT8_GE(lower, context.rightLower))
762 			{
763 				/* Fits also to the right group, so "common entry" */
764 				commonEntries[commonEntriesCount++].index = i;
765 			}
766 			else
767 			{
768 				/* Doesn't fit to the right group, so join to the left group */
769 				PLACE_LEFT(box, i);
770 			}
771 		}
772 		else
773 		{
774 			/*
775 			 * Each entry should fit on either left or right group. Since this
776 			 * entry didn't fit on the left group, it better fit in the right
777 			 * group.
778 			 */
779 			Assert(FLOAT8_GE(lower, context.rightLower));
780 
781 			/* Doesn't fit to the left group, so join to the right group */
782 			PLACE_RIGHT(box, i);
783 		}
784 	}
785 
786 	/*
787 	 * Distribute "common entries", if any.
788 	 */
789 	if (commonEntriesCount > 0)
790 	{
791 		/*
792 		 * Calculate minimum number of entries that must be placed in both
793 		 * groups, to reach LIMIT_RATIO.
794 		 */
795 		int			m = ceil(LIMIT_RATIO * (double) nentries);
796 
797 		/*
798 		 * Calculate delta between penalties of join "common entries" to
799 		 * different groups.
800 		 */
801 		for (i = 0; i < commonEntriesCount; i++)
802 		{
803 			box = DatumGetBoxP(entryvec->vector[commonEntries[i].index].key);
804 			commonEntries[i].delta = Abs(box_penalty(leftBox, box) -
805 										 box_penalty(rightBox, box));
806 		}
807 
808 		/*
809 		 * Sort "common entries" by calculated deltas in order to distribute
810 		 * the most ambiguous entries first.
811 		 */
812 		qsort(commonEntries, commonEntriesCount, sizeof(CommonEntry), common_entry_cmp);
813 
814 		/*
815 		 * Distribute "common entries" between groups.
816 		 */
817 		for (i = 0; i < commonEntriesCount; i++)
818 		{
819 			box = DatumGetBoxP(entryvec->vector[commonEntries[i].index].key);
820 
821 			/*
822 			 * Check if we have to place this entry in either group to achieve
823 			 * LIMIT_RATIO.
824 			 */
825 			if (v->spl_nleft + (commonEntriesCount - i) <= m)
826 				PLACE_LEFT(box, commonEntries[i].index);
827 			else if (v->spl_nright + (commonEntriesCount - i) <= m)
828 				PLACE_RIGHT(box, commonEntries[i].index);
829 			else
830 			{
831 				/* Otherwise select the group by minimal penalty */
832 				if (box_penalty(leftBox, box) < box_penalty(rightBox, box))
833 					PLACE_LEFT(box, commonEntries[i].index);
834 				else
835 					PLACE_RIGHT(box, commonEntries[i].index);
836 			}
837 		}
838 	}
839 
840 	v->spl_ldatum = PointerGetDatum(leftBox);
841 	v->spl_rdatum = PointerGetDatum(rightBox);
842 	PG_RETURN_POINTER(v);
843 }
844 
845 /*
846  * Equality method
847  *
848  * This is used for boxes, points, circles, and polygons, all of which store
849  * boxes as GiST index entries.
850  *
851  * Returns true only when boxes are exactly the same.  We can't use fuzzy
852  * comparisons here without breaking index consistency; therefore, this isn't
853  * equivalent to box_same().
854  */
855 Datum
gist_box_same(PG_FUNCTION_ARGS)856 gist_box_same(PG_FUNCTION_ARGS)
857 {
858 	BOX		   *b1 = PG_GETARG_BOX_P(0);
859 	BOX		   *b2 = PG_GETARG_BOX_P(1);
860 	bool	   *result = (bool *) PG_GETARG_POINTER(2);
861 
862 	if (b1 && b2)
863 		*result = (FLOAT8_EQ(b1->low.x, b2->low.x) &&
864 				   FLOAT8_EQ(b1->low.y, b2->low.y) &&
865 				   FLOAT8_EQ(b1->high.x, b2->high.x) &&
866 				   FLOAT8_EQ(b1->high.y, b2->high.y));
867 	else
868 		*result = (b1 == NULL && b2 == NULL);
869 	PG_RETURN_POINTER(result);
870 }
871 
872 /*
873  * Leaf-level consistency for boxes: just apply the query operator
874  */
875 static bool
gist_box_leaf_consistent(BOX * key,BOX * query,StrategyNumber strategy)876 gist_box_leaf_consistent(BOX *key, BOX *query, StrategyNumber strategy)
877 {
878 	bool		retval;
879 
880 	switch (strategy)
881 	{
882 		case RTLeftStrategyNumber:
883 			retval = DatumGetBool(DirectFunctionCall2(box_left,
884 													  PointerGetDatum(key),
885 													  PointerGetDatum(query)));
886 			break;
887 		case RTOverLeftStrategyNumber:
888 			retval = DatumGetBool(DirectFunctionCall2(box_overleft,
889 													  PointerGetDatum(key),
890 													  PointerGetDatum(query)));
891 			break;
892 		case RTOverlapStrategyNumber:
893 			retval = DatumGetBool(DirectFunctionCall2(box_overlap,
894 													  PointerGetDatum(key),
895 													  PointerGetDatum(query)));
896 			break;
897 		case RTOverRightStrategyNumber:
898 			retval = DatumGetBool(DirectFunctionCall2(box_overright,
899 													  PointerGetDatum(key),
900 													  PointerGetDatum(query)));
901 			break;
902 		case RTRightStrategyNumber:
903 			retval = DatumGetBool(DirectFunctionCall2(box_right,
904 													  PointerGetDatum(key),
905 													  PointerGetDatum(query)));
906 			break;
907 		case RTSameStrategyNumber:
908 			retval = DatumGetBool(DirectFunctionCall2(box_same,
909 													  PointerGetDatum(key),
910 													  PointerGetDatum(query)));
911 			break;
912 		case RTContainsStrategyNumber:
913 		case RTOldContainsStrategyNumber:
914 			retval = DatumGetBool(DirectFunctionCall2(box_contain,
915 													  PointerGetDatum(key),
916 													  PointerGetDatum(query)));
917 			break;
918 		case RTContainedByStrategyNumber:
919 		case RTOldContainedByStrategyNumber:
920 			retval = DatumGetBool(DirectFunctionCall2(box_contained,
921 													  PointerGetDatum(key),
922 													  PointerGetDatum(query)));
923 			break;
924 		case RTOverBelowStrategyNumber:
925 			retval = DatumGetBool(DirectFunctionCall2(box_overbelow,
926 													  PointerGetDatum(key),
927 													  PointerGetDatum(query)));
928 			break;
929 		case RTBelowStrategyNumber:
930 			retval = DatumGetBool(DirectFunctionCall2(box_below,
931 													  PointerGetDatum(key),
932 													  PointerGetDatum(query)));
933 			break;
934 		case RTAboveStrategyNumber:
935 			retval = DatumGetBool(DirectFunctionCall2(box_above,
936 													  PointerGetDatum(key),
937 													  PointerGetDatum(query)));
938 			break;
939 		case RTOverAboveStrategyNumber:
940 			retval = DatumGetBool(DirectFunctionCall2(box_overabove,
941 													  PointerGetDatum(key),
942 													  PointerGetDatum(query)));
943 			break;
944 		default:
945 			elog(ERROR, "unrecognized strategy number: %d", strategy);
946 			retval = false;		/* keep compiler quiet */
947 			break;
948 	}
949 	return retval;
950 }
951 
952 /*****************************************
953  * Common rtree functions (for boxes, polygons, and circles)
954  *****************************************/
955 
956 /*
957  * Internal-page consistency for all these types
958  *
959  * We can use the same function since all types use bounding boxes as the
960  * internal-page representation.
961  */
962 static bool
rtree_internal_consistent(BOX * key,BOX * query,StrategyNumber strategy)963 rtree_internal_consistent(BOX *key, BOX *query, StrategyNumber strategy)
964 {
965 	bool		retval;
966 
967 	switch (strategy)
968 	{
969 		case RTLeftStrategyNumber:
970 			retval = !DatumGetBool(DirectFunctionCall2(box_overright,
971 													   PointerGetDatum(key),
972 													   PointerGetDatum(query)));
973 			break;
974 		case RTOverLeftStrategyNumber:
975 			retval = !DatumGetBool(DirectFunctionCall2(box_right,
976 													   PointerGetDatum(key),
977 													   PointerGetDatum(query)));
978 			break;
979 		case RTOverlapStrategyNumber:
980 			retval = DatumGetBool(DirectFunctionCall2(box_overlap,
981 													  PointerGetDatum(key),
982 													  PointerGetDatum(query)));
983 			break;
984 		case RTOverRightStrategyNumber:
985 			retval = !DatumGetBool(DirectFunctionCall2(box_left,
986 													   PointerGetDatum(key),
987 													   PointerGetDatum(query)));
988 			break;
989 		case RTRightStrategyNumber:
990 			retval = !DatumGetBool(DirectFunctionCall2(box_overleft,
991 													   PointerGetDatum(key),
992 													   PointerGetDatum(query)));
993 			break;
994 		case RTSameStrategyNumber:
995 		case RTContainsStrategyNumber:
996 		case RTOldContainsStrategyNumber:
997 			retval = DatumGetBool(DirectFunctionCall2(box_contain,
998 													  PointerGetDatum(key),
999 													  PointerGetDatum(query)));
1000 			break;
1001 		case RTContainedByStrategyNumber:
1002 		case RTOldContainedByStrategyNumber:
1003 			retval = DatumGetBool(DirectFunctionCall2(box_overlap,
1004 													  PointerGetDatum(key),
1005 													  PointerGetDatum(query)));
1006 			break;
1007 		case RTOverBelowStrategyNumber:
1008 			retval = !DatumGetBool(DirectFunctionCall2(box_above,
1009 													   PointerGetDatum(key),
1010 													   PointerGetDatum(query)));
1011 			break;
1012 		case RTBelowStrategyNumber:
1013 			retval = !DatumGetBool(DirectFunctionCall2(box_overabove,
1014 													   PointerGetDatum(key),
1015 													   PointerGetDatum(query)));
1016 			break;
1017 		case RTAboveStrategyNumber:
1018 			retval = !DatumGetBool(DirectFunctionCall2(box_overbelow,
1019 													   PointerGetDatum(key),
1020 													   PointerGetDatum(query)));
1021 			break;
1022 		case RTOverAboveStrategyNumber:
1023 			retval = !DatumGetBool(DirectFunctionCall2(box_below,
1024 													   PointerGetDatum(key),
1025 													   PointerGetDatum(query)));
1026 			break;
1027 		default:
1028 			elog(ERROR, "unrecognized strategy number: %d", strategy);
1029 			retval = false;		/* keep compiler quiet */
1030 			break;
1031 	}
1032 	return retval;
1033 }
1034 
1035 /**************************************************
1036  * Polygon ops
1037  **************************************************/
1038 
1039 /*
1040  * GiST compress for polygons: represent a polygon by its bounding box
1041  */
1042 Datum
gist_poly_compress(PG_FUNCTION_ARGS)1043 gist_poly_compress(PG_FUNCTION_ARGS)
1044 {
1045 	GISTENTRY  *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
1046 	GISTENTRY  *retval;
1047 
1048 	if (entry->leafkey)
1049 	{
1050 		POLYGON    *in = DatumGetPolygonP(entry->key);
1051 		BOX		   *r;
1052 
1053 		r = (BOX *) palloc(sizeof(BOX));
1054 		memcpy((void *) r, (void *) &(in->boundbox), sizeof(BOX));
1055 
1056 		retval = (GISTENTRY *) palloc(sizeof(GISTENTRY));
1057 		gistentryinit(*retval, PointerGetDatum(r),
1058 					  entry->rel, entry->page,
1059 					  entry->offset, false);
1060 	}
1061 	else
1062 		retval = entry;
1063 	PG_RETURN_POINTER(retval);
1064 }
1065 
1066 /*
1067  * The GiST Consistent method for polygons
1068  */
1069 Datum
gist_poly_consistent(PG_FUNCTION_ARGS)1070 gist_poly_consistent(PG_FUNCTION_ARGS)
1071 {
1072 	GISTENTRY  *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
1073 	POLYGON    *query = PG_GETARG_POLYGON_P(1);
1074 	StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2);
1075 
1076 	/* Oid		subtype = PG_GETARG_OID(3); */
1077 	bool	   *recheck = (bool *) PG_GETARG_POINTER(4);
1078 	bool		result;
1079 
1080 	/* All cases served by this function are inexact */
1081 	*recheck = true;
1082 
1083 	if (DatumGetBoxP(entry->key) == NULL || query == NULL)
1084 		PG_RETURN_BOOL(false);
1085 
1086 	/*
1087 	 * Since the operators require recheck anyway, we can just use
1088 	 * rtree_internal_consistent even at leaf nodes.  (This works in part
1089 	 * because the index entries are bounding boxes not polygons.)
1090 	 */
1091 	result = rtree_internal_consistent(DatumGetBoxP(entry->key),
1092 									   &(query->boundbox), strategy);
1093 
1094 	/* Avoid memory leak if supplied poly is toasted */
1095 	PG_FREE_IF_COPY(query, 1);
1096 
1097 	PG_RETURN_BOOL(result);
1098 }
1099 
1100 /**************************************************
1101  * Circle ops
1102  **************************************************/
1103 
1104 /*
1105  * GiST compress for circles: represent a circle by its bounding box
1106  */
1107 Datum
gist_circle_compress(PG_FUNCTION_ARGS)1108 gist_circle_compress(PG_FUNCTION_ARGS)
1109 {
1110 	GISTENTRY  *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
1111 	GISTENTRY  *retval;
1112 
1113 	if (entry->leafkey)
1114 	{
1115 		CIRCLE	   *in = DatumGetCircleP(entry->key);
1116 		BOX		   *r;
1117 
1118 		r = (BOX *) palloc(sizeof(BOX));
1119 		r->high.x = in->center.x + in->radius;
1120 		r->low.x = in->center.x - in->radius;
1121 		r->high.y = in->center.y + in->radius;
1122 		r->low.y = in->center.y - in->radius;
1123 
1124 		retval = (GISTENTRY *) palloc(sizeof(GISTENTRY));
1125 		gistentryinit(*retval, PointerGetDatum(r),
1126 					  entry->rel, entry->page,
1127 					  entry->offset, false);
1128 	}
1129 	else
1130 		retval = entry;
1131 	PG_RETURN_POINTER(retval);
1132 }
1133 
1134 /*
1135  * The GiST Consistent method for circles
1136  */
1137 Datum
gist_circle_consistent(PG_FUNCTION_ARGS)1138 gist_circle_consistent(PG_FUNCTION_ARGS)
1139 {
1140 	GISTENTRY  *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
1141 	CIRCLE	   *query = PG_GETARG_CIRCLE_P(1);
1142 	StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2);
1143 
1144 	/* Oid		subtype = PG_GETARG_OID(3); */
1145 	bool	   *recheck = (bool *) PG_GETARG_POINTER(4);
1146 	BOX			bbox;
1147 	bool		result;
1148 
1149 	/* All cases served by this function are inexact */
1150 	*recheck = true;
1151 
1152 	if (DatumGetBoxP(entry->key) == NULL || query == NULL)
1153 		PG_RETURN_BOOL(false);
1154 
1155 	/*
1156 	 * Since the operators require recheck anyway, we can just use
1157 	 * rtree_internal_consistent even at leaf nodes.  (This works in part
1158 	 * because the index entries are bounding boxes not circles.)
1159 	 */
1160 	bbox.high.x = query->center.x + query->radius;
1161 	bbox.low.x = query->center.x - query->radius;
1162 	bbox.high.y = query->center.y + query->radius;
1163 	bbox.low.y = query->center.y - query->radius;
1164 
1165 	result = rtree_internal_consistent(DatumGetBoxP(entry->key),
1166 									   &bbox, strategy);
1167 
1168 	PG_RETURN_BOOL(result);
1169 }
1170 
1171 /**************************************************
1172  * Point ops
1173  **************************************************/
1174 
1175 Datum
gist_point_compress(PG_FUNCTION_ARGS)1176 gist_point_compress(PG_FUNCTION_ARGS)
1177 {
1178 	GISTENTRY  *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
1179 
1180 	if (entry->leafkey)			/* Point, actually */
1181 	{
1182 		BOX		   *box = palloc(sizeof(BOX));
1183 		Point	   *point = DatumGetPointP(entry->key);
1184 		GISTENTRY  *retval = palloc(sizeof(GISTENTRY));
1185 
1186 		box->high = box->low = *point;
1187 
1188 		gistentryinit(*retval, BoxPGetDatum(box),
1189 					  entry->rel, entry->page, entry->offset, false);
1190 
1191 		PG_RETURN_POINTER(retval);
1192 	}
1193 
1194 	PG_RETURN_POINTER(entry);
1195 }
1196 
1197 /*
1198  * GiST Fetch method for point
1199  *
1200  * Get point coordinates from its bounding box coordinates and form new
1201  * gistentry.
1202  */
1203 Datum
gist_point_fetch(PG_FUNCTION_ARGS)1204 gist_point_fetch(PG_FUNCTION_ARGS)
1205 {
1206 	GISTENTRY  *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
1207 	BOX		   *in = DatumGetBoxP(entry->key);
1208 	Point	   *r;
1209 	GISTENTRY  *retval;
1210 
1211 	retval = palloc(sizeof(GISTENTRY));
1212 
1213 	r = (Point *) palloc(sizeof(Point));
1214 	r->x = in->high.x;
1215 	r->y = in->high.y;
1216 	gistentryinit(*retval, PointerGetDatum(r),
1217 				  entry->rel, entry->page,
1218 				  entry->offset, false);
1219 
1220 	PG_RETURN_POINTER(retval);
1221 }
1222 
1223 
1224 #define point_point_distance(p1,p2) \
1225 	DatumGetFloat8(DirectFunctionCall2(point_distance, \
1226 									   PointPGetDatum(p1), PointPGetDatum(p2)))
1227 
1228 static double
computeDistance(bool isLeaf,BOX * box,Point * point)1229 computeDistance(bool isLeaf, BOX *box, Point *point)
1230 {
1231 	double		result = 0.0;
1232 
1233 	if (isLeaf)
1234 	{
1235 		/* simple point to point distance */
1236 		result = point_point_distance(point, &box->low);
1237 	}
1238 	else if (point->x <= box->high.x && point->x >= box->low.x &&
1239 			 point->y <= box->high.y && point->y >= box->low.y)
1240 	{
1241 		/* point inside the box */
1242 		result = 0.0;
1243 	}
1244 	else if (point->x <= box->high.x && point->x >= box->low.x)
1245 	{
1246 		/* point is over or below box */
1247 		Assert(box->low.y <= box->high.y);
1248 		if (point->y > box->high.y)
1249 			result = point->y - box->high.y;
1250 		else if (point->y < box->low.y)
1251 			result = box->low.y - point->y;
1252 		else
1253 			elog(ERROR, "inconsistent point values");
1254 	}
1255 	else if (point->y <= box->high.y && point->y >= box->low.y)
1256 	{
1257 		/* point is to left or right of box */
1258 		Assert(box->low.x <= box->high.x);
1259 		if (point->x > box->high.x)
1260 			result = point->x - box->high.x;
1261 		else if (point->x < box->low.x)
1262 			result = box->low.x - point->x;
1263 		else
1264 			elog(ERROR, "inconsistent point values");
1265 	}
1266 	else
1267 	{
1268 		/* closest point will be a vertex */
1269 		Point		p;
1270 		double		subresult;
1271 
1272 		result = point_point_distance(point, &box->low);
1273 
1274 		subresult = point_point_distance(point, &box->high);
1275 		if (result > subresult)
1276 			result = subresult;
1277 
1278 		p.x = box->low.x;
1279 		p.y = box->high.y;
1280 		subresult = point_point_distance(point, &p);
1281 		if (result > subresult)
1282 			result = subresult;
1283 
1284 		p.x = box->high.x;
1285 		p.y = box->low.y;
1286 		subresult = point_point_distance(point, &p);
1287 		if (result > subresult)
1288 			result = subresult;
1289 	}
1290 
1291 	return result;
1292 }
1293 
1294 static bool
gist_point_consistent_internal(StrategyNumber strategy,bool isLeaf,BOX * key,Point * query)1295 gist_point_consistent_internal(StrategyNumber strategy,
1296 							   bool isLeaf, BOX *key, Point *query)
1297 {
1298 	bool		result = false;
1299 
1300 	switch (strategy)
1301 	{
1302 		case RTLeftStrategyNumber:
1303 			result = FPlt(key->low.x, query->x);
1304 			break;
1305 		case RTRightStrategyNumber:
1306 			result = FPgt(key->high.x, query->x);
1307 			break;
1308 		case RTAboveStrategyNumber:
1309 			result = FPgt(key->high.y, query->y);
1310 			break;
1311 		case RTBelowStrategyNumber:
1312 			result = FPlt(key->low.y, query->y);
1313 			break;
1314 		case RTSameStrategyNumber:
1315 			if (isLeaf)
1316 			{
1317 				/* key.high must equal key.low, so we can disregard it */
1318 				result = (FPeq(key->low.x, query->x) &&
1319 						  FPeq(key->low.y, query->y));
1320 			}
1321 			else
1322 			{
1323 				result = (FPle(query->x, key->high.x) &&
1324 						  FPge(query->x, key->low.x) &&
1325 						  FPle(query->y, key->high.y) &&
1326 						  FPge(query->y, key->low.y));
1327 			}
1328 			break;
1329 		default:
1330 			elog(ERROR, "unrecognized strategy number: %d", strategy);
1331 			result = false;		/* keep compiler quiet */
1332 			break;
1333 	}
1334 
1335 	return result;
1336 }
1337 
1338 #define GeoStrategyNumberOffset		20
1339 #define PointStrategyNumberGroup	0
1340 #define BoxStrategyNumberGroup		1
1341 #define PolygonStrategyNumberGroup	2
1342 #define CircleStrategyNumberGroup	3
1343 
1344 Datum
gist_point_consistent(PG_FUNCTION_ARGS)1345 gist_point_consistent(PG_FUNCTION_ARGS)
1346 {
1347 	GISTENTRY  *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
1348 	StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2);
1349 	bool	   *recheck = (bool *) PG_GETARG_POINTER(4);
1350 	bool		result;
1351 	StrategyNumber strategyGroup = strategy / GeoStrategyNumberOffset;
1352 
1353 	switch (strategyGroup)
1354 	{
1355 		case PointStrategyNumberGroup:
1356 			result = gist_point_consistent_internal(strategy % GeoStrategyNumberOffset,
1357 													GIST_LEAF(entry),
1358 													DatumGetBoxP(entry->key),
1359 													PG_GETARG_POINT_P(1));
1360 			*recheck = false;
1361 			break;
1362 		case BoxStrategyNumberGroup:
1363 			{
1364 				/*
1365 				 * The only operator in this group is point <@ box (on_pb), so
1366 				 * we needn't examine strategy again.
1367 				 *
1368 				 * For historical reasons, on_pb uses exact rather than fuzzy
1369 				 * comparisons.  We could use box_overlap when at an internal
1370 				 * page, but that would lead to possibly visiting child pages
1371 				 * uselessly, because box_overlap uses fuzzy comparisons.
1372 				 * Instead we write a non-fuzzy overlap test.  The same code
1373 				 * will also serve for leaf-page tests, since leaf keys have
1374 				 * high == low.
1375 				 */
1376 				BOX		   *query,
1377 						   *key;
1378 
1379 				query = PG_GETARG_BOX_P(1);
1380 				key = DatumGetBoxP(entry->key);
1381 
1382 				result = (key->high.x >= query->low.x &&
1383 						  key->low.x <= query->high.x &&
1384 						  key->high.y >= query->low.y &&
1385 						  key->low.y <= query->high.y);
1386 				*recheck = false;
1387 			}
1388 			break;
1389 		case PolygonStrategyNumberGroup:
1390 			{
1391 				POLYGON    *query = PG_GETARG_POLYGON_P(1);
1392 
1393 				result = DatumGetBool(DirectFunctionCall5(
1394 														  gist_poly_consistent,
1395 														  PointerGetDatum(entry),
1396 														  PolygonPGetDatum(query),
1397 														  Int16GetDatum(RTOverlapStrategyNumber),
1398 														  0, PointerGetDatum(recheck)));
1399 
1400 				if (GIST_LEAF(entry) && result)
1401 				{
1402 					/*
1403 					 * We are on leaf page and quick check shows overlapping
1404 					 * of polygon's bounding box and point
1405 					 */
1406 					BOX		   *box = DatumGetBoxP(entry->key);
1407 
1408 					Assert(box->high.x == box->low.x
1409 						   && box->high.y == box->low.y);
1410 					result = DatumGetBool(DirectFunctionCall2(
1411 															  poly_contain_pt,
1412 															  PolygonPGetDatum(query),
1413 															  PointPGetDatum(&box->high)));
1414 					*recheck = false;
1415 				}
1416 			}
1417 			break;
1418 		case CircleStrategyNumberGroup:
1419 			{
1420 				CIRCLE	   *query = PG_GETARG_CIRCLE_P(1);
1421 
1422 				result = DatumGetBool(DirectFunctionCall5(
1423 														  gist_circle_consistent,
1424 														  PointerGetDatum(entry),
1425 														  CirclePGetDatum(query),
1426 														  Int16GetDatum(RTOverlapStrategyNumber),
1427 														  0, PointerGetDatum(recheck)));
1428 
1429 				if (GIST_LEAF(entry) && result)
1430 				{
1431 					/*
1432 					 * We are on leaf page and quick check shows overlapping
1433 					 * of polygon's bounding box and point
1434 					 */
1435 					BOX		   *box = DatumGetBoxP(entry->key);
1436 
1437 					Assert(box->high.x == box->low.x
1438 						   && box->high.y == box->low.y);
1439 					result = DatumGetBool(DirectFunctionCall2(
1440 															  circle_contain_pt,
1441 															  CirclePGetDatum(query),
1442 															  PointPGetDatum(&box->high)));
1443 					*recheck = false;
1444 				}
1445 			}
1446 			break;
1447 		default:
1448 			elog(ERROR, "unrecognized strategy number: %d", strategy);
1449 			result = false;		/* keep compiler quiet */
1450 			break;
1451 	}
1452 
1453 	PG_RETURN_BOOL(result);
1454 }
1455 
1456 Datum
gist_point_distance(PG_FUNCTION_ARGS)1457 gist_point_distance(PG_FUNCTION_ARGS)
1458 {
1459 	GISTENTRY  *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
1460 	StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2);
1461 	double		distance;
1462 	StrategyNumber strategyGroup = strategy / GeoStrategyNumberOffset;
1463 
1464 	switch (strategyGroup)
1465 	{
1466 		case PointStrategyNumberGroup:
1467 			distance = computeDistance(GIST_LEAF(entry),
1468 									   DatumGetBoxP(entry->key),
1469 									   PG_GETARG_POINT_P(1));
1470 			break;
1471 		default:
1472 			elog(ERROR, "unrecognized strategy number: %d", strategy);
1473 			distance = 0.0;		/* keep compiler quiet */
1474 			break;
1475 	}
1476 
1477 	PG_RETURN_FLOAT8(distance);
1478 }
1479 
1480 /*
1481  * The inexact GiST distance method for geometric types that store bounding
1482  * boxes.
1483  *
1484  * Compute lossy distance from point to index entries.  The result is inexact
1485  * because index entries are bounding boxes, not the exact shapes of the
1486  * indexed geometric types.  We use distance from point to MBR of index entry.
1487  * This is a lower bound estimate of distance from point to indexed geometric
1488  * type.
1489  */
1490 static double
gist_bbox_distance(GISTENTRY * entry,Datum query,StrategyNumber strategy,bool * recheck)1491 gist_bbox_distance(GISTENTRY *entry, Datum query,
1492 				   StrategyNumber strategy, bool *recheck)
1493 {
1494 	double		distance;
1495 	StrategyNumber strategyGroup = strategy / GeoStrategyNumberOffset;
1496 
1497 	/* Bounding box distance is always inexact. */
1498 	*recheck = true;
1499 
1500 	switch (strategyGroup)
1501 	{
1502 		case PointStrategyNumberGroup:
1503 			distance = computeDistance(false,
1504 									   DatumGetBoxP(entry->key),
1505 									   DatumGetPointP(query));
1506 			break;
1507 		default:
1508 			elog(ERROR, "unrecognized strategy number: %d", strategy);
1509 			distance = 0.0;		/* keep compiler quiet */
1510 	}
1511 
1512 	return distance;
1513 }
1514 
1515 Datum
gist_circle_distance(PG_FUNCTION_ARGS)1516 gist_circle_distance(PG_FUNCTION_ARGS)
1517 {
1518 	GISTENTRY  *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
1519 	Datum		query = PG_GETARG_DATUM(1);
1520 	StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2);
1521 
1522 	/* Oid subtype = PG_GETARG_OID(3); */
1523 	bool	   *recheck = (bool *) PG_GETARG_POINTER(4);
1524 	double		distance;
1525 
1526 	distance = gist_bbox_distance(entry, query, strategy, recheck);
1527 
1528 	PG_RETURN_FLOAT8(distance);
1529 }
1530 
1531 Datum
gist_poly_distance(PG_FUNCTION_ARGS)1532 gist_poly_distance(PG_FUNCTION_ARGS)
1533 {
1534 	GISTENTRY  *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
1535 	Datum		query = PG_GETARG_DATUM(1);
1536 	StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2);
1537 
1538 	/* Oid subtype = PG_GETARG_OID(3); */
1539 	bool	   *recheck = (bool *) PG_GETARG_POINTER(4);
1540 	double		distance;
1541 
1542 	distance = gist_bbox_distance(entry, query, strategy, recheck);
1543 
1544 	PG_RETURN_FLOAT8(distance);
1545 }
1546