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