~ubuntu-branches/debian/experimental/postgresql-11/experimental

« back to all changes in this revision

Viewing changes to src/backend/access/gist/gistproc.c

  • Committer: Package Import Robot
  • Author(s): Christoph Berg
  • Date: 2018-05-22 14:19:08 UTC
  • Revision ID: package-import@ubuntu.com-20180522141908-0oy9ujs1b5vrda74
Tags: upstream-11~beta1
ImportĀ upstreamĀ versionĀ 11~beta1

Show diffs side-by-side

added added

removed removed

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