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

Generated by: LCOV version 1.16