Coverage Report

Created: 2026-10-16 06:48

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