PostGIS  3.4.0dev-r@@SVN_REVISION@@
gserialized_gist_2d.c
Go to the documentation of this file.
1 /**********************************************************************
2  *
3  * PostGIS - Spatial Types for PostgreSQL
4  * http://postgis.net
5  *
6  * PostGIS is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * PostGIS is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with PostGIS. If not, see <http://www.gnu.org/licenses/>.
18  *
19  **********************************************************************
20  *
21  * Copyright 2009 Paul Ramsey <pramsey@cleverelephant.ca>
22  * Copyright 2017-2022 Darafei Praliaskouski <me@komzpa.net>
23  *
24  **********************************************************************/
25 
26 /*
27 ** R-Tree Bibliography
28 **
29 ** [1] A. Guttman. R-tree: a dynamic index structure for spatial searching.
30 ** Proceedings of the ACM SIGMOD Conference, pp 47-57, June 1984.
31 ** [2] C.-H. Ang and T. C. Tan. New linear node splitting algorithm for
32 ** R-Trees. Advances in Spatial Databases - 5th International Symposium,
33 ** 1997
34 ** [3] N. Beckmann, H.-P. Kriegel, R. Schneider, B. Seeger. The R*tree: an
35 ** efficient and robust access method for points and rectangles.
36 ** Proceedings of the ACM SIGMOD Conference. June 1990.
37 ** [4] A. Korotkov, "A new double sorting-based node splitting algorithm for R-tree",
38 ** http://syrcose.ispras.ru/2011/files/SYRCoSE2011_Proceedings.pdf#page=36
39 */
40 
41 #include "postgres.h"
42 #include "access/gist.h" /* For GiST */
43 #include "access/itup.h"
44 #include "access/skey.h"
45 #include "utils/sortsupport.h" /* For index building sort support */
46 
47 #include "../postgis_config.h"
48 
49 #include "liblwgeom.h" /* For standard geometry types. */
50 #include "liblwgeom_internal.h"
51 #include "lwgeom_pg.h" /* For debugging macros. */
52 #include "gserialized_gist.h" /* For utility functions. */
53 
54 #include <float.h> /* For FLT_MAX */
55 #include <math.h>
56 
57 /* When is a node split not so good? Prefer to not split 2 pages into more than 3 pages in index. */
58 #define LIMIT_RATIO 0.3333333333333333
59 
60 /* How many index tuples does one page fit? Recursive splits will target this. */
61 #define INDEX_TUPLES_PER_PAGE 291
62 
63 /*
64 ** For debugging
65 */
66 #if POSTGIS_DEBUG_LEVEL > 0
67 static int g2d_counter_leaf = 0;
68 static int g2d_counter_internal = 0;
69 #endif
70 
71 /*
72 ** GiST 2D key stubs
73 */
74 Datum box2df_out(PG_FUNCTION_ARGS);
75 Datum box2df_in(PG_FUNCTION_ARGS);
76 
77 /*
78 ** GiST 2D index function prototypes
79 */
80 Datum gserialized_gist_consistent_2d(PG_FUNCTION_ARGS);
81 Datum gserialized_gist_compress_2d(PG_FUNCTION_ARGS);
82 Datum gserialized_gist_decompress_2d(PG_FUNCTION_ARGS);
83 Datum gserialized_gist_penalty_2d(PG_FUNCTION_ARGS);
84 Datum gserialized_gist_picksplit_2d(PG_FUNCTION_ARGS);
85 Datum gserialized_gist_union_2d(PG_FUNCTION_ARGS);
86 Datum gserialized_gist_same_2d(PG_FUNCTION_ARGS);
87 Datum gserialized_gist_distance_2d(PG_FUNCTION_ARGS);
88 Datum gserialized_gist_sortsupport_2d(PG_FUNCTION_ARGS);
89 
90 /*
91 ** GiST 2D operator prototypes
92 */
93 Datum gserialized_same_2d(PG_FUNCTION_ARGS);
94 Datum gserialized_within_2d(PG_FUNCTION_ARGS);
95 Datum gserialized_contains_2d(PG_FUNCTION_ARGS);
96 Datum gserialized_overlaps_2d(PG_FUNCTION_ARGS);
97 Datum gserialized_left_2d(PG_FUNCTION_ARGS);
98 Datum gserialized_right_2d(PG_FUNCTION_ARGS);
99 Datum gserialized_above_2d(PG_FUNCTION_ARGS);
100 Datum gserialized_below_2d(PG_FUNCTION_ARGS);
101 Datum gserialized_overleft_2d(PG_FUNCTION_ARGS);
102 Datum gserialized_overright_2d(PG_FUNCTION_ARGS);
103 Datum gserialized_overabove_2d(PG_FUNCTION_ARGS);
104 Datum gserialized_overbelow_2d(PG_FUNCTION_ARGS);
105 Datum gserialized_distance_box_2d(PG_FUNCTION_ARGS);
106 Datum gserialized_distance_centroid_2d(PG_FUNCTION_ARGS);
107 Datum gserialized_contains_box2df_geom_2d(PG_FUNCTION_ARGS);
108 Datum gserialized_contains_box2df_box2df_2d(PG_FUNCTION_ARGS);
109 Datum gserialized_within_box2df_geom_2d(PG_FUNCTION_ARGS);
110 Datum gserialized_within_box2df_box2df_2d(PG_FUNCTION_ARGS);
111 Datum gserialized_overlaps_box2df_geom_2d(PG_FUNCTION_ARGS);
112 Datum gserialized_overlaps_box2df_box2df_2d(PG_FUNCTION_ARGS);
113 
114 /*
115 ** true/false test function type
116 */
117 typedef bool (*box2df_predicate)(const BOX2DF *a, const BOX2DF *b);
118 
119 
120 static char* box2df_to_string(const BOX2DF *a)
121 {
122  static const int precision = 12;
123  char tmp[13 + 4 * OUT_MAX_BYTES_DOUBLE] = {'B', 'O', 'X', '2', 'D', 'F', '(', 0};
124  int len = 7;
125 
126  if (a == NULL)
127  return pstrdup("<NULLPTR>");
128 
129  len += lwprint_double(a->xmin, precision, &tmp[len]);
130  tmp[len++] = ' ';
131  len += lwprint_double(a->ymin, precision, &tmp[len]);
132  tmp[len++] = ',';
133  tmp[len++] = ' ';
134  len += lwprint_double(a->xmax, precision, &tmp[len]);
135  tmp[len++] = ' ';
136  len += lwprint_double(a->ymax, precision, &tmp[len]);
137  tmp[len++] = ')';
138 
139  return pstrdup(tmp);
140 }
141 
142 /* Allocate a new copy of BOX2DF */
143 BOX2DF* box2df_copy(BOX2DF *b)
144 {
145  BOX2DF *c = (BOX2DF*)palloc(sizeof(BOX2DF));
146  memcpy((void*)c, (void*)b, sizeof(BOX2DF));
147  POSTGIS_DEBUGF(5, "copied box2df (%p) to box2df (%p)", b, c);
148  return c;
149 }
150 
151 inline bool box2df_is_empty(const BOX2DF *a)
152 {
153  if (isnan(a->xmin))
154  return true;
155  else
156  return false;
157 }
158 
159 inline void box2df_set_empty(BOX2DF *a)
160 {
161  a->xmin = a->xmax = a->ymin = a->ymax = NAN;
162  return;
163 }
164 
165 inline void box2df_set_finite(BOX2DF *a)
166 {
167  if ( ! isfinite(a->xmax) )
168  a->xmax = FLT_MAX;
169  if ( ! isfinite(a->ymax) )
170  a->ymax = FLT_MAX;
171  if ( ! isfinite(a->ymin) )
172  a->ymin = -1*FLT_MAX;
173  if ( ! isfinite(a->xmin) )
174  a->xmin = -1*FLT_MAX;
175  return;
176 }
177 
178 /* Enlarge b_union to contain b_new. */
179 void box2df_merge(BOX2DF *b_union, BOX2DF *b_new)
180 {
181 
182  POSTGIS_DEBUGF(5, "merging %s with %s", box2df_to_string(b_union), box2df_to_string(b_new));
183  /* Adjust minimums */
184  if (b_union->xmin > b_new->xmin || isnan(b_union->xmin))
185  b_union->xmin = b_new->xmin;
186  if (b_union->ymin > b_new->ymin || isnan(b_union->ymin))
187  b_union->ymin = b_new->ymin;
188  /* Adjust maximums */
189  if (b_union->xmax < b_new->xmax || isnan(b_union->xmax))
190  b_union->xmax = b_new->xmax;
191  if (b_union->ymax < b_new->ymax || isnan(b_union->ymax))
192  b_union->ymax = b_new->ymax;
193 
194  POSTGIS_DEBUGF(5, "merge complete %s", box2df_to_string(b_union));
195  return;
196 }
197 
198 
199 /* Convert a double-based GBOX into a float-based BOX2DF,
200  ensuring the float box is larger than the double box */
201 static inline int box2df_from_gbox_p(GBOX *box, BOX2DF *a)
202 {
203  a->xmin = next_float_down(box->xmin);
204  a->xmax = next_float_up(box->xmax);
205  a->ymin = next_float_down(box->ymin);
206  a->ymax = next_float_up(box->ymax);
207  return LW_SUCCESS;
208 }
209 
210 int box2df_to_gbox_p(BOX2DF *a, GBOX *box)
211 {
212  memset(box, 0, sizeof(GBOX));
213  box->xmin = a->xmin;
214  box->xmax = a->xmax;
215  box->ymin = a->ymin;
216  box->ymax = a->ymax;
217  return LW_SUCCESS;
218 }
219 
220 /***********************************************************************
221 ** BOX2DF tests for 2D index operators.
222 */
223 
224 /* Ensure all minimums are below maximums. */
225 inline void box2df_validate(BOX2DF *b)
226 {
227  float tmp;
228 
229  if ( box2df_is_empty(b) )
230  return;
231 
232  if ( b->xmax < b->xmin )
233  {
234  tmp = b->xmin;
235  b->xmin = b->xmax;
236  b->xmax = tmp;
237  }
238  if ( b->ymax < b->ymin )
239  {
240  tmp = b->ymin;
241  b->ymin = b->ymax;
242  b->ymax = tmp;
243  }
244  return;
245 }
246 
247 bool box2df_overlaps(const BOX2DF *a, const BOX2DF *b)
248 {
249  if ( !a || !b || box2df_is_empty(a) || box2df_is_empty(b) )
250  return false;
251 
252  if ( (a->xmin > b->xmax) || (b->xmin > a->xmax) ||
253  (a->ymin > b->ymax) || (b->ymin > a->ymax) )
254  {
255  return false;
256  }
257 
258  return true;
259 }
260 
261 bool box2df_contains(const BOX2DF *a, const BOX2DF *b)
262 {
263  if ( !a || !b )
264  return false;
265 
266  /* All things can contain EMPTY (except EMPTY) */
267  if ( box2df_is_empty(b) && ! box2df_is_empty(a) )
268  return true;
269 
270  if ( (a->xmin > b->xmin) || (a->xmax < b->xmax) ||
271  (a->ymin > b->ymin) || (a->ymax < b->ymax) )
272  {
273  return false;
274  }
275 
276  return true;
277 }
278 
279 static bool box2df_within(const BOX2DF *a, const BOX2DF *b)
280 {
281  if ( !a || !b )
282  return false;
283 
284  /* EMPTY is within all other things (except EMPTY) */
285  if ( box2df_is_empty(a) && ! box2df_is_empty(b) )
286  return true;
287 
288  if ( (a->xmin < b->xmin) || (a->xmax > b->xmax) ||
289  (a->ymin < b->ymin) || (a->ymax > b->ymax) )
290  {
291  return false;
292  }
293 
294  return true;
295 }
296 
297 bool box2df_equals(const BOX2DF *a, const BOX2DF *b)
298 {
299  if ( !a && !b )
300  return true;
301  else if ( !a || !b )
302  return false;
303  else if ( box2df_is_empty(a) && box2df_is_empty(b) )
304  return true;
305  else if ( box2df_is_empty(a) || box2df_is_empty(b) )
306  return false;
307  else if ((a->xmin == b->xmin) && (a->xmax == b->xmax) && (a->ymin == b->ymin) && (a->ymax == b->ymax))
308  return true;
309  else
310  return false;
311 }
312 
313 bool box2df_overleft(const BOX2DF *a, const BOX2DF *b)
314 {
315  if ( !a || !b || box2df_is_empty(a) || box2df_is_empty(b) )
316  return false;
317 
318  /* a.xmax <= b.xmax */
319  return a->xmax <= b->xmax;
320 }
321 
322 bool box2df_left(const BOX2DF *a, const BOX2DF *b)
323 {
324  if ( !a || !b || box2df_is_empty(a) || box2df_is_empty(b) )
325  return false;
326 
327  /* a.xmax < b.xmin */
328  return a->xmax < b->xmin;
329 }
330 
331 bool box2df_right(const BOX2DF *a, const BOX2DF *b)
332 {
333  if ( !a || !b || box2df_is_empty(a) || box2df_is_empty(b) )
334  return false;
335 
336  /* a.xmin > b.xmax */
337  return a->xmin > b->xmax;
338 }
339 
340 bool box2df_overright(const BOX2DF *a, const BOX2DF *b)
341 {
342  if ( !a || !b || box2df_is_empty(a) || box2df_is_empty(b) )
343  return false;
344 
345  /* a.xmin >= b.xmin */
346  return a->xmin >= b->xmin;
347 }
348 
349 bool box2df_overbelow(const BOX2DF *a, const BOX2DF *b)
350 {
351  if ( !a || !b || box2df_is_empty(a) || box2df_is_empty(b) )
352  return false;
353 
354  /* a.ymax <= b.ymax */
355  return a->ymax <= b->ymax;
356 }
357 
358 bool box2df_below(const BOX2DF *a, const BOX2DF *b)
359 {
360  if ( !a || !b || box2df_is_empty(a) || box2df_is_empty(b) )
361  return false;
362 
363  /* a.ymax < b.ymin */
364  return a->ymax < b->ymin;
365 }
366 
367 bool box2df_above(const BOX2DF *a, const BOX2DF *b)
368 {
369  if ( !a || !b || box2df_is_empty(a) || box2df_is_empty(b) )
370  return false;
371 
372  /* a.ymin > b.ymax */
373  return a->ymin > b->ymax;
374 }
375 
376 bool box2df_overabove(const BOX2DF *a, const BOX2DF *b)
377 {
378  if ( !a || !b || box2df_is_empty(a) || box2df_is_empty(b) )
379  return false;
380 
381  /* a.ymin >= b.ymin */
382  return a->ymin >= b->ymin;
383 }
384 
388 static double box2df_distance_leaf_centroid(const BOX2DF *a, const BOX2DF *b)
389 {
390  /* The centroid->centroid distance between the boxes */
391  double a_x = (a->xmax + a->xmin) / 2.0;
392  double a_y = (a->ymax + a->ymin) / 2.0;
393  double b_x = (b->xmax + b->xmin) / 2.0;
394  double b_y = (b->ymax + b->ymin) / 2.0;
395 
396  /* This "distance" is only used for comparisons, */
397  return sqrt((a_x - b_x) * (a_x - b_x) + (a_y - b_y) * (a_y - b_y));
398 }
399 
400 /* Quick distance function */
401 static inline double pt_distance(double ax, double ay, double bx, double by)
402 {
403  return sqrt((ax - bx) * (ax - bx) + (ay - by) * (ay - by));
404 }
405 
409 static double box2df_distance(const BOX2DF *a, const BOX2DF *b)
410 {
411  /* Check for overlap */
412  if ( box2df_overlaps(a, b) )
413  return 0.0;
414 
415  if ( box2df_left(a, b) )
416  {
417  if ( box2df_above(a, b) )
418  return pt_distance(a->xmax, a->ymin, b->xmin, b->ymax);
419  if ( box2df_below(a, b) )
420  return pt_distance(a->xmax, a->ymax, b->xmin, b->ymin);
421  else
422  return (double)b->xmin - (double)a->xmax;
423  }
424  if ( box2df_right(a, b) )
425  {
426  if ( box2df_above(a, b) )
427  return pt_distance(a->xmin, a->ymin, b->xmax, b->ymax);
428  if ( box2df_below(a, b) )
429  return pt_distance(a->xmin, a->ymax, b->xmax, b->ymin);
430  else
431  return (double)a->xmin - (double)b->xmax;
432  }
433  if ( box2df_above(a, b) )
434  {
435  if ( box2df_left(a, b) )
436  return pt_distance(a->xmax, a->ymin, b->xmin, b->ymax);
437  if ( box2df_right(a, b) )
438  return pt_distance(a->xmin, a->ymin, b->xmax, b->ymax);
439  else
440  return (double)a->ymin - (double)b->ymax;
441  }
442  if ( box2df_below(a, b) )
443  {
444  if ( box2df_left(a, b) )
445  return pt_distance(a->xmax, a->ymax, b->xmin, b->ymin);
446  if ( box2df_right(a, b) )
447  return pt_distance(a->xmin, a->ymax, b->xmax, b->ymin);
448  else
449  return (double)b->ymin - (double)a->ymax;
450  }
451 
452  return FLT_MAX;
453 }
454 
455 static inline uint64_t
456 box2df_get_sortable_hash(const BOX2DF *b)
457 {
458  union floatuint {
459  uint32_t u;
460  float f;
461  } x, y;
462 
463  x.f = (b->xmax + b->xmin) / 2;
464  y.f = (b->ymax + b->ymin) / 2;
465 
466  return uint32_hilbert(y.u, x.u);
467 }
468 
474 int
475 gserialized_datum_get_internals_p(Datum gsdatum, GBOX *gbox, lwflags_t *flags, uint8_t *type, int32_t *srid)
476 {
477  int result = LW_SUCCESS;
478  GSERIALIZED *gpart = NULL;
479  int need_detoast = PG_GSERIALIZED_DATUM_NEEDS_DETOAST((struct varlena *)gsdatum);
480  if (need_detoast)
481  {
482  gpart = (GSERIALIZED *)PG_DETOAST_DATUM_SLICE(gsdatum, 0, gserialized_max_header_size());
483  }
484  else
485  {
486  gpart = (GSERIALIZED *)gsdatum;
487  }
488 
489  if (!gserialized_has_bbox(gpart) && need_detoast && LWSIZE_GET(gpart->size) >= gserialized_max_header_size())
490  {
491  /* The headers don't contain a bbox and the object is larger than what we retrieved, so
492  * we now detoast it completely */
493  POSTGIS_FREE_IF_COPY_P(gpart, gsdatum);
494  gpart = (GSERIALIZED *)PG_DETOAST_DATUM(gsdatum);
495  }
496 
497  result = gserialized_get_gbox_p(gpart, gbox);
498  *flags = gserialized_get_lwflags(gpart);
499  *srid = gserialized_get_srid(gpart);
500  *type = gserialized_get_type(gpart);
501 
502  POSTGIS_FREE_IF_COPY_P(gpart, gsdatum);
503  return result;
504 }
505 
513 int
514 gserialized_datum_get_gbox_p(Datum gsdatum, GBOX *gbox)
515 {
516  uint8_t type;
517  int32_t srid;
518  lwflags_t flags;
519 
520  return gserialized_datum_get_internals_p(gsdatum, gbox, &flags, &type, &srid);
521 }
522 
523 /* Note that this duplicates code from gserialized_datum_get_internals_p. It does so because
524  * accessing only the BOX2DF (as floats) is faster and this is a hot path function in indexes
525  */
526 int
527 gserialized_datum_get_box2df_p(Datum gsdatum, BOX2DF *box2df)
528 {
529  int result = LW_SUCCESS;
530  GSERIALIZED *gpart = NULL;
531  int need_detoast = PG_GSERIALIZED_DATUM_NEEDS_DETOAST((struct varlena *)gsdatum);
532  if (need_detoast)
533  {
534  gpart = (GSERIALIZED *)PG_DETOAST_DATUM_SLICE(gsdatum, 0, gserialized_max_header_size());
535  }
536  else
537  {
538  gpart = (GSERIALIZED *)gsdatum;
539  }
540 
541  if (gserialized_has_bbox(gpart))
542  {
543  size_t box_ndims;
544  const float *f = gserialized_get_float_box_p(gpart, &box_ndims);
545  memcpy(box2df, f, sizeof(BOX2DF));
546  result = LW_SUCCESS;
547  }
548  else
549  {
550  GBOX gbox = {0};
551  if (need_detoast && LWSIZE_GET(gpart->size) >= gserialized_max_header_size())
552  {
553  /* The headers don't contain a bbox and the object is larger than what we retrieved, so
554  * we now detoast it completely and recheck */
555  POSTGIS_FREE_IF_COPY_P(gpart, gsdatum);
556  gpart = (GSERIALIZED *)PG_DETOAST_DATUM(gsdatum);
557  }
558  result = gserialized_get_gbox_p(gpart, &gbox);
559  if (result == LW_SUCCESS)
560  {
561  result = box2df_from_gbox_p(&gbox, box2df);
562  }
563  }
564 
565  POSTGIS_FREE_IF_COPY_P(gpart, gsdatum);
566  return result;
567 }
568 
573 static int
574 gserialized_datum_predicate_2d(Datum gs1, Datum gs2, box2df_predicate predicate)
575 {
576  BOX2DF b1, b2, *br1=NULL, *br2=NULL;
577  POSTGIS_DEBUG(3, "entered function");
578 
579  if (gserialized_datum_get_box2df_p(gs1, &b1) == LW_SUCCESS) br1 = &b1;
580  if (gserialized_datum_get_box2df_p(gs2, &b2) == LW_SUCCESS) br2 = &b2;
581 
582  if ( predicate(br1, br2) )
583  {
584  POSTGIS_DEBUGF(3, "got boxes %s and %s", br1 ? box2df_to_string(&b1) : "(null)", br2 ? box2df_to_string(&b2) : "(null)");
585  return LW_TRUE;
586  }
587  return LW_FALSE;
588 }
589 
590 static int
591 gserialized_datum_predicate_box2df_geom_2d(const BOX2DF *br1, Datum gs2, box2df_predicate predicate)
592 {
593  BOX2DF b2, *br2=NULL;
594  POSTGIS_DEBUG(3, "entered function");
595 
596  if (gserialized_datum_get_box2df_p(gs2, &b2) == LW_SUCCESS) br2 = &b2;
597 
598  if ( predicate(br1, br2) )
599  {
600  POSTGIS_DEBUGF(3, "got boxes %s", br2 ? box2df_to_string(&b2) : "(null)");
601  return LW_TRUE;
602  }
603  return LW_FALSE;
604 }
605 
606 /***********************************************************************
607 * BRIN 2-D Index Operator Functions
608 */
609 
611 Datum gserialized_contains_box2df_geom_2d(PG_FUNCTION_ARGS)
612 {
613  POSTGIS_DEBUG(3, "entered function");
614  if ( gserialized_datum_predicate_box2df_geom_2d((BOX2DF*)PG_GETARG_POINTER(0), PG_GETARG_DATUM(1), box2df_contains) == LW_TRUE )
615  PG_RETURN_BOOL(true);
616 
617  PG_RETURN_BOOL(false);
618 }
619 
622 {
623  if ( box2df_contains((BOX2DF *)PG_GETARG_POINTER(0), (BOX2DF *)PG_GETARG_POINTER(1)))
624  PG_RETURN_BOOL(true);
625 
626  PG_RETURN_BOOL(false);
627 }
628 
630 Datum gserialized_within_box2df_geom_2d(PG_FUNCTION_ARGS)
631 {
632  POSTGIS_DEBUG(3, "entered function");
633  if ( gserialized_datum_predicate_box2df_geom_2d((BOX2DF*)PG_GETARG_POINTER(0), PG_GETARG_DATUM(1), box2df_within) == LW_TRUE )
634  PG_RETURN_BOOL(true);
635 
636  PG_RETURN_BOOL(false);
637 }
638 
640 Datum gserialized_within_box2df_box2df_2d(PG_FUNCTION_ARGS)
641 {
642  if ( box2df_within((BOX2DF *)PG_GETARG_POINTER(0), (BOX2DF *)PG_GETARG_POINTER(1)))
643  PG_RETURN_BOOL(true);
644 
645  PG_RETURN_BOOL(false);
646 }
647 
649 Datum gserialized_overlaps_box2df_geom_2d(PG_FUNCTION_ARGS)
650 {
651  POSTGIS_DEBUG(3, "entered function");
652  if ( gserialized_datum_predicate_box2df_geom_2d((BOX2DF*)PG_GETARG_POINTER(0), PG_GETARG_DATUM(1), box2df_overlaps) == LW_TRUE )
653  PG_RETURN_BOOL(true);
654 
655  PG_RETURN_BOOL(false);
656 }
657 
660 {
661  if ( box2df_overlaps((BOX2DF *)PG_GETARG_POINTER(0), (BOX2DF *)PG_GETARG_POINTER(1)))
662  PG_RETURN_BOOL(true);
663 
664  PG_RETURN_BOOL(false);
665 }
666 
667 /***********************************************************************
668 * GiST 2-D Index Operator Functions
669 */
670 
672 Datum gserialized_distance_centroid_2d(PG_FUNCTION_ARGS)
673 {
674  BOX2DF b1, b2;
675  Datum gs1 = PG_GETARG_DATUM(0);
676  Datum gs2 = PG_GETARG_DATUM(1);
677 
678  POSTGIS_DEBUG(3, "entered function");
679 
680  /* Must be able to build box for each argument (ie, not empty geometry). */
681  if ( (gserialized_datum_get_box2df_p(gs1, &b1) == LW_SUCCESS) &&
683  {
684  double distance = box2df_distance_leaf_centroid(&b1, &b2);
685  POSTGIS_DEBUGF(3, "got boxes %s and %s", box2df_to_string(&b1), box2df_to_string(&b2));
686  PG_RETURN_FLOAT8(distance);
687  }
688  PG_RETURN_FLOAT8(FLT_MAX);
689 }
690 
692 Datum gserialized_distance_box_2d(PG_FUNCTION_ARGS)
693 {
694  BOX2DF b1, b2;
695  Datum gs1 = PG_GETARG_DATUM(0);
696  Datum gs2 = PG_GETARG_DATUM(1);
697 
698  POSTGIS_DEBUG(3, "entered function");
699 
700  /* Must be able to build box for each argument (ie, not empty geometry). */
701  if ( (gserialized_datum_get_box2df_p(gs1, &b1) == LW_SUCCESS) &&
703  {
704  double distance = box2df_distance(&b1, &b2);
705  POSTGIS_DEBUGF(3, "got boxes %s and %s", box2df_to_string(&b1), box2df_to_string(&b2));
706  PG_RETURN_FLOAT8(distance);
707  }
708  PG_RETURN_FLOAT8(FLT_MAX);
709 }
710 
712 Datum gserialized_same_2d(PG_FUNCTION_ARGS)
713 {
714  if ( gserialized_datum_predicate_2d(PG_GETARG_DATUM(0), PG_GETARG_DATUM(1), box2df_equals) == LW_TRUE )
715  PG_RETURN_BOOL(true);
716 
717  PG_RETURN_BOOL(false);
718 }
719 
721 Datum gserialized_within_2d(PG_FUNCTION_ARGS)
722 {
723  POSTGIS_DEBUG(3, "entered function");
724  if ( gserialized_datum_predicate_2d(PG_GETARG_DATUM(0), PG_GETARG_DATUM(1), box2df_within) == LW_TRUE )
725  PG_RETURN_BOOL(true);
726 
727  PG_RETURN_BOOL(false);
728 }
729 
731 Datum gserialized_contains_2d(PG_FUNCTION_ARGS)
732 {
733  POSTGIS_DEBUG(3, "entered function");
734  if ( gserialized_datum_predicate_2d(PG_GETARG_DATUM(0), PG_GETARG_DATUM(1), box2df_contains) == LW_TRUE )
735  PG_RETURN_BOOL(true);
736 
737  PG_RETURN_BOOL(false);
738 }
739 
741 Datum gserialized_overlaps_2d(PG_FUNCTION_ARGS)
742 {
743  if ( gserialized_datum_predicate_2d(PG_GETARG_DATUM(0), PG_GETARG_DATUM(1), box2df_overlaps) == LW_TRUE )
744  PG_RETURN_BOOL(true);
745 
746  PG_RETURN_BOOL(false);
747 }
748 
750 Datum gserialized_left_2d(PG_FUNCTION_ARGS)
751 {
752  if ( gserialized_datum_predicate_2d(PG_GETARG_DATUM(0), PG_GETARG_DATUM(1), box2df_left) == LW_TRUE )
753  PG_RETURN_BOOL(true);
754 
755  PG_RETURN_BOOL(false);
756 }
757 
759 Datum gserialized_right_2d(PG_FUNCTION_ARGS)
760 {
761  if ( gserialized_datum_predicate_2d(PG_GETARG_DATUM(0), PG_GETARG_DATUM(1), box2df_right) == LW_TRUE )
762  PG_RETURN_BOOL(true);
763 
764  PG_RETURN_BOOL(false);
765 }
766 
768 Datum gserialized_above_2d(PG_FUNCTION_ARGS)
769 {
770  if ( gserialized_datum_predicate_2d(PG_GETARG_DATUM(0), PG_GETARG_DATUM(1), box2df_above) == LW_TRUE )
771  PG_RETURN_BOOL(true);
772 
773  PG_RETURN_BOOL(false);
774 }
775 
777 Datum gserialized_below_2d(PG_FUNCTION_ARGS)
778 {
779  if ( gserialized_datum_predicate_2d(PG_GETARG_DATUM(0), PG_GETARG_DATUM(1), box2df_below) == LW_TRUE )
780  PG_RETURN_BOOL(true);
781 
782  PG_RETURN_BOOL(false);
783 }
784 
786 Datum gserialized_overleft_2d(PG_FUNCTION_ARGS)
787 {
788  if ( gserialized_datum_predicate_2d(PG_GETARG_DATUM(0), PG_GETARG_DATUM(1), box2df_overleft) == LW_TRUE )
789  PG_RETURN_BOOL(true);
790 
791  PG_RETURN_BOOL(false);
792 }
793 
795 Datum gserialized_overright_2d(PG_FUNCTION_ARGS)
796 {
797  if ( gserialized_datum_predicate_2d(PG_GETARG_DATUM(0), PG_GETARG_DATUM(1), box2df_overright) == LW_TRUE )
798  PG_RETURN_BOOL(true);
799 
800  PG_RETURN_BOOL(false);
801 }
802 
804 Datum gserialized_overabove_2d(PG_FUNCTION_ARGS)
805 {
806  if ( gserialized_datum_predicate_2d(PG_GETARG_DATUM(0), PG_GETARG_DATUM(1), box2df_overabove) == LW_TRUE )
807  PG_RETURN_BOOL(true);
808 
809  PG_RETURN_BOOL(false);
810 }
811 
813 Datum gserialized_overbelow_2d(PG_FUNCTION_ARGS)
814 {
815  if ( gserialized_datum_predicate_2d(PG_GETARG_DATUM(0), PG_GETARG_DATUM(1), box2df_overbelow) == LW_TRUE )
816  PG_RETURN_BOOL(true);
817 
818  PG_RETURN_BOOL(false);
819 }
820 
821 
822 /***********************************************************************
823 * GiST Index Support Functions
824 */
825 
826 /*
827 ** GiST support function. Given a geography, return a "compressed"
828 ** version. In this case, we convert the geography into a geocentric
829 ** bounding box. If the geography already has the box embedded in it
830 ** we pull that out and hand it back.
831 */
833 Datum gserialized_gist_compress_2d(PG_FUNCTION_ARGS)
834 {
835  GISTENTRY *entry_in = (GISTENTRY*)PG_GETARG_POINTER(0);
836  GISTENTRY *entry_out = NULL;
837  BOX2DF bbox_out;
838  int result = LW_SUCCESS;
839 
840  POSTGIS_DEBUG(4, "[GIST] 'compress' function called");
841 
842  /*
843  ** Not a leaf key? There's nothing to do.
844  ** Return the input unchanged.
845  */
846  if ( ! entry_in->leafkey )
847  {
848  POSTGIS_DEBUG(4, "[GIST] non-leafkey entry, returning input unaltered");
849  PG_RETURN_POINTER(entry_in);
850  }
851 
852  POSTGIS_DEBUG(4, "[GIST] processing leafkey input");
853  entry_out = palloc(sizeof(GISTENTRY));
854 
855  /*
856  ** Null key? Make a copy of the input entry and
857  ** return.
858  */
859  if ( DatumGetPointer(entry_in->key) == NULL )
860  {
861  POSTGIS_DEBUG(4, "[GIST] leafkey is null");
862  gistentryinit(*entry_out, (Datum) 0, entry_in->rel,
863  entry_in->page, entry_in->offset, false);
864  POSTGIS_DEBUG(4, "[GIST] returning copy of input");
865  PG_RETURN_POINTER(entry_out);
866  }
867 
868  /* Extract our index key from the GiST entry. */
869  result = gserialized_datum_get_box2df_p(entry_in->key, &bbox_out);
870 
871  /* Is the bounding box valid (non-empty, non-infinite)? If not, return input uncompressed. */
872  if ( result == LW_FAILURE )
873  {
874  box2df_set_empty(&bbox_out);
875  gistentryinit(*entry_out, PointerGetDatum(box2df_copy(&bbox_out)),
876  entry_in->rel, entry_in->page, entry_in->offset, false);
877 
878  POSTGIS_DEBUG(4, "[GIST] empty geometry!");
879  PG_RETURN_POINTER(entry_out);
880  }
881 
882  POSTGIS_DEBUGF(4, "[GIST] got entry_in->key: %s", box2df_to_string(&bbox_out));
883 
884  /* Check all the dimensions for finite values */
885  if ( ! isfinite(bbox_out.xmax) || ! isfinite(bbox_out.xmin) ||
886  ! isfinite(bbox_out.ymax) || ! isfinite(bbox_out.ymin) )
887  {
888  box2df_set_finite(&bbox_out);
889  gistentryinit(*entry_out, PointerGetDatum(box2df_copy(&bbox_out)),
890  entry_in->rel, entry_in->page, entry_in->offset, false);
891 
892  POSTGIS_DEBUG(4, "[GIST] infinite geometry!");
893  PG_RETURN_POINTER(entry_out);
894  }
895 
896  /* Enure bounding box has minimums below maximums. */
897  box2df_validate(&bbox_out);
898 
899  /* Prepare GISTENTRY for return. */
900  gistentryinit(*entry_out, PointerGetDatum(box2df_copy(&bbox_out)),
901  entry_in->rel, entry_in->page, entry_in->offset, false);
902 
903  /* Return GISTENTRY. */
904  POSTGIS_DEBUG(4, "[GIST] 'compress' function complete");
905  PG_RETURN_POINTER(entry_out);
906 }
907 
908 
909 /*
910 ** GiST support function.
911 ** Decompress an entry. Unused for geography, so we return.
912 */
914 Datum gserialized_gist_decompress_2d(PG_FUNCTION_ARGS)
915 {
916  POSTGIS_DEBUG(5, "[GIST] 'decompress' function called");
917  /* We don't decompress. Just return the input. */
918  PG_RETURN_POINTER(PG_GETARG_POINTER(0));
919 }
920 
921 
922 
923 /*
924 ** GiST support function. Called from gserialized_gist_consistent below.
925 */
926 static inline bool gserialized_gist_consistent_leaf_2d(BOX2DF *key, BOX2DF *query, StrategyNumber strategy)
927 {
928  bool retval;
929 
930  POSTGIS_DEBUGF(4, "[GIST] leaf consistent, strategy [%d], count[%i]",
931  strategy, g2d_counter_leaf++);
932 
933  switch (strategy)
934  {
935 
936  /* Basic overlaps */
937  case RTOverlapStrategyNumber:
938  retval = (bool) box2df_overlaps(key, query);
939  break;
940  case RTSameStrategyNumber:
941  retval = (bool) box2df_equals(key, query);
942  break;
943  case RTContainsStrategyNumber:
944  case RTOldContainsStrategyNumber:
945  retval = (bool) box2df_contains(key, query);
946  break;
947  case RTContainedByStrategyNumber:
948  case RTOldContainedByStrategyNumber:
949  retval = (bool) box2df_within(key, query);
950  break;
951 
952  /* To one side */
953  case RTAboveStrategyNumber:
954  retval = (bool) box2df_above(key, query);
955  break;
956  case RTBelowStrategyNumber:
957  retval = (bool) box2df_below(key, query);
958  break;
959  case RTRightStrategyNumber:
960  retval = (bool) box2df_right(key, query);
961  break;
962  case RTLeftStrategyNumber:
963  retval = (bool) box2df_left(key, query);
964  break;
965 
966  /* Overlapping to one side */
967  case RTOverAboveStrategyNumber:
968  retval = (bool) box2df_overabove(key, query);
969  break;
970  case RTOverBelowStrategyNumber:
971  retval = (bool) box2df_overbelow(key, query);
972  break;
973  case RTOverRightStrategyNumber:
974  retval = (bool) box2df_overright(key, query);
975  break;
976  case RTOverLeftStrategyNumber:
977  retval = (bool) box2df_overleft(key, query);
978  break;
979 
980  default:
981  retval = false;
982  }
983 
984  return (retval);
985 }
986 
987 /*
988 ** GiST support function. Called from gserialized_gist_consistent below.
989 */
990 static inline bool gserialized_gist_consistent_internal_2d(BOX2DF *key, BOX2DF *query, StrategyNumber strategy)
991 {
992  bool retval;
993 
994  POSTGIS_DEBUGF(4, "[GIST] internal consistent, strategy [%d], count[%i], query[%s], key[%s]",
995  strategy, g2d_counter_internal++, box2df_to_string(query), box2df_to_string(key) );
996 
997  switch (strategy)
998  {
999 
1000  /* Basic overlaps */
1001  case RTOverlapStrategyNumber:
1002  retval = (bool) box2df_overlaps(key, query);
1003  break;
1004  case RTSameStrategyNumber:
1005  case RTContainsStrategyNumber:
1006  case RTOldContainsStrategyNumber:
1007  retval = (bool) box2df_contains(key, query);
1008  break;
1009  case RTContainedByStrategyNumber:
1010  case RTOldContainedByStrategyNumber:
1011  retval = (bool) box2df_overlaps(key, query);
1012  break;
1013 
1014  /* To one side */
1015  case RTAboveStrategyNumber:
1016  retval = (bool)(!box2df_overbelow(key, query));
1017  break;
1018  case RTBelowStrategyNumber:
1019  retval = (bool)(!box2df_overabove(key, query));
1020  break;
1021  case RTRightStrategyNumber:
1022  retval = (bool)(!box2df_overleft(key, query));
1023  break;
1024  case RTLeftStrategyNumber:
1025  retval = (bool)(!box2df_overright(key, query));
1026  break;
1027 
1028  /* Overlapping to one side */
1029  case RTOverAboveStrategyNumber:
1030  retval = (bool)(!box2df_below(key, query));
1031  break;
1032  case RTOverBelowStrategyNumber:
1033  retval = (bool)(!box2df_above(key, query));
1034  break;
1035  case RTOverRightStrategyNumber:
1036  retval = (bool)(!box2df_left(key, query));
1037  break;
1038  case RTOverLeftStrategyNumber:
1039  retval = (bool)(!box2df_right(key, query));
1040  break;
1041 
1042  default:
1043  retval = false;
1044  }
1045 
1046  return (retval);
1047 }
1048 
1049 /*
1050 ** GiST support function. Take in a query and an entry and see what the
1051 ** relationship is, based on the query strategy.
1052 */
1054 Datum gserialized_gist_consistent_2d(PG_FUNCTION_ARGS)
1055 {
1056  GISTENTRY *entry = (GISTENTRY*) PG_GETARG_POINTER(0);
1057  StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2);
1058  bool result;
1059  BOX2DF query_gbox_index;
1060 
1061  /* PostgreSQL 8.4 and later require the RECHECK flag to be set here,
1062  rather than being supplied as part of the operator class definition */
1063  bool *recheck = (bool *) PG_GETARG_POINTER(4);
1064 
1065  /* We set recheck to false to avoid repeatedly pulling every "possibly matched" geometry
1066  out during index scans. For cases when the geometries are large, rechecking
1067  can make things twice as slow. */
1068  *recheck = false;
1069 
1070  POSTGIS_DEBUG(4, "[GIST] 'consistent' function called");
1071 
1072  /* Quick sanity check on query argument. */
1073  if ( DatumGetPointer(PG_GETARG_DATUM(1)) == NULL )
1074  {
1075  POSTGIS_DEBUG(4, "[GIST] null query pointer (!?!), returning false");
1076  PG_RETURN_BOOL(false); /* NULL query! This is screwy! */
1077  }
1078 
1079  /* Quick sanity check on entry key. */
1080  if ( DatumGetPointer(entry->key) == NULL )
1081  {
1082  POSTGIS_DEBUG(4, "[GIST] null index entry, returning false");
1083  PG_RETURN_BOOL(false); /* NULL entry! */
1084  }
1085 
1086  /* Null box should never make this far. */
1087  if ( gserialized_datum_get_box2df_p(PG_GETARG_DATUM(1), &query_gbox_index) == LW_FAILURE )
1088  {
1089  POSTGIS_DEBUG(4, "[GIST] null query_gbox_index!");
1090  PG_RETURN_BOOL(false);
1091  }
1092 
1093  /* Treat leaf node tests different from internal nodes */
1094  if (GIST_LEAF(entry))
1095  {
1097  (BOX2DF*)DatumGetPointer(entry->key),
1098  &query_gbox_index, strategy);
1099  }
1100  else
1101  {
1103  (BOX2DF*)DatumGetPointer(entry->key),
1104  &query_gbox_index, strategy);
1105  }
1106 
1107  PG_RETURN_BOOL(result);
1108 }
1109 
1110 
1111 /*
1112 ** GiST support function. Take in a query and an entry and return the "distance"
1113 ** between them.
1114 **
1115 ** Given an index entry p and a query value q, this function determines the
1116 ** index entry's "distance" from the query value. This function must be
1117 ** supplied if the operator class contains any ordering operators. A query
1118 ** using the ordering operator will be implemented by returning index entries
1119 ** with the smallest "distance" values first, so the results must be consistent
1120 ** with the operator's semantics. For a leaf index entry the result just
1121 ** represents the distance to the index entry; for an internal tree node, the
1122 ** result must be the smallest distance that any child entry could have.
1123 **
1124 ** Strategy 13 = true distance tests <->
1125 ** Strategy 14 = box-based distance tests <#>
1126 */
1128 Datum gserialized_gist_distance_2d(PG_FUNCTION_ARGS)
1129 {
1130  GISTENTRY *entry = (GISTENTRY*) PG_GETARG_POINTER(0);
1131  BOX2DF query_box;
1132  BOX2DF *entry_box;
1133  StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2);
1134  double distance;
1135  bool *recheck = (bool *) PG_GETARG_POINTER(4);
1136 
1137  POSTGIS_DEBUG(4, "[GIST] 'distance' function called");
1138 
1139  /* We are using '13' as the gist true-distance <-> strategy number
1140  * and '14' as the gist distance-between-boxes <#> strategy number */
1141  if ( strategy != 13 && strategy != 14 ) {
1142  elog(ERROR, "unrecognized strategy number: %d", strategy);
1143  PG_RETURN_FLOAT8(FLT_MAX);
1144  }
1145 
1146  /* Null box should never make this far. */
1147  if ( gserialized_datum_get_box2df_p(PG_GETARG_DATUM(1), &query_box) == LW_FAILURE )
1148  {
1149  POSTGIS_DEBUG(4, "[GIST] null query_gbox_index!");
1150  PG_RETURN_FLOAT8(FLT_MAX);
1151  }
1152 
1153  /* Get the entry box */
1154  entry_box = (BOX2DF*)DatumGetPointer(entry->key);
1155 
1156  /* Box-style distance test */
1157  if ( strategy == 14 ) /* operator <#> */
1158  {
1159  distance = box2df_distance(entry_box, &query_box);
1160  }
1161  /* True distance test (formerly centroid distance) */
1162  else if ( strategy == 13 ) /* operator <-> */
1163  {
1164  /* In all cases, since we only have keys (boxes) we'll return */
1165  /* the minimum possible distance, which is the box2df_distance */
1166  /* and let the recheck sort things out in the case of leaves */
1167  distance = box2df_distance(entry_box, &query_box);
1168 
1169  if (GIST_LEAF(entry))
1170  *recheck = true;
1171  }
1172  else
1173  {
1174  elog(ERROR, "%s: reached unreachable code", __func__);
1175  PG_RETURN_NULL();
1176  }
1177 
1178  PG_RETURN_FLOAT8(distance);
1179 }
1180 
1181 /*
1182 ** Function to pack floats of different realms.
1183 ** This function serves to pack bit flags inside float type.
1184 ** Result value represent can be from two different "realms".
1185 ** Every value from realm 1 is greater than any value from realm 0.
1186 ** Values from the same realm loose one bit of precision.
1187 **
1188 ** This technique is possible due to floating point numbers specification
1189 ** according to IEEE 754: exponent bits are highest
1190 ** (excluding sign bits, but here penalty is always positive). If float a is
1191 ** greater than float b, integer A with same bit representation as a is greater
1192 ** than integer B with same bits as b.
1193 */
1194 static inline float
1195 pack_float(const float value, const uint8_t realm)
1196 {
1197  union {
1198  float f;
1199  struct {
1200  unsigned value : 31, sign : 1;
1201  } vbits;
1202  struct {
1203  unsigned value : 30, realm : 1, sign : 1;
1204  } rbits;
1205  } a;
1206 
1207  a.f = value;
1208  a.rbits.value = a.vbits.value >> 1;
1209  a.rbits.realm = realm;
1210 
1211  return a.f;
1212 }
1213 
1214 static inline float
1215 box2df_penalty(const BOX2DF *b1, const BOX2DF *b2)
1216 {
1217  float b1xmin = b1->xmin, b1xmax = b1->xmax;
1218  float b1ymin = b1->ymin, b1ymax = b1->ymax;
1219  float b2xmin = b2->xmin, b2xmax = b2->xmax;
1220  float b2ymin = b2->ymin, b2ymax = b2->ymax;
1221 
1222  float box_union_xmin = Min(b1xmin, b2xmin), box_union_xmax = Max(b1xmax, b2xmax);
1223  float box_union_ymin = Min(b1ymin, b2ymin), box_union_ymax = Max(b1ymax, b2ymax);
1224 
1225  float b1dx = b1xmax - b1xmin, b1dy = b1ymax - b1ymin;
1226  float box_union_dx = box_union_xmax - box_union_xmin, box_union_dy = box_union_ymax - box_union_ymin;
1227 
1228  float box_union_area = box_union_dx * box_union_dy, box1area = b1dx * b1dy;
1229  float box_union_edge = box_union_dx + box_union_dy, box1edge = b1dx + b1dy;
1230 
1231  float area_extension = box_union_area - box1area;
1232  float edge_extension = box_union_edge - box1edge;
1233 
1234  /* REALM 1: Area extension is nonzero, return it */
1235  if (area_extension > FLT_EPSILON)
1236  return pack_float(area_extension, 1);
1237  /* REALM 0: Area extension is zero, return nonzero edge extension */
1238  else if (edge_extension > FLT_EPSILON)
1239  return pack_float(edge_extension, 0);
1240 
1241  return 0;
1242 }
1243 
1244 /*
1245 ** GiST support function. Calculate the "penalty" cost of adding this entry into an existing entry.
1246 ** Calculate the change in volume of the old entry once the new entry is added.
1247 */
1249 Datum gserialized_gist_penalty_2d(PG_FUNCTION_ARGS)
1250 {
1251  GISTENTRY *origentry = (GISTENTRY*) PG_GETARG_POINTER(0);
1252  GISTENTRY *newentry = (GISTENTRY*) PG_GETARG_POINTER(1);
1253  float *result = (float*) PG_GETARG_POINTER(2);
1254  BOX2DF *b1, *b2;
1255 
1256  b1 = (BOX2DF *)DatumGetPointer(origentry->key);
1257  b2 = (BOX2DF *)DatumGetPointer(newentry->key);
1258 
1259  /* Penalty value of 0 has special code path in Postgres's gistchoose.
1260  * It is used as an early exit condition in subtree loop, allowing faster tree descend.
1261  * For multi-column index, it lets next column break the tie, possibly more confidently.
1262  */
1263  *result = 0;
1264 
1265  if (b1 && b2 && !box2df_is_empty(b1) && !box2df_is_empty(b2))
1266  *result = box2df_penalty(b1, b2);
1267 
1268  PG_RETURN_POINTER(result);
1269 }
1270 
1271 /*
1272 ** GiST support function. Merge all the boxes in a page into one master box.
1273 */
1275 Datum gserialized_gist_union_2d(PG_FUNCTION_ARGS)
1276 {
1277  GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
1278  int *sizep = (int *) PG_GETARG_POINTER(1); /* Size of the return value */
1279  int numranges, i;
1280  BOX2DF *box_cur, *box_union;
1281 
1282  POSTGIS_DEBUG(4, "[GIST] 'union' function called");
1283 
1284  numranges = entryvec->n;
1285 
1286  box_cur = (BOX2DF*) DatumGetPointer(entryvec->vector[0].key);
1287 
1288  box_union = box2df_copy(box_cur);
1289 
1290  for ( i = 1; i < numranges; i++ )
1291  {
1292  box_cur = (BOX2DF*) DatumGetPointer(entryvec->vector[i].key);
1293  box2df_merge(box_union, box_cur);
1294  }
1295 
1296  *sizep = sizeof(BOX2DF);
1297 
1298  POSTGIS_DEBUGF(4, "[GIST] 'union', numranges(%i), pageunion %s", numranges, box2df_to_string(box_union));
1299 
1300  PG_RETURN_POINTER(box_union);
1301 
1302 }
1303 
1304 /*
1305 ** GiST support function. Test equality of keys.
1306 */
1308 Datum gserialized_gist_same_2d(PG_FUNCTION_ARGS)
1309 {
1310  BOX2DF *b1 = (BOX2DF*)PG_GETARG_POINTER(0);
1311  BOX2DF *b2 = (BOX2DF*)PG_GETARG_POINTER(1);
1312  bool *result = (bool*)PG_GETARG_POINTER(2);
1313 
1314  POSTGIS_DEBUG(4, "[GIST] 'same' function called");
1315 
1316  *result = box2df_equals(b1, b2);
1317 
1318  PG_RETURN_POINTER(result);
1319 }
1320 
1321 static int
1322 gserialized_gist_cmp_abbrev_2d(Datum x, Datum y, SortSupport ssup)
1323 {
1324  /* Empty is a special case */
1325  if (x == 0 || y == 0 || x == y)
1326  return 0; /* 0 means "ask bigger comparator" and not equality*/
1327  else if (x > y)
1328  return 1;
1329  else
1330  return -1;
1331 }
1332 
1333 static bool
1334 gserialized_gist_abbrev_abort_2d(int memtupcount, SortSupport ssup)
1335 {
1336  return LW_FALSE;
1337 }
1338 
1339 static Datum
1340 gserialized_gist_abbrev_convert_2d(Datum original, SortSupport ssup)
1341 {
1342  return box2df_get_sortable_hash((BOX2DF *)original);
1343 }
1344 
1345 static int
1346 gserialized_gist_cmp_full_2d(Datum a, Datum b, SortSupport ssup)
1347 {
1348  BOX2DF *b1 = (BOX2DF *)a;
1349  BOX2DF *b2 = (BOX2DF *)b;
1350  uint64_t hash1, hash2;
1351  int cmp;
1352 
1353  cmp = memcmp(b1, b2, sizeof(BOX2DF));
1354  if (cmp == 0)
1355  return 0;
1356 
1357  hash1 = box2df_get_sortable_hash(b1);
1358  hash2 = box2df_get_sortable_hash(b2);
1359  if (hash1 > hash2)
1360  return 1;
1361  else if (hash1 < hash2)
1362  return -1;
1363 
1364  return cmp > 0 ? 1 : -1;
1365 }
1366 
1368 Datum gserialized_gist_sortsupport_2d(PG_FUNCTION_ARGS)
1369 {
1370  SortSupport ssup = (SortSupport)PG_GETARG_POINTER(0);
1371 
1372  ssup->comparator = gserialized_gist_cmp_full_2d;
1373  ssup->ssup_extra = NULL;
1374  /* Enable sortsupport only on 64 bit Datum */
1375  if (ssup->abbreviate && sizeof(Datum) == 8)
1376  {
1377  ssup->comparator = gserialized_gist_cmp_abbrev_2d;
1378  ssup->abbrev_converter = gserialized_gist_abbrev_convert_2d;
1379  ssup->abbrev_abort = gserialized_gist_abbrev_abort_2d;
1380  ssup->abbrev_full_comparator = gserialized_gist_cmp_full_2d;
1381  }
1382 
1383  PG_RETURN_VOID();
1384 }
1385 
1386 /*
1387  * Adjust BOX2DF b boundaries with insertion of addon.
1388  */
1389 static void
1390 adjustBox(BOX2DF *b, BOX2DF *addon)
1391 {
1392  if (b->xmax < addon->xmax || isnan(b->xmax))
1393  b->xmax = addon->xmax;
1394  if (b->xmin > addon->xmin || isnan(b->xmin))
1395  b->xmin = addon->xmin;
1396  if (b->ymax < addon->ymax || isnan(b->ymax))
1397  b->ymax = addon->ymax;
1398  if (b->ymin > addon->ymin || isnan(b->ymin))
1399  b->ymin = addon->ymin;
1400 }
1401 
1402 /*
1403  * Trivial split: half of entries will be placed on one page
1404  * and another half - to another
1405  */
1406 static void
1407 fallbackSplit(GistEntryVector *entryvec, GIST_SPLITVEC *v)
1408 {
1409  OffsetNumber i,
1410  maxoff;
1411  BOX2DF *unionL = NULL,
1412  *unionR = NULL;
1413  int nbytes;
1414 
1415  maxoff = entryvec->n - 1;
1416 
1417  nbytes = (maxoff + 2) * sizeof(OffsetNumber);
1418  v->spl_left = (OffsetNumber *) palloc(nbytes);
1419  v->spl_right = (OffsetNumber *) palloc(nbytes);
1420  v->spl_nleft = v->spl_nright = 0;
1421 
1422  for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
1423  {
1424  BOX2DF *cur = (BOX2DF *) DatumGetPointer(entryvec->vector[i].key);
1425 
1426  if (i <= (maxoff - FirstOffsetNumber + 1) / 2)
1427  {
1428  v->spl_left[v->spl_nleft] = i;
1429  if (unionL == NULL)
1430  {
1431  unionL = (BOX2DF *) palloc(sizeof(BOX2DF));
1432  *unionL = *cur;
1433  }
1434  else
1435  adjustBox(unionL, cur);
1436 
1437  v->spl_nleft++;
1438  }
1439  else
1440  {
1441  v->spl_right[v->spl_nright] = i;
1442  if (unionR == NULL)
1443  {
1444  unionR = (BOX2DF *) palloc(sizeof(BOX2DF));
1445  *unionR = *cur;
1446  }
1447  else
1448  adjustBox(unionR, cur);
1449 
1450  v->spl_nright++;
1451  }
1452  }
1453 
1454  if (v->spl_ldatum_exists)
1455  adjustBox(unionL, (BOX2DF *) DatumGetPointer(v->spl_ldatum));
1456  v->spl_ldatum = PointerGetDatum(unionL);
1457 
1458  if (v->spl_rdatum_exists)
1459  adjustBox(unionR, (BOX2DF *) DatumGetPointer(v->spl_rdatum));
1460  v->spl_rdatum = PointerGetDatum(unionR);
1461 
1462  v->spl_ldatum_exists = v->spl_rdatum_exists = false;
1463 }
1464 
1465 /*
1466  * Represents information about an entry that can be placed to either group
1467  * without affecting overlap over selected axis ("common entry").
1468  */
1469 typedef struct
1470 {
1471  /* Index of entry in the initial array */
1472  int index;
1473  /* Delta between penalties of entry insertion into different groups */
1474  float delta;
1475 } CommonEntry;
1476 
1477 /*
1478  * Context for g_box_consider_split. Contains information about currently
1479  * selected split and some general information.
1480  */
1481 typedef struct
1482 {
1483  int entriesCount; /* total number of entries being split */
1484  BOX2DF boundingBox; /* minimum bounding box across all entries */
1485 
1486  /* Information about currently selected split follows */
1487 
1488  bool first; /* true if no split was selected yet */
1489 
1490  float leftUpper; /* upper bound of left interval */
1491  float rightLower; /* lower bound of right interval */
1492 
1493  float4 ratio;
1494  float4 overlap;
1495  int dim; /* axis of this split */
1496  float range; /* width of general MBR projection to the
1497  * selected axis */
1499 
1500 /*
1501  * Interval represents projection of box to axis.
1502  */
1503 typedef struct
1504 {
1505  float lower,
1507 } SplitInterval;
1508 
1509 /*
1510  * Interval comparison function by lower bound of the interval;
1511  */
1512 static int
1513 interval_cmp_lower(const void *i1, const void *i2)
1514 {
1515  float lower1 = ((const SplitInterval *) i1)->lower,
1516  lower2 = ((const SplitInterval *) i2)->lower;
1517 
1518  if (isnan(lower1))
1519  {
1520  if (isnan(lower2))
1521  return 0;
1522  else
1523  return 1;
1524  }
1525  else if (isnan(lower2))
1526  {
1527  return -1;
1528  }
1529  else
1530  {
1531  if (lower1 < lower2)
1532  return -1;
1533  else if (lower1 > lower2)
1534  return 1;
1535  else
1536  return 0;
1537  }
1538 }
1539 
1540 
1541 /*
1542  * Interval comparison function by upper bound of the interval;
1543  */
1544 static int
1545 interval_cmp_upper(const void *i1, const void *i2)
1546 {
1547  float upper1 = ((const SplitInterval *) i1)->upper,
1548  upper2 = ((const SplitInterval *) i2)->upper;
1549 
1550  if (isnan(upper1))
1551  {
1552  if (isnan(upper2))
1553  return 0;
1554  else
1555  return -1;
1556  }
1557  else if (isnan(upper2))
1558  {
1559  return 1;
1560  }
1561  else
1562  {
1563  if (upper1 < upper2)
1564  return -1;
1565  else if (upper1 > upper2)
1566  return 1;
1567  else
1568  return 0;
1569  }
1570 }
1571 
1572 /*
1573  * Replace negative value with zero.
1574  */
1575 static inline float
1576 non_negative(float val)
1577 {
1578  if (val >= 0.0f)
1579  return val;
1580  else
1581  return 0.0f;
1582 }
1583 
1584 /*
1585  * Consider replacement of currently selected split with the better one.
1586  */
1587 static inline void
1589  float rightLower, int minLeftCount,
1590  float leftUpper, int maxLeftCount)
1591 {
1592  int leftCount,
1593  rightCount;
1594  float4 ratio,
1595  overlap;
1596  float range;
1597 
1598  POSTGIS_DEBUGF(5, "consider split: dimNum = %d, rightLower = %f, "
1599  "minLeftCount = %d, leftUpper = %f, maxLeftCount = %d ",
1600  dimNum, rightLower, minLeftCount, leftUpper, maxLeftCount);
1601 
1602  /*
1603  * Calculate entries distribution ratio assuming most uniform distribution
1604  * of common entries.
1605  */
1606  if (minLeftCount >= (context->entriesCount + 1) / 2)
1607  {
1608  leftCount = minLeftCount;
1609  }
1610  else
1611  {
1612  if (maxLeftCount <= context->entriesCount / 2)
1613  leftCount = maxLeftCount;
1614  else
1615  leftCount = context->entriesCount / 2;
1616  }
1617  rightCount = context->entriesCount - leftCount;
1618 
1619  /*
1620  * Ratio of split - quotient between size of lesser group and total
1621  * entries count.
1622  */
1623  ratio = ((float4) Min(leftCount, rightCount)) /
1624  ((float4) context->entriesCount);
1625 
1626  if (ratio > LIMIT_RATIO)
1627  {
1628  bool selectthis = false;
1629 
1630  /*
1631  * The ratio is acceptable, so compare current split with previously
1632  * selected one. Between splits of one dimension we search for minimal
1633  * overlap (allowing negative values) and minimal ration (between same
1634  * overlaps. We switch dimension if find less overlap (non-negative)
1635  * or less range with same overlap.
1636  */
1637  if (dimNum == 0)
1638  range = context->boundingBox.xmax - context->boundingBox.xmin;
1639  else
1640  range = context->boundingBox.ymax - context->boundingBox.ymin;
1641 
1642  overlap = (leftUpper - rightLower) / range;
1643 
1644  /* If there is no previous selection, select this */
1645  if (context->first)
1646  selectthis = true;
1647  else if (context->dim == dimNum)
1648  {
1649  /*
1650  * Within the same dimension, choose the new split if it has a
1651  * smaller overlap, or same overlap but better ratio.
1652  */
1653  if (overlap < context->overlap ||
1654  (overlap == context->overlap && ratio > context->ratio))
1655  selectthis = true;
1656  }
1657  else
1658  {
1659  /*
1660  * Across dimensions, choose the new split if it has a smaller
1661  * *non-negative* overlap, or same *non-negative* overlap but
1662  * bigger range. This condition differs from the one described in
1663  * the article. On the datasets where leaf MBRs don't overlap
1664  * themselves, non-overlapping splits (i.e. splits which have zero
1665  * *non-negative* overlap) are frequently possible. In this case
1666  * splits tends to be along one dimension, because most distant
1667  * non-overlapping splits (i.e. having lowest negative overlap)
1668  * appears to be in the same dimension as in the previous split.
1669  * Therefore MBRs appear to be very prolonged along another
1670  * dimension, which leads to bad search performance. Using range
1671  * as the second split criteria makes MBRs more quadratic. Using
1672  * *non-negative* overlap instead of overlap as the first split
1673  * criteria gives to range criteria a chance to matter, because
1674  * non-overlapping splits are equivalent in this criteria.
1675  */
1676  if (non_negative(overlap) < non_negative(context->overlap) ||
1677  (range > context->range &&
1678  non_negative(overlap) <= non_negative(context->overlap)))
1679  selectthis = true;
1680  }
1681 
1682  if (selectthis)
1683  {
1684  /* save information about selected split */
1685  context->first = false;
1686  context->ratio = ratio;
1687  context->range = range;
1688  context->overlap = overlap;
1689  context->rightLower = rightLower;
1690  context->leftUpper = leftUpper;
1691  context->dim = dimNum;
1692  POSTGIS_DEBUG(5, "split selected");
1693  }
1694  }
1695 }
1696 
1697 /*
1698  * Compare common entries by their deltas.
1699  */
1700 static int
1701 common_entry_cmp(const void *i1, const void *i2)
1702 {
1703  float delta1 = ((const CommonEntry *) i1)->delta,
1704  delta2 = ((const CommonEntry *) i2)->delta;
1705 
1706  if (delta1 < delta2)
1707  return -1;
1708  else if (delta1 > delta2)
1709  return 1;
1710  else
1711  return 0;
1712 }
1713 
1714 /*
1715  * --------------------------------------------------------------------------
1716  * Double sorting split algorithm. This is used for both boxes and points.
1717  *
1718  * The algorithm finds split of boxes by considering splits along each axis.
1719  * Each entry is first projected as an interval on the X-axis, and different
1720  * ways to split the intervals into two groups are considered, trying to
1721  * minimize the overlap of the groups. Then the same is repeated for the
1722  * Y-axis, and the overall best split is chosen. The quality of a split is
1723  * determined by overlap along that axis and some other criteria (see
1724  * g_box_consider_split).
1725  *
1726  * After that, all the entries are divided into three groups:
1727  *
1728  * 1) Entries which should be placed to the left group
1729  * 2) Entries which should be placed to the right group
1730  * 3) "Common entries" which can be placed to any of groups without affecting
1731  * of overlap along selected axis.
1732  *
1733  * The common entries are distributed by minimizing penalty.
1734  *
1735  * For details see:
1736  * "A new double sorting-based node splitting algorithm for R-tree", A. Korotkov
1737  * http://syrcose.ispras.ru/2011/files/SYRCoSE2011_Proceedings.pdf#page=36
1738  * --------------------------------------------------------------------------
1739  */
1741 Datum gserialized_gist_picksplit_2d(PG_FUNCTION_ARGS)
1742 {
1743  GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
1744  GIST_SPLITVEC *v = (GIST_SPLITVEC *) PG_GETARG_POINTER(1);
1745  OffsetNumber i,
1746  maxoff;
1747  ConsiderSplitContext context;
1748  BOX2DF *box,
1749  *leftBox,
1750  *rightBox;
1751  int dim,
1752  commonEntriesCount;
1753  SplitInterval *intervalsLower,
1754  *intervalsUpper;
1755  CommonEntry *commonEntries;
1756  int nentries;
1757 
1758  POSTGIS_DEBUG(3, "[GIST] 'picksplit' entered");
1759 
1760  memset(&context, 0, sizeof(ConsiderSplitContext));
1761 
1762  maxoff = entryvec->n - 1;
1763  nentries = context.entriesCount = maxoff - FirstOffsetNumber + 1;
1764 
1765  /* Allocate arrays for intervals along axes */
1766  intervalsLower = (SplitInterval *) palloc(nentries * sizeof(SplitInterval));
1767  intervalsUpper = (SplitInterval *) palloc(nentries * sizeof(SplitInterval));
1768 
1769  /*
1770  * Calculate the overall minimum bounding box over all the entries.
1771  */
1772  for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
1773  {
1774  box = (BOX2DF *) DatumGetPointer(entryvec->vector[i].key);
1775  if (i == FirstOffsetNumber)
1776  context.boundingBox = *box;
1777  else
1778  adjustBox(&context.boundingBox, box);
1779  }
1780 
1781  POSTGIS_DEBUGF(4, "boundingBox is %s", box2df_to_string(
1782  &context.boundingBox));
1783 
1784  /*
1785  * Iterate over axes for optimal split searching.
1786  */
1787  context.first = true; /* nothing selected yet */
1788  for (dim = 0; dim < 2; dim++)
1789  {
1790  float leftUpper,
1791  rightLower;
1792  int i1,
1793  i2;
1794 
1795  /* Project each entry as an interval on the selected axis. */
1796  for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
1797  {
1798  box = (BOX2DF *) DatumGetPointer(entryvec->vector[i].key);
1799  if (dim == 0)
1800  {
1801  intervalsLower[i - FirstOffsetNumber].lower = box->xmin;
1802  intervalsLower[i - FirstOffsetNumber].upper = box->xmax;
1803  }
1804  else
1805  {
1806  intervalsLower[i - FirstOffsetNumber].lower = box->ymin;
1807  intervalsLower[i - FirstOffsetNumber].upper = box->ymax;
1808  }
1809  }
1810 
1811  /*
1812  * Make two arrays of intervals: one sorted by lower bound and another
1813  * sorted by upper bound.
1814  */
1815  memcpy(intervalsUpper, intervalsLower,
1816  sizeof(SplitInterval) * nentries);
1817  qsort(intervalsLower, nentries, sizeof(SplitInterval),
1819  qsort(intervalsUpper, nentries, sizeof(SplitInterval),
1821 
1822  /*----
1823  * The goal is to form a left and right interval, so that every entry
1824  * interval is contained by either left or right interval (or both).
1825  *
1826  * For example, with the intervals (0,1), (1,3), (2,3), (2,4):
1827  *
1828  * 0 1 2 3 4
1829  * +-+
1830  * +---+
1831  * +-+
1832  * +---+
1833  *
1834  * The left and right intervals are of the form (0,a) and (b,4).
1835  * We first consider splits where b is the lower bound of an entry.
1836  * We iterate through all entries, and for each b, calculate the
1837  * smallest possible a. Then we consider splits where a is the
1838  * upper bound of an entry, and for each a, calculate the greatest
1839  * possible b.
1840  *
1841  * In the above example, the first loop would consider splits:
1842  * b=0: (0,1)-(0,4)
1843  * b=1: (0,1)-(1,4)
1844  * b=2: (0,3)-(2,4)
1845  *
1846  * And the second loop:
1847  * a=1: (0,1)-(1,4)
1848  * a=3: (0,3)-(2,4)
1849  * a=4: (0,4)-(2,4)
1850  */
1851 
1852  /*
1853  * Iterate over lower bound of right group, finding smallest possible
1854  * upper bound of left group.
1855  */
1856  i1 = 0;
1857  i2 = 0;
1858  rightLower = intervalsLower[i1].lower;
1859  leftUpper = intervalsUpper[i2].lower;
1860  while (true)
1861  {
1862  /*
1863  * Find next lower bound of right group.
1864  */
1865  while (i1 < nentries && (rightLower == intervalsLower[i1].lower ||
1866  isnan(intervalsLower[i1].lower)))
1867  {
1868  leftUpper = Max(leftUpper, intervalsLower[i1].upper);
1869  i1++;
1870  }
1871  if (i1 >= nentries)
1872  break;
1873  rightLower = intervalsLower[i1].lower;
1874 
1875  /*
1876  * Find count of intervals which anyway should be placed to the
1877  * left group.
1878  */
1879  while (i2 < nentries && intervalsUpper[i2].upper <= leftUpper)
1880  i2++;
1881 
1882  /*
1883  * Consider found split.
1884  */
1885  g_box_consider_split(&context, dim, rightLower, i1, leftUpper, i2);
1886  }
1887 
1888  /*
1889  * Iterate over upper bound of left group finding greatest possible
1890  * lower bound of right group.
1891  */
1892  i1 = nentries - 1;
1893  i2 = nentries - 1;
1894  rightLower = intervalsLower[i1].upper;
1895  leftUpper = intervalsUpper[i2].upper;
1896  while (true)
1897  {
1898  /*
1899  * Find next upper bound of left group.
1900  */
1901  while (i2 >= 0 && (leftUpper == intervalsUpper[i2].upper ||
1902  isnan(intervalsUpper[i2].upper)))
1903  {
1904  rightLower = Min(rightLower, intervalsUpper[i2].lower);
1905  i2--;
1906  }
1907  if (i2 < 0)
1908  break;
1909  leftUpper = intervalsUpper[i2].upper;
1910 
1911  /*
1912  * Find count of intervals which anyway should be placed to the
1913  * right group.
1914  */
1915  while (i1 >= 0 && intervalsLower[i1].lower >= rightLower)
1916  i1--;
1917 
1918  /*
1919  * Consider found split.
1920  */
1921  g_box_consider_split(&context, dim,
1922  rightLower, i1 + 1, leftUpper, i2 + 1);
1923  }
1924  }
1925 
1926  /*
1927  * If we failed to find any acceptable splits, use trivial split.
1928  */
1929  if (context.first)
1930  {
1931  POSTGIS_DEBUG(4, "no acceptable splits, trivial split");
1932  fallbackSplit(entryvec, v);
1933  PG_RETURN_POINTER(v);
1934  }
1935 
1936  /*
1937  * Ok, we have now selected the split across one axis.
1938  *
1939  * While considering the splits, we already determined that there will be
1940  * enough entries in both groups to reach the desired ratio, but we did
1941  * not memorize which entries go to which group. So determine that now.
1942  */
1943 
1944  POSTGIS_DEBUGF(4, "split direction: %d", context.dim);
1945 
1946  /* Allocate vectors for results */
1947  v->spl_left = (OffsetNumber *) palloc(nentries * sizeof(OffsetNumber));
1948  v->spl_right = (OffsetNumber *) palloc(nentries * sizeof(OffsetNumber));
1949  v->spl_nleft = 0;
1950  v->spl_nright = 0;
1951 
1952  /* Allocate bounding boxes of left and right groups */
1953  leftBox = palloc0(sizeof(BOX2DF));
1954  rightBox = palloc0(sizeof(BOX2DF));
1955 
1956  /*
1957  * Allocate an array for "common entries" - entries which can be placed to
1958  * either group without affecting overlap along selected axis.
1959  */
1960  commonEntriesCount = 0;
1961  commonEntries = (CommonEntry *) palloc(nentries * sizeof(CommonEntry));
1962 
1963  /* Helper macros to place an entry in the left or right group */
1964 #define PLACE_LEFT(box, off) \
1965  do { \
1966  if (v->spl_nleft > 0) \
1967  adjustBox(leftBox, box); \
1968  else \
1969  *leftBox = *(box); \
1970  v->spl_left[v->spl_nleft++] = off; \
1971  } while(0)
1972 
1973 #define PLACE_RIGHT(box, off) \
1974  do { \
1975  if (v->spl_nright > 0) \
1976  adjustBox(rightBox, box); \
1977  else \
1978  *rightBox = *(box); \
1979  v->spl_right[v->spl_nright++] = off; \
1980  } while(0)
1981 
1982  /*
1983  * Distribute entries which can be distributed unambiguously, and collect
1984  * common entries.
1985  */
1986  for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
1987  {
1988  float lower,
1989  upper;
1990 
1991  /*
1992  * Get upper and lower bounds along selected axis.
1993  */
1994  box = (BOX2DF *) DatumGetPointer(entryvec->vector[i].key);
1995  if (context.dim == 0)
1996  {
1997  lower = box->xmin;
1998  upper = box->xmax;
1999  }
2000  else
2001  {
2002  lower = box->ymin;
2003  upper = box->ymax;
2004  }
2005 
2006  if (upper <= context.leftUpper || isnan(upper))
2007  {
2008  /* Fits to the left group */
2009  if (lower >= context.rightLower || isnan(lower))
2010  {
2011  /* Fits also to the right group, so "common entry" */
2012  commonEntries[commonEntriesCount++].index = i;
2013  }
2014  else
2015  {
2016  /* Doesn't fit to the right group, so join to the left group */
2017  PLACE_LEFT(box, i);
2018  }
2019  }
2020  else
2021  {
2022  /*
2023  * Each entry should fit on either left or right group. Since this
2024  * entry didn't fit on the left group, it better fit in the right
2025  * group.
2026  */
2027  Assert(lower >= context.rightLower);
2028 
2029  /* Doesn't fit to the left group, so join to the right group */
2030  PLACE_RIGHT(box, i);
2031  }
2032  }
2033 
2034  POSTGIS_DEBUGF(4, "leftBox is %s", box2df_to_string(leftBox));
2035  POSTGIS_DEBUGF(4, "rightBox is %s", box2df_to_string(rightBox));
2036 
2037  /*
2038  * Distribute "common entries", if any.
2039  */
2040  if (commonEntriesCount > 0)
2041  {
2042  /* Calculate minimum number of entries that must be placed in both groups, to reach LIMIT_RATIO. */
2043  int m = ceil(LIMIT_RATIO * (double)nentries);
2044 
2045  /* Recursive picksplit called, this is going to be the last split, keep split into 2 parts */
2046  if (nentries > INDEX_TUPLES_PER_PAGE && nentries <= 2 * INDEX_TUPLES_PER_PAGE)
2047  m = Max(m, nentries - INDEX_TUPLES_PER_PAGE);
2048 
2049  /*
2050  * Calculate delta between penalties of join "common entries" to
2051  * different groups.
2052  */
2053  for (i = 0; i < commonEntriesCount; i++)
2054  {
2055  box = (BOX2DF *) DatumGetPointer(entryvec->vector[
2056  commonEntries[i].index].key);
2058  commonEntries[i].delta = fabs(box2df_penalty(leftBox, box) - box2df_penalty(rightBox, box));
2059  }
2060 
2061  /*
2062  * Sort "common entries" by calculated deltas in order to distribute
2063  * the most ambiguous entries first.
2064  */
2065  qsort(commonEntries, commonEntriesCount, sizeof(CommonEntry), common_entry_cmp);
2066 
2067  /*
2068  * Distribute "common entries" between groups.
2069  */
2070  for (i = 0; i < commonEntriesCount; i++)
2071  {
2072  float right_penalty, left_penalty;
2073  bool place_right = true;
2074  box = (BOX2DF *)DatumGetPointer(entryvec->vector[commonEntries[i].index].key);
2075 
2076  /* Recheck penalties. After we put undecided tuples to some side they're changed */
2077  left_penalty = box2df_penalty(leftBox, box);
2078  right_penalty = box2df_penalty(rightBox, box);
2079 
2080  /* Check if penalty is absolutely telling a tuple should go to some side */
2081  if (right_penalty > 0 && left_penalty == 0)
2082  place_right = false;
2083  else if (right_penalty == 0 && left_penalty > 0)
2084  place_right = true;
2085  /* Check if we have to place this entry in either group to achieve LIMIT_RATIO */
2086  else if (v->spl_nleft + (commonEntriesCount - i) <= m)
2087  place_right = false;
2088  else if (v->spl_nright + (commonEntriesCount - i) <= m)
2089  place_right = true;
2090  /* Otherwise select the group by smaller penalty */
2091  else if (left_penalty < right_penalty)
2092  place_right = false;
2093  else if (right_penalty < left_penalty)
2094  place_right = true;
2095  /* or just put tuple into smaller group */
2096  else if (v->spl_nleft < v->spl_nright)
2097  place_right = false;
2098  else
2099  place_right = true;
2100 
2101  if (place_right)
2102  PLACE_RIGHT(box, commonEntries[i].index);
2103  else
2104  PLACE_LEFT(box, commonEntries[i].index);
2105  }
2106  }
2107  v->spl_ldatum = PointerGetDatum(leftBox);
2108  v->spl_rdatum = PointerGetDatum(rightBox);
2109 
2110  POSTGIS_DEBUG(4, "[GIST] 'picksplit' completed");
2111 
2112  PG_RETURN_POINTER(v);
2113 }
2114 
2115 /*
2116 ** The BOX2DF key must be defined as a PostgreSQL type, even though it is only
2117 ** ever used internally. These no-op stubs are used to bind the type.
2118 */
2120 Datum box2df_in(PG_FUNCTION_ARGS)
2121 {
2122  ereport(ERROR,(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2123  errmsg("function box2df_in not implemented")));
2124  PG_RETURN_POINTER(NULL);
2125 }
2126 
2128 Datum box2df_out(PG_FUNCTION_ARGS)
2129 {
2130  BOX2DF *box = (BOX2DF *) PG_GETARG_POINTER(0);
2131  char *result = box2df_to_string(box);
2132  PG_RETURN_CSTRING(result);
2133 }
static uint8_t precision
Definition: cu_in_twkb.c:25
char result[OUT_DOUBLE_BUFFER_SIZE]
Definition: cu_print.c:262
int gserialized_has_bbox(const GSERIALIZED *g)
Check if a GSERIALIZED has a bounding box without deserializing first.
Definition: gserialized.c:163
int32_t gserialized_get_srid(const GSERIALIZED *g)
Extract the SRID from the serialized form (it is packed into three bytes so this is a handy function)...
Definition: gserialized.c:126
int gserialized_get_gbox_p(const GSERIALIZED *g, GBOX *gbox)
Read the box from the GSERIALIZED or calculate it if necessary.
Definition: gserialized.c:65
const float * gserialized_get_float_box_p(const GSERIALIZED *g, size_t *ndims)
Access to the float bounding box, if there is one.
Definition: gserialized.c:248
uint32_t gserialized_get_type(const GSERIALIZED *g)
Extract the geometry type from the serialized form (it hides in the anonymous data area,...
Definition: gserialized.c:89
uint32_t gserialized_max_header_size(void)
Returns the size in bytes to read from toast to get the basic information from a geometry: GSERIALIZE...
Definition: gserialized.c:101
lwflags_t gserialized_get_lwflags(const GSERIALIZED *g)
Read the flags from a GSERIALIZED and return a standard lwflag integer.
Definition: gserialized.c:18
bool box2df_left(const BOX2DF *a, const BOX2DF *b)
static char * box2df_to_string(const BOX2DF *a)
void box2df_merge(BOX2DF *b_union, BOX2DF *b_new)
static void fallbackSplit(GistEntryVector *entryvec, GIST_SPLITVEC *v)
Datum gserialized_overbelow_2d(PG_FUNCTION_ARGS)
Datum gserialized_gist_sortsupport_2d(PG_FUNCTION_ARGS)
void box2df_set_empty(BOX2DF *a)
Datum gserialized_gist_same_2d(PG_FUNCTION_ARGS)
Datum gserialized_contains_box2df_geom_2d(PG_FUNCTION_ARGS)
bool box2df_is_empty(const BOX2DF *a)
static int gserialized_datum_predicate_2d(Datum gs1, Datum gs2, box2df_predicate predicate)
Support function.
Datum gserialized_within_2d(PG_FUNCTION_ARGS)
PG_FUNCTION_INFO_V1(gserialized_contains_box2df_geom_2d)
Datum box2df_out(PG_FUNCTION_ARGS)
static Datum gserialized_gist_abbrev_convert_2d(Datum original, SortSupport ssup)
Datum gserialized_within_box2df_geom_2d(PG_FUNCTION_ARGS)
Datum gserialized_gist_penalty_2d(PG_FUNCTION_ARGS)
Datum gserialized_left_2d(PG_FUNCTION_ARGS)
Datum gserialized_contains_2d(PG_FUNCTION_ARGS)
#define INDEX_TUPLES_PER_PAGE
Datum gserialized_gist_consistent_2d(PG_FUNCTION_ARGS)
static int interval_cmp_upper(const void *i1, const void *i2)
Datum gserialized_distance_box_2d(PG_FUNCTION_ARGS)
bool box2df_equals(const BOX2DF *a, const BOX2DF *b)
static uint64_t box2df_get_sortable_hash(const BOX2DF *b)
static int gserialized_gist_cmp_abbrev_2d(Datum x, Datum y, SortSupport ssup)
#define LIMIT_RATIO
bool box2df_contains(const BOX2DF *a, const BOX2DF *b)
Datum gserialized_gist_union_2d(PG_FUNCTION_ARGS)
#define PLACE_RIGHT(box, off)
static int gserialized_gist_cmp_full_2d(Datum a, Datum b, SortSupport ssup)
Datum gserialized_gist_compress_2d(PG_FUNCTION_ARGS)
Datum gserialized_distance_centroid_2d(PG_FUNCTION_ARGS)
Datum gserialized_contains_box2df_box2df_2d(PG_FUNCTION_ARGS)
Datum gserialized_overlaps_2d(PG_FUNCTION_ARGS)
Datum gserialized_overlaps_box2df_geom_2d(PG_FUNCTION_ARGS)
Datum gserialized_within_box2df_box2df_2d(PG_FUNCTION_ARGS)
bool box2df_right(const BOX2DF *a, const BOX2DF *b)
bool box2df_overlaps(const BOX2DF *a, const BOX2DF *b)
Datum gserialized_overleft_2d(PG_FUNCTION_ARGS)
void box2df_set_finite(BOX2DF *a)
static bool gserialized_gist_consistent_internal_2d(BOX2DF *key, BOX2DF *query, StrategyNumber strategy)
Datum gserialized_overlaps_box2df_box2df_2d(PG_FUNCTION_ARGS)
Datum gserialized_gist_distance_2d(PG_FUNCTION_ARGS)
Datum gserialized_overright_2d(PG_FUNCTION_ARGS)
static bool box2df_within(const BOX2DF *a, const BOX2DF *b)
bool box2df_above(const BOX2DF *a, const BOX2DF *b)
Datum gserialized_right_2d(PG_FUNCTION_ARGS)
static bool gserialized_gist_abbrev_abort_2d(int memtupcount, SortSupport ssup)
bool box2df_overbelow(const BOX2DF *a, const BOX2DF *b)
bool(* box2df_predicate)(const BOX2DF *a, const BOX2DF *b)
static double box2df_distance(const BOX2DF *a, const BOX2DF *b)
Calculate the box->box distance.
static double box2df_distance_leaf_centroid(const BOX2DF *a, const BOX2DF *b)
Calculate the centroid->centroid distance between the boxes.
Datum gserialized_below_2d(PG_FUNCTION_ARGS)
Datum gserialized_overabove_2d(PG_FUNCTION_ARGS)
static void g_box_consider_split(ConsiderSplitContext *context, int dimNum, float rightLower, int minLeftCount, float leftUpper, int maxLeftCount)
int box2df_to_gbox_p(BOX2DF *a, GBOX *box)
static int interval_cmp_lower(const void *i1, const void *i2)
bool box2df_overright(const BOX2DF *a, const BOX2DF *b)
static double pt_distance(double ax, double ay, double bx, double by)
static float pack_float(const float value, const uint8_t realm)
static int common_entry_cmp(const void *i1, const void *i2)
Datum gserialized_gist_decompress_2d(PG_FUNCTION_ARGS)
int gserialized_datum_get_box2df_p(Datum gsdatum, BOX2DF *box2df)
int gserialized_datum_get_internals_p(Datum gsdatum, GBOX *gbox, lwflags_t *flags, uint8_t *type, int32_t *srid)
Peak into a GSERIALIZED datum to find its bounding box and some other metadata.
int gserialized_datum_get_gbox_p(Datum gsdatum, GBOX *gbox)
Given a GSERIALIZED datum, as quickly as possible (peaking into the top of the memory) return the gbo...
static int box2df_from_gbox_p(GBOX *box, BOX2DF *a)
bool box2df_overabove(const BOX2DF *a, const BOX2DF *b)
Datum gserialized_gist_picksplit_2d(PG_FUNCTION_ARGS)
#define PLACE_LEFT(box, off)
void box2df_validate(BOX2DF *b)
static float box2df_penalty(const BOX2DF *b1, const BOX2DF *b2)
bool box2df_below(const BOX2DF *a, const BOX2DF *b)
static bool gserialized_gist_consistent_leaf_2d(BOX2DF *key, BOX2DF *query, StrategyNumber strategy)
bool box2df_overleft(const BOX2DF *a, const BOX2DF *b)
Datum gserialized_above_2d(PG_FUNCTION_ARGS)
static int gserialized_datum_predicate_box2df_geom_2d(const BOX2DF *br1, Datum gs2, box2df_predicate predicate)
BOX2DF * box2df_copy(BOX2DF *b)
Datum gserialized_same_2d(PG_FUNCTION_ARGS)
Datum box2df_in(PG_FUNCTION_ARGS)
static void adjustBox(BOX2DF *b, BOX2DF *addon)
static float non_negative(float val)
#define LW_FALSE
Definition: liblwgeom.h:94
#define LW_FAILURE
Definition: liblwgeom.h:96
#define LWSIZE_GET(varsize)
Macro for reading the size from the GSERIALIZED size attribute.
Definition: liblwgeom.h:324
#define LW_SUCCESS
Definition: liblwgeom.h:97
uint16_t lwflags_t
Definition: liblwgeom.h:299
float next_float_up(double d)
Definition: lwgeom_api.c:75
#define LW_TRUE
Return types for functions with status returns.
Definition: liblwgeom.h:93
float next_float_down(double d)
Definition: lwgeom_api.c:54
This library is the generic geometry handling section of PostGIS.
#define OUT_MAX_BYTES_DOUBLE
int lwprint_double(double d, int maxdd, char *buf)
Definition: lwprint.c:457
#define NAN
Definition: lwgeodetic.h:37
#define POSTGIS_FREE_IF_COPY_P(ptrsrc, ptrori)
Definition: lwinline.h:344
static uint64_t uint32_hilbert(uint32_t px, uint32_t py)
Definition: lwinline.h:260
static double distance(double x1, double y1, double x2, double y2)
Definition: lwtree.c:1032
int value
Definition: genraster.py:62
type
Definition: ovdump.py:42
double ymax
Definition: liblwgeom.h:357
double xmax
Definition: liblwgeom.h:355
double ymin
Definition: liblwgeom.h:356
double xmin
Definition: liblwgeom.h:354
uint32_t size
Definition: liblwgeom.h:444