Coverage Report

Created: 2025-10-09 06:07

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