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