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-2018, 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/stratnum.h" 78 #include "catalog/pg_type.h" 79 #include "utils/builtins.h" 80 #include "utils/geo_decls.h" 81 82 /* 83 * Comparator for qsort 84 * 85 * We don't need to use the floating point macros in here, because this 86 * is going only going to be used in a place to effect the performance 87 * of the index, not the correctness. 88 */ 89 static int 90 compareDoubles(const void *a, const void *b) 91 { 92 double x = *(double *) a; 93 double y = *(double *) b; 94 95 if (x == y) 96 return 0; 97 return (x > y) ? 1 : -1; 98 } 99 100 typedef struct 101 { 102 double low; 103 double high; 104 } Range; 105 106 typedef struct 107 { 108 Range left; 109 Range right; 110 } RangeBox; 111 112 typedef struct 113 { 114 RangeBox range_box_x; 115 RangeBox range_box_y; 116 } RectBox; 117 118 /* 119 * Calculate the quadrant 120 * 121 * The quadrant is 8 bit unsigned integer with 4 least bits in use. 122 * This function accepts BOXes as input. They are not casted to 123 * RangeBoxes, yet. All 4 bits are set by comparing a corner of the box. 124 * This makes 16 quadrants in total. 125 */ 126 static uint8 127 getQuadrant(BOX *centroid, BOX *inBox) 128 { 129 uint8 quadrant = 0; 130 131 if (inBox->low.x > centroid->low.x) 132 quadrant |= 0x8; 133 134 if (inBox->high.x > centroid->high.x) 135 quadrant |= 0x4; 136 137 if (inBox->low.y > centroid->low.y) 138 quadrant |= 0x2; 139 140 if (inBox->high.y > centroid->high.y) 141 quadrant |= 0x1; 142 143 return quadrant; 144 } 145 146 /* 147 * Get RangeBox using BOX 148 * 149 * We are turning the BOX to our structures to emphasize their function 150 * of representing points in 4D space. It also is more convenient to 151 * access the values with this structure. 152 */ 153 static RangeBox * 154 getRangeBox(BOX *box) 155 { 156 RangeBox *range_box = (RangeBox *) palloc(sizeof(RangeBox)); 157 158 range_box->left.low = box->low.x; 159 range_box->left.high = box->high.x; 160 161 range_box->right.low = box->low.y; 162 range_box->right.high = box->high.y; 163 164 return range_box; 165 } 166 167 /* 168 * Initialize the traversal value 169 * 170 * In the beginning, we don't have any restrictions. We have to 171 * initialize the struct to cover the whole 4D space. 172 */ 173 static RectBox * 174 initRectBox(void) 175 { 176 RectBox *rect_box = (RectBox *) palloc(sizeof(RectBox)); 177 double infinity = get_float8_infinity(); 178 179 rect_box->range_box_x.left.low = -infinity; 180 rect_box->range_box_x.left.high = infinity; 181 182 rect_box->range_box_x.right.low = -infinity; 183 rect_box->range_box_x.right.high = infinity; 184 185 rect_box->range_box_y.left.low = -infinity; 186 rect_box->range_box_y.left.high = infinity; 187 188 rect_box->range_box_y.right.low = -infinity; 189 rect_box->range_box_y.right.high = infinity; 190 191 return rect_box; 192 } 193 194 /* 195 * Calculate the next traversal value 196 * 197 * All centroids are bounded by RectBox, but SP-GiST only keeps 198 * boxes. When we are traversing the tree, we must calculate RectBox, 199 * using centroid and quadrant. 200 */ 201 static RectBox * 202 nextRectBox(RectBox *rect_box, RangeBox *centroid, uint8 quadrant) 203 { 204 RectBox *next_rect_box = (RectBox *) palloc(sizeof(RectBox)); 205 206 memcpy(next_rect_box, rect_box, sizeof(RectBox)); 207 208 if (quadrant & 0x8) 209 next_rect_box->range_box_x.left.low = centroid->left.low; 210 else 211 next_rect_box->range_box_x.left.high = centroid->left.low; 212 213 if (quadrant & 0x4) 214 next_rect_box->range_box_x.right.low = centroid->left.high; 215 else 216 next_rect_box->range_box_x.right.high = centroid->left.high; 217 218 if (quadrant & 0x2) 219 next_rect_box->range_box_y.left.low = centroid->right.low; 220 else 221 next_rect_box->range_box_y.left.high = centroid->right.low; 222 223 if (quadrant & 0x1) 224 next_rect_box->range_box_y.right.low = centroid->right.high; 225 else 226 next_rect_box->range_box_y.right.high = centroid->right.high; 227 228 return next_rect_box; 229 } 230 231 /* Can any range from range_box overlap with this argument? */ 232 static bool 233 overlap2D(RangeBox *range_box, Range *query) 234 { 235 return FPge(range_box->right.high, query->low) && 236 FPle(range_box->left.low, query->high); 237 } 238 239 /* Can any rectangle from rect_box overlap with this argument? */ 240 static bool 241 overlap4D(RectBox *rect_box, RangeBox *query) 242 { 243 return overlap2D(&rect_box->range_box_x, &query->left) && 244 overlap2D(&rect_box->range_box_y, &query->right); 245 } 246 247 /* Can any range from range_box contain this argument? */ 248 static bool 249 contain2D(RangeBox *range_box, Range *query) 250 { 251 return FPge(range_box->right.high, query->high) && 252 FPle(range_box->left.low, query->low); 253 } 254 255 /* Can any rectangle from rect_box contain this argument? */ 256 static bool 257 contain4D(RectBox *rect_box, RangeBox *query) 258 { 259 return contain2D(&rect_box->range_box_x, &query->left) && 260 contain2D(&rect_box->range_box_y, &query->right); 261 } 262 263 /* Can any range from range_box be contained by this argument? */ 264 static bool 265 contained2D(RangeBox *range_box, Range *query) 266 { 267 return FPle(range_box->left.low, query->high) && 268 FPge(range_box->left.high, query->low) && 269 FPle(range_box->right.low, query->high) && 270 FPge(range_box->right.high, query->low); 271 } 272 273 /* Can any rectangle from rect_box be contained by this argument? */ 274 static bool 275 contained4D(RectBox *rect_box, RangeBox *query) 276 { 277 return contained2D(&rect_box->range_box_x, &query->left) && 278 contained2D(&rect_box->range_box_y, &query->right); 279 } 280 281 /* Can any range from range_box to be lower than this argument? */ 282 static bool 283 lower2D(RangeBox *range_box, Range *query) 284 { 285 return FPlt(range_box->left.low, query->low) && 286 FPlt(range_box->right.low, query->low); 287 } 288 289 /* Can any range from range_box not extend to the right side of the query? */ 290 static bool 291 overLower2D(RangeBox *range_box, Range *query) 292 { 293 return FPle(range_box->left.low, query->high) && 294 FPle(range_box->right.low, query->high); 295 } 296 297 /* Can any range from range_box to be higher than this argument? */ 298 static bool 299 higher2D(RangeBox *range_box, Range *query) 300 { 301 return FPgt(range_box->left.high, query->high) && 302 FPgt(range_box->right.high, query->high); 303 } 304 305 /* Can any range from range_box not extend to the left side of the query? */ 306 static bool 307 overHigher2D(RangeBox *range_box, Range *query) 308 { 309 return FPge(range_box->left.high, query->low) && 310 FPge(range_box->right.high, query->low); 311 } 312 313 /* Can any rectangle from rect_box be left of this argument? */ 314 static bool 315 left4D(RectBox *rect_box, RangeBox *query) 316 { 317 return lower2D(&rect_box->range_box_x, &query->left); 318 } 319 320 /* Can any rectangle from rect_box does not extend the right of this argument? */ 321 static bool 322 overLeft4D(RectBox *rect_box, RangeBox *query) 323 { 324 return overLower2D(&rect_box->range_box_x, &query->left); 325 } 326 327 /* Can any rectangle from rect_box be right of this argument? */ 328 static bool 329 right4D(RectBox *rect_box, RangeBox *query) 330 { 331 return higher2D(&rect_box->range_box_x, &query->left); 332 } 333 334 /* Can any rectangle from rect_box does not extend the left of this argument? */ 335 static bool 336 overRight4D(RectBox *rect_box, RangeBox *query) 337 { 338 return overHigher2D(&rect_box->range_box_x, &query->left); 339 } 340 341 /* Can any rectangle from rect_box be below of this argument? */ 342 static bool 343 below4D(RectBox *rect_box, RangeBox *query) 344 { 345 return lower2D(&rect_box->range_box_y, &query->right); 346 } 347 348 /* Can any rectangle from rect_box does not extend above this argument? */ 349 static bool 350 overBelow4D(RectBox *rect_box, RangeBox *query) 351 { 352 return overLower2D(&rect_box->range_box_y, &query->right); 353 } 354 355 /* Can any rectangle from rect_box be above of this argument? */ 356 static bool 357 above4D(RectBox *rect_box, RangeBox *query) 358 { 359 return higher2D(&rect_box->range_box_y, &query->right); 360 } 361 362 /* Can any rectangle from rect_box does not extend below of this argument? */ 363 static bool 364 overAbove4D(RectBox *rect_box, RangeBox *query) 365 { 366 return overHigher2D(&rect_box->range_box_y, &query->right); 367 } 368 369 /* 370 * SP-GiST config function 371 */ 372 Datum 373 spg_box_quad_config(PG_FUNCTION_ARGS) 374 { 375 spgConfigOut *cfg = (spgConfigOut *) PG_GETARG_POINTER(1); 376 377 cfg->prefixType = BOXOID; 378 cfg->labelType = VOIDOID; /* We don't need node labels. */ 379 cfg->canReturnData = true; 380 cfg->longValuesOK = false; 381 382 PG_RETURN_VOID(); 383 } 384 385 /* 386 * SP-GiST choose function 387 */ 388 Datum 389 spg_box_quad_choose(PG_FUNCTION_ARGS) 390 { 391 spgChooseIn *in = (spgChooseIn *) PG_GETARG_POINTER(0); 392 spgChooseOut *out = (spgChooseOut *) PG_GETARG_POINTER(1); 393 BOX *centroid = DatumGetBoxP(in->prefixDatum), 394 *box = DatumGetBoxP(in->leafDatum); 395 396 out->resultType = spgMatchNode; 397 out->result.matchNode.restDatum = BoxPGetDatum(box); 398 399 /* nodeN will be set by core, when allTheSame. */ 400 if (!in->allTheSame) 401 out->result.matchNode.nodeN = getQuadrant(centroid, box); 402 403 PG_RETURN_VOID(); 404 } 405 406 /* 407 * SP-GiST pick-split function 408 * 409 * It splits a list of boxes into quadrants by choosing a central 4D 410 * point as the median of the coordinates of the boxes. 411 */ 412 Datum 413 spg_box_quad_picksplit(PG_FUNCTION_ARGS) 414 { 415 spgPickSplitIn *in = (spgPickSplitIn *) PG_GETARG_POINTER(0); 416 spgPickSplitOut *out = (spgPickSplitOut *) PG_GETARG_POINTER(1); 417 BOX *centroid; 418 int median, 419 i; 420 double *lowXs = palloc(sizeof(double) * in->nTuples); 421 double *highXs = palloc(sizeof(double) * in->nTuples); 422 double *lowYs = palloc(sizeof(double) * in->nTuples); 423 double *highYs = palloc(sizeof(double) * in->nTuples); 424 425 /* Calculate median of all 4D coordinates */ 426 for (i = 0; i < in->nTuples; i++) 427 { 428 BOX *box = DatumGetBoxP(in->datums[i]); 429 430 lowXs[i] = box->low.x; 431 highXs[i] = box->high.x; 432 lowYs[i] = box->low.y; 433 highYs[i] = box->high.y; 434 } 435 436 qsort(lowXs, in->nTuples, sizeof(double), compareDoubles); 437 qsort(highXs, in->nTuples, sizeof(double), compareDoubles); 438 qsort(lowYs, in->nTuples, sizeof(double), compareDoubles); 439 qsort(highYs, in->nTuples, sizeof(double), compareDoubles); 440 441 median = in->nTuples / 2; 442 443 centroid = palloc(sizeof(BOX)); 444 445 centroid->low.x = lowXs[median]; 446 centroid->high.x = highXs[median]; 447 centroid->low.y = lowYs[median]; 448 centroid->high.y = highYs[median]; 449 450 /* Fill the output */ 451 out->hasPrefix = true; 452 out->prefixDatum = BoxPGetDatum(centroid); 453 454 out->nNodes = 16; 455 out->nodeLabels = NULL; /* We don't need node labels. */ 456 457 out->mapTuplesToNodes = palloc(sizeof(int) * in->nTuples); 458 out->leafTupleDatums = palloc(sizeof(Datum) * in->nTuples); 459 460 /* 461 * Assign ranges to corresponding nodes according to quadrants relative to 462 * the "centroid" range 463 */ 464 for (i = 0; i < in->nTuples; i++) 465 { 466 BOX *box = DatumGetBoxP(in->datums[i]); 467 uint8 quadrant = getQuadrant(centroid, box); 468 469 out->leafTupleDatums[i] = BoxPGetDatum(box); 470 out->mapTuplesToNodes[i] = quadrant; 471 } 472 473 PG_RETURN_VOID(); 474 } 475 476 /* 477 * Check if result of consistent method based on bounding box is exact. 478 */ 479 static bool 480 is_bounding_box_test_exact(StrategyNumber strategy) 481 { 482 switch (strategy) 483 { 484 case RTLeftStrategyNumber: 485 case RTOverLeftStrategyNumber: 486 case RTOverRightStrategyNumber: 487 case RTRightStrategyNumber: 488 case RTOverBelowStrategyNumber: 489 case RTBelowStrategyNumber: 490 case RTAboveStrategyNumber: 491 case RTOverAboveStrategyNumber: 492 return true; 493 494 default: 495 return false; 496 } 497 } 498 499 /* 500 * Get bounding box for ScanKey. 501 */ 502 static BOX * 503 spg_box_quad_get_scankey_bbox(ScanKey sk, bool *recheck) 504 { 505 switch (sk->sk_subtype) 506 { 507 case BOXOID: 508 return DatumGetBoxP(sk->sk_argument); 509 510 case POLYGONOID: 511 if (recheck && !is_bounding_box_test_exact(sk->sk_strategy)) 512 *recheck = true; 513 return &DatumGetPolygonP(sk->sk_argument)->boundbox; 514 515 default: 516 elog(ERROR, "unrecognized scankey subtype: %d", sk->sk_subtype); 517 return NULL; 518 } 519 } 520 521 /* 522 * SP-GiST inner consistent function 523 */ 524 Datum 525 spg_box_quad_inner_consistent(PG_FUNCTION_ARGS) 526 { 527 spgInnerConsistentIn *in = (spgInnerConsistentIn *) PG_GETARG_POINTER(0); 528 spgInnerConsistentOut *out = (spgInnerConsistentOut *) PG_GETARG_POINTER(1); 529 int i; 530 MemoryContext old_ctx; 531 RectBox *rect_box; 532 uint8 quadrant; 533 RangeBox *centroid, 534 **queries; 535 536 if (in->allTheSame) 537 { 538 /* Report that all nodes should be visited */ 539 out->nNodes = in->nNodes; 540 out->nodeNumbers = (int *) palloc(sizeof(int) * in->nNodes); 541 for (i = 0; i < in->nNodes; i++) 542 out->nodeNumbers[i] = i; 543 544 PG_RETURN_VOID(); 545 } 546 547 /* 548 * We are saving the traversal value or initialize it an unbounded one, if 549 * we have just begun to walk the tree. 550 */ 551 if (in->traversalValue) 552 rect_box = in->traversalValue; 553 else 554 rect_box = initRectBox(); 555 556 /* 557 * We are casting the prefix and queries to RangeBoxes for ease of the 558 * following operations. 559 */ 560 centroid = getRangeBox(DatumGetBoxP(in->prefixDatum)); 561 queries = (RangeBox **) palloc(in->nkeys * sizeof(RangeBox *)); 562 for (i = 0; i < in->nkeys; i++) 563 { 564 BOX *box = spg_box_quad_get_scankey_bbox(&in->scankeys[i], NULL); 565 566 queries[i] = getRangeBox(box); 567 } 568 569 /* Allocate enough memory for nodes */ 570 out->nNodes = 0; 571 out->nodeNumbers = (int *) palloc(sizeof(int) * in->nNodes); 572 out->traversalValues = (void **) palloc(sizeof(void *) * in->nNodes); 573 574 /* 575 * We switch memory context, because we want to allocate memory for new 576 * traversal values (next_rect_box) and pass these pieces of memory to 577 * further call of this function. 578 */ 579 old_ctx = MemoryContextSwitchTo(in->traversalMemoryContext); 580 581 for (quadrant = 0; quadrant < in->nNodes; quadrant++) 582 { 583 RectBox *next_rect_box = nextRectBox(rect_box, centroid, quadrant); 584 bool flag = true; 585 586 for (i = 0; i < in->nkeys; i++) 587 { 588 StrategyNumber strategy = in->scankeys[i].sk_strategy; 589 590 switch (strategy) 591 { 592 case RTOverlapStrategyNumber: 593 flag = overlap4D(next_rect_box, queries[i]); 594 break; 595 596 case RTContainsStrategyNumber: 597 flag = contain4D(next_rect_box, queries[i]); 598 break; 599 600 case RTSameStrategyNumber: 601 case RTContainedByStrategyNumber: 602 flag = contained4D(next_rect_box, queries[i]); 603 break; 604 605 case RTLeftStrategyNumber: 606 flag = left4D(next_rect_box, queries[i]); 607 break; 608 609 case RTOverLeftStrategyNumber: 610 flag = overLeft4D(next_rect_box, queries[i]); 611 break; 612 613 case RTRightStrategyNumber: 614 flag = right4D(next_rect_box, queries[i]); 615 break; 616 617 case RTOverRightStrategyNumber: 618 flag = overRight4D(next_rect_box, queries[i]); 619 break; 620 621 case RTAboveStrategyNumber: 622 flag = above4D(next_rect_box, queries[i]); 623 break; 624 625 case RTOverAboveStrategyNumber: 626 flag = overAbove4D(next_rect_box, queries[i]); 627 break; 628 629 case RTBelowStrategyNumber: 630 flag = below4D(next_rect_box, queries[i]); 631 break; 632 633 case RTOverBelowStrategyNumber: 634 flag = overBelow4D(next_rect_box, queries[i]); 635 break; 636 637 default: 638 elog(ERROR, "unrecognized strategy: %d", strategy); 639 } 640 641 /* If any check is failed, we have found our answer. */ 642 if (!flag) 643 break; 644 } 645 646 if (flag) 647 { 648 out->traversalValues[out->nNodes] = next_rect_box; 649 out->nodeNumbers[out->nNodes] = quadrant; 650 out->nNodes++; 651 } 652 else 653 { 654 /* 655 * If this node is not selected, we don't need to keep the next 656 * traversal value in the memory context. 657 */ 658 pfree(next_rect_box); 659 } 660 } 661 662 /* Switch back */ 663 MemoryContextSwitchTo(old_ctx); 664 665 PG_RETURN_VOID(); 666 } 667 668 /* 669 * SP-GiST inner consistent function 670 */ 671 Datum 672 spg_box_quad_leaf_consistent(PG_FUNCTION_ARGS) 673 { 674 spgLeafConsistentIn *in = (spgLeafConsistentIn *) PG_GETARG_POINTER(0); 675 spgLeafConsistentOut *out = (spgLeafConsistentOut *) PG_GETARG_POINTER(1); 676 Datum leaf = in->leafDatum; 677 bool flag = true; 678 int i; 679 680 /* All tests are exact. */ 681 out->recheck = false; 682 683 /* leafDatum is what it is... */ 684 out->leafValue = in->leafDatum; 685 686 /* Perform the required comparison(s) */ 687 for (i = 0; i < in->nkeys; i++) 688 { 689 StrategyNumber strategy = in->scankeys[i].sk_strategy; 690 BOX *box = spg_box_quad_get_scankey_bbox(&in->scankeys[i], 691 &out->recheck); 692 Datum query = BoxPGetDatum(box); 693 694 switch (strategy) 695 { 696 case RTOverlapStrategyNumber: 697 flag = DatumGetBool(DirectFunctionCall2(box_overlap, leaf, 698 query)); 699 break; 700 701 case RTContainsStrategyNumber: 702 flag = DatumGetBool(DirectFunctionCall2(box_contain, leaf, 703 query)); 704 break; 705 706 case RTContainedByStrategyNumber: 707 flag = DatumGetBool(DirectFunctionCall2(box_contained, leaf, 708 query)); 709 break; 710 711 case RTSameStrategyNumber: 712 flag = DatumGetBool(DirectFunctionCall2(box_same, leaf, 713 query)); 714 break; 715 716 case RTLeftStrategyNumber: 717 flag = DatumGetBool(DirectFunctionCall2(box_left, leaf, 718 query)); 719 break; 720 721 case RTOverLeftStrategyNumber: 722 flag = DatumGetBool(DirectFunctionCall2(box_overleft, leaf, 723 query)); 724 break; 725 726 case RTRightStrategyNumber: 727 flag = DatumGetBool(DirectFunctionCall2(box_right, leaf, 728 query)); 729 break; 730 731 case RTOverRightStrategyNumber: 732 flag = DatumGetBool(DirectFunctionCall2(box_overright, leaf, 733 query)); 734 break; 735 736 case RTAboveStrategyNumber: 737 flag = DatumGetBool(DirectFunctionCall2(box_above, leaf, 738 query)); 739 break; 740 741 case RTOverAboveStrategyNumber: 742 flag = DatumGetBool(DirectFunctionCall2(box_overabove, leaf, 743 query)); 744 break; 745 746 case RTBelowStrategyNumber: 747 flag = DatumGetBool(DirectFunctionCall2(box_below, leaf, 748 query)); 749 break; 750 751 case RTOverBelowStrategyNumber: 752 flag = DatumGetBool(DirectFunctionCall2(box_overbelow, leaf, 753 query)); 754 break; 755 756 default: 757 elog(ERROR, "unrecognized strategy: %d", strategy); 758 } 759 760 /* If any check is failed, we have found our answer. */ 761 if (!flag) 762 break; 763 } 764 765 PG_RETURN_BOOL(flag); 766 } 767 768 769 /* 770 * SP-GiST config function for 2-D types that are lossy represented by their 771 * bounding boxes 772 */ 773 Datum 774 spg_bbox_quad_config(PG_FUNCTION_ARGS) 775 { 776 spgConfigOut *cfg = (spgConfigOut *) PG_GETARG_POINTER(1); 777 778 cfg->prefixType = BOXOID; /* A type represented by its bounding box */ 779 cfg->labelType = VOIDOID; /* We don't need node labels. */ 780 cfg->leafType = BOXOID; 781 cfg->canReturnData = false; 782 cfg->longValuesOK = false; 783 784 PG_RETURN_VOID(); 785 } 786 787 /* 788 * SP-GiST compress function for polygons 789 */ 790 Datum 791 spg_poly_quad_compress(PG_FUNCTION_ARGS) 792 { 793 POLYGON *polygon = PG_GETARG_POLYGON_P(0); 794 BOX *box; 795 796 box = box_copy(&polygon->boundbox); 797 798 PG_RETURN_BOX_P(box); 799 } 800