LCOV - code coverage report
Current view: top level - src/backend/access/gist - gistproc.c (source / functions) Hit Total Coverage
Test: PostgreSQL 18devel Lines: 479 578 82.9 %
Date: 2024-11-21 08:14:44 Functions: 37 38 97.4 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.14