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