LCOV - code coverage report
Current view: top level - src/backend/access/gist - gistproc.c (source / functions) Coverage Total Hit
Test: Code coverage Lines: 1.6 % 684 11
Test Date: 2026-01-26 10:56:24 Functions: 2.6 % 38 1
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 1.2 % 328 4

             Branch data     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                 :           0 : rt_box_union(BOX *n, const BOX *a, const BOX *b)
      56                 :             : {
      57                 :           0 :         n->high.x = float8_max(a->high.x, b->high.x);
      58                 :           0 :         n->high.y = float8_max(a->high.y, b->high.y);
      59                 :           0 :         n->low.x = float8_min(a->low.x, b->low.x);
      60                 :           0 :         n->low.y = float8_min(a->low.y, b->low.y);
      61                 :           0 : }
      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                 :           0 : 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   [ #  #  #  # ]:           0 :         if (float8_le(box->high.x, box->low.x) ||
      78                 :           0 :                 float8_le(box->high.y, box->low.y))
      79                 :           0 :                 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   [ #  #  #  #  :           0 :         if (isnan(box->high.x) || isnan(box->high.y))
          #  #  #  #  #  
                #  #  # ]
      87                 :           0 :                 return get_float8_infinity();
      88                 :           0 :         return float8_mul(float8_mi(box->high.x, box->low.x),
      89                 :           0 :                                           float8_mi(box->high.y, box->low.y));
      90                 :           0 : }
      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                 :           0 : box_penalty(const BOX *original, const BOX *new)
      98                 :             : {
      99                 :           0 :         BOX                     unionbox;
     100                 :             : 
     101                 :           0 :         rt_box_union(&unionbox, original, new);
     102                 :           0 :         return float8_mi(size_box(&unionbox), size_box(original));
     103                 :           0 : }
     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                 :        1361 : gist_box_consistent(PG_FUNCTION_ARGS)
     114                 :             : {
     115                 :        1361 :         GISTENTRY  *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
     116                 :        1361 :         BOX                *query = PG_GETARG_BOX_P(1);
     117                 :        1361 :         StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2);
     118                 :             : #ifdef NOT_USED
     119                 :             :         Oid                     subtype = PG_GETARG_OID(3);
     120                 :             : #endif
     121                 :        1361 :         bool       *recheck = (bool *) PG_GETARG_POINTER(4);
     122                 :             : 
     123                 :             :         /* All cases served by this function are exact */
     124                 :        1361 :         *recheck = false;
     125                 :             : 
     126   [ +  -  -  + ]:        1361 :         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         [ +  + ]:        1361 :         if (GIST_LEAF(entry))
     134                 :         782 :                 PG_RETURN_BOOL(gist_box_leaf_consistent(DatumGetBoxP(entry->key),
     135                 :             :                                                                                                 query,
     136                 :             :                                                                                                 strategy));
     137                 :             :         else
     138                 :         579 :                 PG_RETURN_BOOL(rtree_internal_consistent(DatumGetBoxP(entry->key),
     139                 :             :                                                                                                  query,
     140                 :             :                                                                                                  strategy));
     141                 :        1361 : }
     142                 :             : 
     143                 :             : /*
     144                 :             :  * Increase BOX b to include addon.
     145                 :             :  */
     146                 :             : static void
     147                 :           0 : adjustBox(BOX *b, const BOX *addon)
     148                 :             : {
     149         [ #  # ]:           0 :         if (float8_lt(b->high.x, addon->high.x))
     150                 :           0 :                 b->high.x = addon->high.x;
     151         [ #  # ]:           0 :         if (float8_gt(b->low.x, addon->low.x))
     152                 :           0 :                 b->low.x = addon->low.x;
     153         [ #  # ]:           0 :         if (float8_lt(b->high.y, addon->high.y))
     154                 :           0 :                 b->high.y = addon->high.y;
     155         [ #  # ]:           0 :         if (float8_gt(b->low.y, addon->low.y))
     156                 :           0 :                 b->low.y = addon->low.y;
     157                 :           0 : }
     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                 :           0 : gist_box_union(PG_FUNCTION_ARGS)
     166                 :             : {
     167                 :           0 :         GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
     168                 :           0 :         int                *sizep = (int *) PG_GETARG_POINTER(1);
     169                 :           0 :         int                     numranges,
     170                 :             :                                 i;
     171                 :           0 :         BOX                *cur,
     172                 :             :                            *pageunion;
     173                 :             : 
     174                 :           0 :         numranges = entryvec->n;
     175                 :           0 :         pageunion = palloc_object(BOX);
     176                 :           0 :         cur = DatumGetBoxP(entryvec->vector[0].key);
     177                 :           0 :         memcpy(pageunion, cur, sizeof(BOX));
     178                 :             : 
     179         [ #  # ]:           0 :         for (i = 1; i < numranges; i++)
     180                 :             :         {
     181                 :           0 :                 cur = DatumGetBoxP(entryvec->vector[i].key);
     182                 :           0 :                 adjustBox(pageunion, cur);
     183                 :           0 :         }
     184                 :           0 :         *sizep = sizeof(BOX);
     185                 :             : 
     186                 :           0 :         PG_RETURN_POINTER(pageunion);
     187                 :           0 : }
     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                 :           0 : gist_box_penalty(PG_FUNCTION_ARGS)
     201                 :             : {
     202                 :           0 :         GISTENTRY  *origentry = (GISTENTRY *) PG_GETARG_POINTER(0);
     203                 :           0 :         GISTENTRY  *newentry = (GISTENTRY *) PG_GETARG_POINTER(1);
     204                 :           0 :         float      *result = (float *) PG_GETARG_POINTER(2);
     205                 :           0 :         BOX                *origbox = DatumGetBoxP(origentry->key);
     206                 :           0 :         BOX                *newbox = DatumGetBoxP(newentry->key);
     207                 :             : 
     208                 :           0 :         *result = (float) box_penalty(origbox, newbox);
     209                 :           0 :         PG_RETURN_POINTER(result);
     210                 :           0 : }
     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                 :           0 : fallbackSplit(GistEntryVector *entryvec, GIST_SPLITVEC *v)
     218                 :             : {
     219                 :           0 :         OffsetNumber i,
     220                 :             :                                 maxoff;
     221                 :           0 :         BOX                *unionL = NULL,
     222                 :           0 :                            *unionR = NULL;
     223                 :           0 :         int                     nbytes;
     224                 :             : 
     225                 :           0 :         maxoff = entryvec->n - 1;
     226                 :             : 
     227                 :           0 :         nbytes = (maxoff + 2) * sizeof(OffsetNumber);
     228                 :           0 :         v->spl_left = (OffsetNumber *) palloc(nbytes);
     229                 :           0 :         v->spl_right = (OffsetNumber *) palloc(nbytes);
     230                 :           0 :         v->spl_nleft = v->spl_nright = 0;
     231                 :             : 
     232         [ #  # ]:           0 :         for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
     233                 :             :         {
     234                 :           0 :                 BOX                *cur = DatumGetBoxP(entryvec->vector[i].key);
     235                 :             : 
     236         [ #  # ]:           0 :                 if (i <= (maxoff - FirstOffsetNumber + 1) / 2)
     237                 :             :                 {
     238                 :           0 :                         v->spl_left[v->spl_nleft] = i;
     239         [ #  # ]:           0 :                         if (unionL == NULL)
     240                 :             :                         {
     241                 :           0 :                                 unionL = palloc_object(BOX);
     242                 :           0 :                                 *unionL = *cur;
     243                 :           0 :                         }
     244                 :             :                         else
     245                 :           0 :                                 adjustBox(unionL, cur);
     246                 :             : 
     247                 :           0 :                         v->spl_nleft++;
     248                 :           0 :                 }
     249                 :             :                 else
     250                 :             :                 {
     251                 :           0 :                         v->spl_right[v->spl_nright] = i;
     252         [ #  # ]:           0 :                         if (unionR == NULL)
     253                 :             :                         {
     254                 :           0 :                                 unionR = palloc_object(BOX);
     255                 :           0 :                                 *unionR = *cur;
     256                 :           0 :                         }
     257                 :             :                         else
     258                 :           0 :                                 adjustBox(unionR, cur);
     259                 :             : 
     260                 :           0 :                         v->spl_nright++;
     261                 :             :                 }
     262                 :           0 :         }
     263                 :             : 
     264                 :           0 :         v->spl_ldatum = BoxPGetDatum(unionL);
     265                 :           0 :         v->spl_rdatum = BoxPGetDatum(unionR);
     266                 :           0 : }
     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                 :           0 : interval_cmp_lower(const void *i1, const void *i2)
     317                 :             : {
     318                 :           0 :         float8          lower1 = ((const SplitInterval *) i1)->lower,
     319                 :           0 :                                 lower2 = ((const SplitInterval *) i2)->lower;
     320                 :             : 
     321                 :           0 :         return float8_cmp_internal(lower1, lower2);
     322                 :           0 : }
     323                 :             : 
     324                 :             : /*
     325                 :             :  * Interval comparison function by upper bound of the interval;
     326                 :             :  */
     327                 :             : static int
     328                 :           0 : interval_cmp_upper(const void *i1, const void *i2)
     329                 :             : {
     330                 :           0 :         float8          upper1 = ((const SplitInterval *) i1)->upper,
     331                 :           0 :                                 upper2 = ((const SplitInterval *) i2)->upper;
     332                 :             : 
     333                 :           0 :         return float8_cmp_internal(upper1, upper2);
     334                 :           0 : }
     335                 :             : 
     336                 :             : /*
     337                 :             :  * Replace negative (or NaN) value with zero.
     338                 :             :  */
     339                 :             : static inline float
     340                 :           0 : non_negative(float val)
     341                 :             : {
     342         [ #  # ]:           0 :         if (val >= 0.0f)
     343                 :           0 :                 return val;
     344                 :             :         else
     345                 :           0 :                 return 0.0f;
     346                 :           0 : }
     347                 :             : 
     348                 :             : /*
     349                 :             :  * Consider replacement of currently selected split with the better one.
     350                 :             :  */
     351                 :             : static inline void
     352                 :           0 : g_box_consider_split(ConsiderSplitContext *context, int dimNum,
     353                 :             :                                          float8 rightLower, int minLeftCount,
     354                 :             :                                          float8 leftUpper, int maxLeftCount)
     355                 :             : {
     356                 :           0 :         int                     leftCount,
     357                 :             :                                 rightCount;
     358                 :           0 :         float4          ratio,
     359                 :             :                                 overlap;
     360                 :           0 :         float8          range;
     361                 :             : 
     362                 :             :         /*
     363                 :             :          * Calculate entries distribution ratio assuming most uniform distribution
     364                 :             :          * of common entries.
     365                 :             :          */
     366         [ #  # ]:           0 :         if (minLeftCount >= (context->entriesCount + 1) / 2)
     367                 :             :         {
     368                 :           0 :                 leftCount = minLeftCount;
     369                 :           0 :         }
     370                 :             :         else
     371                 :             :         {
     372         [ #  # ]:           0 :                 if (maxLeftCount <= context->entriesCount / 2)
     373                 :           0 :                         leftCount = maxLeftCount;
     374                 :             :                 else
     375                 :           0 :                         leftCount = context->entriesCount / 2;
     376                 :             :         }
     377                 :           0 :         rightCount = context->entriesCount - leftCount;
     378                 :             : 
     379                 :             :         /*
     380                 :             :          * Ratio of split - quotient between size of lesser group and total
     381                 :             :          * entries count.
     382                 :             :          */
     383         [ #  # ]:           0 :         ratio = float4_div(Min(leftCount, rightCount), context->entriesCount);
     384                 :             : 
     385         [ #  # ]:           0 :         if (ratio > LIMIT_RATIO)
     386                 :             :         {
     387                 :           0 :                 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         [ #  # ]:           0 :                 if (dimNum == 0)
     397                 :           0 :                         range = float8_mi(context->boundingBox.high.x,
     398                 :           0 :                                                           context->boundingBox.low.x);
     399                 :             :                 else
     400                 :           0 :                         range = float8_mi(context->boundingBox.high.y,
     401                 :           0 :                                                           context->boundingBox.low.y);
     402                 :             : 
     403                 :           0 :                 overlap = float8_div(float8_mi(leftUpper, rightLower), range);
     404                 :             : 
     405                 :             :                 /* If there is no previous selection, select this */
     406         [ #  # ]:           0 :                 if (context->first)
     407                 :           0 :                         selectthis = true;
     408         [ #  # ]:           0 :                 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   [ #  #  #  # ]:           0 :                         if (overlap < context->overlap ||
     415         [ #  # ]:           0 :                                 (overlap == context->overlap && ratio > context->ratio))
     416                 :           0 :                                 selectthis = true;
     417                 :           0 :                 }
     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   [ #  #  #  # ]:           0 :                         if (non_negative(overlap) < non_negative(context->overlap) ||
     438         [ #  # ]:           0 :                                 (range > context->range &&
     439                 :           0 :                                  non_negative(overlap) <= non_negative(context->overlap)))
     440                 :           0 :                                 selectthis = true;
     441                 :             :                 }
     442                 :             : 
     443         [ #  # ]:           0 :                 if (selectthis)
     444                 :             :                 {
     445                 :             :                         /* save information about selected split */
     446                 :           0 :                         context->first = false;
     447                 :           0 :                         context->ratio = ratio;
     448                 :           0 :                         context->range = range;
     449                 :           0 :                         context->overlap = overlap;
     450                 :           0 :                         context->rightLower = rightLower;
     451                 :           0 :                         context->leftUpper = leftUpper;
     452                 :           0 :                         context->dim = dimNum;
     453                 :           0 :                 }
     454                 :           0 :         }
     455                 :           0 : }
     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                 :           0 : }
     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                 :           0 : gist_box_picksplit(PG_FUNCTION_ARGS)
     497                 :             : {
     498                 :           0 :         GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
     499                 :           0 :         GIST_SPLITVEC *v = (GIST_SPLITVEC *) PG_GETARG_POINTER(1);
     500                 :           0 :         OffsetNumber i,
     501                 :             :                                 maxoff;
     502                 :           0 :         ConsiderSplitContext context;
     503                 :           0 :         BOX                *box,
     504                 :             :                            *leftBox,
     505                 :             :                            *rightBox;
     506                 :           0 :         int                     dim,
     507                 :             :                                 commonEntriesCount;
     508                 :           0 :         SplitInterval *intervalsLower,
     509                 :             :                            *intervalsUpper;
     510                 :           0 :         CommonEntry *commonEntries;
     511                 :           0 :         int                     nentries;
     512                 :             : 
     513                 :           0 :         memset(&context, 0, sizeof(ConsiderSplitContext));
     514                 :             : 
     515                 :           0 :         maxoff = entryvec->n - 1;
     516                 :           0 :         nentries = context.entriesCount = maxoff - FirstOffsetNumber + 1;
     517                 :             : 
     518                 :             :         /* Allocate arrays for intervals along axes */
     519                 :           0 :         intervalsLower = (SplitInterval *) palloc(nentries * sizeof(SplitInterval));
     520                 :           0 :         intervalsUpper = (SplitInterval *) palloc(nentries * sizeof(SplitInterval));
     521                 :             : 
     522                 :             :         /*
     523                 :             :          * Calculate the overall minimum bounding box over all the entries.
     524                 :             :          */
     525         [ #  # ]:           0 :         for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
     526                 :             :         {
     527                 :           0 :                 box = DatumGetBoxP(entryvec->vector[i].key);
     528         [ #  # ]:           0 :                 if (i == FirstOffsetNumber)
     529                 :           0 :                         context.boundingBox = *box;
     530                 :             :                 else
     531                 :           0 :                         adjustBox(&context.boundingBox, box);
     532                 :           0 :         }
     533                 :             : 
     534                 :             :         /*
     535                 :             :          * Iterate over axes for optimal split searching.
     536                 :             :          */
     537                 :           0 :         context.first = true;           /* nothing selected yet */
     538         [ #  # ]:           0 :         for (dim = 0; dim < 2; dim++)
     539                 :             :         {
     540                 :           0 :                 float8          leftUpper,
     541                 :             :                                         rightLower;
     542                 :           0 :                 int                     i1,
     543                 :             :                                         i2;
     544                 :             : 
     545                 :             :                 /* Project each entry as an interval on the selected axis. */
     546         [ #  # ]:           0 :                 for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
     547                 :             :                 {
     548                 :           0 :                         box = DatumGetBoxP(entryvec->vector[i].key);
     549         [ #  # ]:           0 :                         if (dim == 0)
     550                 :             :                         {
     551                 :           0 :                                 intervalsLower[i - FirstOffsetNumber].lower = box->low.x;
     552                 :           0 :                                 intervalsLower[i - FirstOffsetNumber].upper = box->high.x;
     553                 :           0 :                         }
     554                 :             :                         else
     555                 :             :                         {
     556                 :           0 :                                 intervalsLower[i - FirstOffsetNumber].lower = box->low.y;
     557                 :           0 :                                 intervalsLower[i - FirstOffsetNumber].upper = box->high.y;
     558                 :             :                         }
     559                 :           0 :                 }
     560                 :             : 
     561                 :             :                 /*
     562                 :             :                  * Make two arrays of intervals: one sorted by lower bound and another
     563                 :             :                  * sorted by upper bound.
     564                 :             :                  */
     565                 :           0 :                 memcpy(intervalsUpper, intervalsLower,
     566                 :             :                            sizeof(SplitInterval) * nentries);
     567                 :           0 :                 qsort(intervalsLower, nentries, sizeof(SplitInterval),
     568                 :             :                           interval_cmp_lower);
     569                 :           0 :                 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                 :           0 :                 i1 = 0;
     607                 :           0 :                 i2 = 0;
     608                 :           0 :                 rightLower = intervalsLower[i1].lower;
     609                 :           0 :                 leftUpper = intervalsUpper[i2].lower;
     610                 :           0 :                 while (true)
     611                 :             :                 {
     612                 :             :                         /*
     613                 :             :                          * Find next lower bound of right group.
     614                 :             :                          */
     615   [ #  #  #  # ]:           0 :                         while (i1 < nentries &&
     616                 :           0 :                                    float8_eq(rightLower, intervalsLower[i1].lower))
     617                 :             :                         {
     618         [ #  # ]:           0 :                                 if (float8_lt(leftUpper, intervalsLower[i1].upper))
     619                 :           0 :                                         leftUpper = intervalsLower[i1].upper;
     620                 :           0 :                                 i1++;
     621                 :             :                         }
     622         [ #  # ]:           0 :                         if (i1 >= nentries)
     623                 :           0 :                                 break;
     624                 :           0 :                         rightLower = intervalsLower[i1].lower;
     625                 :             : 
     626                 :             :                         /*
     627                 :             :                          * Find count of intervals which anyway should be placed to the
     628                 :             :                          * left group.
     629                 :             :                          */
     630   [ #  #  #  # ]:           0 :                         while (i2 < nentries &&
     631                 :           0 :                                    float8_le(intervalsUpper[i2].upper, leftUpper))
     632                 :           0 :                                 i2++;
     633                 :             : 
     634                 :             :                         /*
     635                 :             :                          * Consider found split.
     636                 :             :                          */
     637                 :           0 :                         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                 :           0 :                 i1 = nentries - 1;
     645                 :           0 :                 i2 = nentries - 1;
     646                 :           0 :                 rightLower = intervalsLower[i1].upper;
     647                 :           0 :                 leftUpper = intervalsUpper[i2].upper;
     648                 :           0 :                 while (true)
     649                 :             :                 {
     650                 :             :                         /*
     651                 :             :                          * Find next upper bound of left group.
     652                 :             :                          */
     653   [ #  #  #  # ]:           0 :                         while (i2 >= 0 && float8_eq(leftUpper, intervalsUpper[i2].upper))
     654                 :             :                         {
     655         [ #  # ]:           0 :                                 if (float8_gt(rightLower, intervalsUpper[i2].lower))
     656                 :           0 :                                         rightLower = intervalsUpper[i2].lower;
     657                 :           0 :                                 i2--;
     658                 :             :                         }
     659         [ #  # ]:           0 :                         if (i2 < 0)
     660                 :           0 :                                 break;
     661                 :           0 :                         leftUpper = intervalsUpper[i2].upper;
     662                 :             : 
     663                 :             :                         /*
     664                 :             :                          * Find count of intervals which anyway should be placed to the
     665                 :             :                          * right group.
     666                 :             :                          */
     667   [ #  #  #  # ]:           0 :                         while (i1 >= 0 && float8_ge(intervalsLower[i1].lower, rightLower))
     668                 :           0 :                                 i1--;
     669                 :             : 
     670                 :             :                         /*
     671                 :             :                          * Consider found split.
     672                 :             :                          */
     673                 :           0 :                         g_box_consider_split(&context, dim,
     674                 :           0 :                                                                  rightLower, i1 + 1, leftUpper, i2 + 1);
     675                 :             :                 }
     676                 :           0 :         }
     677                 :             : 
     678                 :             :         /*
     679                 :             :          * If we failed to find any acceptable splits, use trivial split.
     680                 :             :          */
     681         [ #  # ]:           0 :         if (context.first)
     682                 :             :         {
     683                 :           0 :                 fallbackSplit(entryvec, v);
     684                 :           0 :                 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                 :           0 :         v->spl_left = (OffsetNumber *) palloc(nentries * sizeof(OffsetNumber));
     697                 :           0 :         v->spl_right = (OffsetNumber *) palloc(nentries * sizeof(OffsetNumber));
     698                 :           0 :         v->spl_nleft = 0;
     699                 :           0 :         v->spl_nright = 0;
     700                 :             : 
     701                 :             :         /* Allocate bounding boxes of left and right groups */
     702                 :           0 :         leftBox = palloc0_object(BOX);
     703                 :           0 :         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                 :           0 :         commonEntriesCount = 0;
     710                 :           0 :         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         [ #  # ]:           0 :         for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
     736                 :             :         {
     737                 :           0 :                 float8          lower,
     738                 :             :                                         upper;
     739                 :             : 
     740                 :             :                 /*
     741                 :             :                  * Get upper and lower bounds along selected axis.
     742                 :             :                  */
     743                 :           0 :                 box = DatumGetBoxP(entryvec->vector[i].key);
     744         [ #  # ]:           0 :                 if (context.dim == 0)
     745                 :             :                 {
     746                 :           0 :                         lower = box->low.x;
     747                 :           0 :                         upper = box->high.x;
     748                 :           0 :                 }
     749                 :             :                 else
     750                 :             :                 {
     751                 :           0 :                         lower = box->low.y;
     752                 :           0 :                         upper = box->high.y;
     753                 :             :                 }
     754                 :             : 
     755         [ #  # ]:           0 :                 if (float8_le(upper, context.leftUpper))
     756                 :             :                 {
     757                 :             :                         /* Fits to the left group */
     758         [ #  # ]:           0 :                         if (float8_ge(lower, context.rightLower))
     759                 :             :                         {
     760                 :             :                                 /* Fits also to the right group, so "common entry" */
     761                 :           0 :                                 commonEntries[commonEntriesCount++].index = i;
     762                 :           0 :                         }
     763                 :             :                         else
     764                 :             :                         {
     765                 :             :                                 /* Doesn't fit to the right group, so join to the left group */
     766         [ #  # ]:           0 :                                 PLACE_LEFT(box, i);
     767                 :             :                         }
     768                 :           0 :                 }
     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         [ #  # ]:           0 :                         Assert(float8_ge(lower, context.rightLower));
     777                 :             : 
     778                 :             :                         /* Doesn't fit to the left group, so join to the right group */
     779         [ #  # ]:           0 :                         PLACE_RIGHT(box, i);
     780                 :             :                 }
     781                 :           0 :         }
     782                 :             : 
     783                 :             :         /*
     784                 :             :          * Distribute "common entries", if any.
     785                 :             :          */
     786         [ #  # ]:           0 :         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                 :           0 :                                                                                                         box_penalty(rightBox, box)));
     803                 :           0 :                 }
     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                 :           0 :                 }
     835                 :           0 :         }
     836                 :             : 
     837                 :           0 :         v->spl_ldatum = PointerGetDatum(leftBox);
     838                 :           0 :         v->spl_rdatum = PointerGetDatum(rightBox);
     839                 :           0 :         PG_RETURN_POINTER(v);
     840                 :           0 : }
     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                 :           0 : gist_box_same(PG_FUNCTION_ARGS)
     854                 :             : {
     855                 :           0 :         BOX                *b1 = PG_GETARG_BOX_P(0);
     856                 :           0 :         BOX                *b2 = PG_GETARG_BOX_P(1);
     857                 :           0 :         bool       *result = (bool *) PG_GETARG_POINTER(2);
     858                 :             : 
     859   [ #  #  #  # ]:           0 :         if (b1 && b2)
     860         [ #  # ]:           0 :                 *result = (float8_eq(b1->low.x, b2->low.x) &&
     861         [ #  # ]:           0 :                                    float8_eq(b1->low.y, b2->low.y) &&
     862         [ #  # ]:           0 :                                    float8_eq(b1->high.x, b2->high.x) &&
     863                 :           0 :                                    float8_eq(b1->high.y, b2->high.y));
     864                 :             :         else
     865         [ #  # ]:           0 :                 *result = (b1 == NULL && b2 == NULL);
     866                 :           0 :         PG_RETURN_POINTER(result);
     867                 :           0 : }
     868                 :             : 
     869                 :             : /*
     870                 :             :  * Leaf-level consistency for boxes: just apply the query operator
     871                 :             :  */
     872                 :             : static bool
     873                 :           0 : gist_box_leaf_consistent(BOX *key, BOX *query, StrategyNumber strategy)
     874                 :             : {
     875                 :           0 :         bool            retval;
     876                 :             : 
     877   [ #  #  #  #  :           0 :         switch (strategy)
          #  #  #  #  #  
             #  #  #  # ]
     878                 :             :         {
     879                 :             :                 case RTLeftStrategyNumber:
     880                 :           0 :                         retval = DatumGetBool(DirectFunctionCall2(box_left,
     881                 :             :                                                                                                           PointerGetDatum(key),
     882                 :             :                                                                                                           PointerGetDatum(query)));
     883                 :           0 :                         break;
     884                 :             :                 case RTOverLeftStrategyNumber:
     885                 :           0 :                         retval = DatumGetBool(DirectFunctionCall2(box_overleft,
     886                 :             :                                                                                                           PointerGetDatum(key),
     887                 :             :                                                                                                           PointerGetDatum(query)));
     888                 :           0 :                         break;
     889                 :             :                 case RTOverlapStrategyNumber:
     890                 :           0 :                         retval = DatumGetBool(DirectFunctionCall2(box_overlap,
     891                 :             :                                                                                                           PointerGetDatum(key),
     892                 :             :                                                                                                           PointerGetDatum(query)));
     893                 :           0 :                         break;
     894                 :             :                 case RTOverRightStrategyNumber:
     895                 :           0 :                         retval = DatumGetBool(DirectFunctionCall2(box_overright,
     896                 :             :                                                                                                           PointerGetDatum(key),
     897                 :             :                                                                                                           PointerGetDatum(query)));
     898                 :           0 :                         break;
     899                 :             :                 case RTRightStrategyNumber:
     900                 :           0 :                         retval = DatumGetBool(DirectFunctionCall2(box_right,
     901                 :             :                                                                                                           PointerGetDatum(key),
     902                 :             :                                                                                                           PointerGetDatum(query)));
     903                 :           0 :                         break;
     904                 :             :                 case RTSameStrategyNumber:
     905                 :           0 :                         retval = DatumGetBool(DirectFunctionCall2(box_same,
     906                 :             :                                                                                                           PointerGetDatum(key),
     907                 :             :                                                                                                           PointerGetDatum(query)));
     908                 :           0 :                         break;
     909                 :             :                 case RTContainsStrategyNumber:
     910                 :           0 :                         retval = DatumGetBool(DirectFunctionCall2(box_contain,
     911                 :             :                                                                                                           PointerGetDatum(key),
     912                 :             :                                                                                                           PointerGetDatum(query)));
     913                 :           0 :                         break;
     914                 :             :                 case RTContainedByStrategyNumber:
     915                 :           0 :                         retval = DatumGetBool(DirectFunctionCall2(box_contained,
     916                 :             :                                                                                                           PointerGetDatum(key),
     917                 :             :                                                                                                           PointerGetDatum(query)));
     918                 :           0 :                         break;
     919                 :             :                 case RTOverBelowStrategyNumber:
     920                 :           0 :                         retval = DatumGetBool(DirectFunctionCall2(box_overbelow,
     921                 :             :                                                                                                           PointerGetDatum(key),
     922                 :             :                                                                                                           PointerGetDatum(query)));
     923                 :           0 :                         break;
     924                 :             :                 case RTBelowStrategyNumber:
     925                 :           0 :                         retval = DatumGetBool(DirectFunctionCall2(box_below,
     926                 :             :                                                                                                           PointerGetDatum(key),
     927                 :             :                                                                                                           PointerGetDatum(query)));
     928                 :           0 :                         break;
     929                 :             :                 case RTAboveStrategyNumber:
     930                 :           0 :                         retval = DatumGetBool(DirectFunctionCall2(box_above,
     931                 :             :                                                                                                           PointerGetDatum(key),
     932                 :             :                                                                                                           PointerGetDatum(query)));
     933                 :           0 :                         break;
     934                 :             :                 case RTOverAboveStrategyNumber:
     935                 :           0 :                         retval = DatumGetBool(DirectFunctionCall2(box_overabove,
     936                 :             :                                                                                                           PointerGetDatum(key),
     937                 :             :                                                                                                           PointerGetDatum(query)));
     938                 :           0 :                         break;
     939                 :             :                 default:
     940   [ #  #  #  # ]:           0 :                         elog(ERROR, "unrecognized strategy number: %d", strategy);
     941                 :           0 :                         retval = false;         /* keep compiler quiet */
     942                 :           0 :                         break;
     943                 :             :         }
     944                 :           0 :         return retval;
     945                 :           0 : }
     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                 :           0 : rtree_internal_consistent(BOX *key, BOX *query, StrategyNumber strategy)
     959                 :             : {
     960                 :           0 :         bool            retval;
     961                 :             : 
     962   [ #  #  #  #  :           0 :         switch (strategy)
          #  #  #  #  #  
                #  #  # ]
     963                 :             :         {
     964                 :             :                 case RTLeftStrategyNumber:
     965                 :           0 :                         retval = !DatumGetBool(DirectFunctionCall2(box_overright,
     966                 :             :                                                                                                            PointerGetDatum(key),
     967                 :             :                                                                                                            PointerGetDatum(query)));
     968                 :           0 :                         break;
     969                 :             :                 case RTOverLeftStrategyNumber:
     970                 :           0 :                         retval = !DatumGetBool(DirectFunctionCall2(box_right,
     971                 :             :                                                                                                            PointerGetDatum(key),
     972                 :             :                                                                                                            PointerGetDatum(query)));
     973                 :           0 :                         break;
     974                 :             :                 case RTOverlapStrategyNumber:
     975                 :           0 :                         retval = DatumGetBool(DirectFunctionCall2(box_overlap,
     976                 :             :                                                                                                           PointerGetDatum(key),
     977                 :             :                                                                                                           PointerGetDatum(query)));
     978                 :           0 :                         break;
     979                 :             :                 case RTOverRightStrategyNumber:
     980                 :           0 :                         retval = !DatumGetBool(DirectFunctionCall2(box_left,
     981                 :             :                                                                                                            PointerGetDatum(key),
     982                 :             :                                                                                                            PointerGetDatum(query)));
     983                 :           0 :                         break;
     984                 :             :                 case RTRightStrategyNumber:
     985                 :           0 :                         retval = !DatumGetBool(DirectFunctionCall2(box_overleft,
     986                 :             :                                                                                                            PointerGetDatum(key),
     987                 :             :                                                                                                            PointerGetDatum(query)));
     988                 :           0 :                         break;
     989                 :             :                 case RTSameStrategyNumber:
     990                 :             :                 case RTContainsStrategyNumber:
     991                 :           0 :                         retval = DatumGetBool(DirectFunctionCall2(box_contain,
     992                 :             :                                                                                                           PointerGetDatum(key),
     993                 :             :                                                                                                           PointerGetDatum(query)));
     994                 :           0 :                         break;
     995                 :             :                 case RTContainedByStrategyNumber:
     996                 :           0 :                         retval = DatumGetBool(DirectFunctionCall2(box_overlap,
     997                 :             :                                                                                                           PointerGetDatum(key),
     998                 :             :                                                                                                           PointerGetDatum(query)));
     999                 :           0 :                         break;
    1000                 :             :                 case RTOverBelowStrategyNumber:
    1001                 :           0 :                         retval = !DatumGetBool(DirectFunctionCall2(box_above,
    1002                 :             :                                                                                                            PointerGetDatum(key),
    1003                 :             :                                                                                                            PointerGetDatum(query)));
    1004                 :           0 :                         break;
    1005                 :             :                 case RTBelowStrategyNumber:
    1006                 :           0 :                         retval = !DatumGetBool(DirectFunctionCall2(box_overabove,
    1007                 :             :                                                                                                            PointerGetDatum(key),
    1008                 :             :                                                                                                            PointerGetDatum(query)));
    1009                 :           0 :                         break;
    1010                 :             :                 case RTAboveStrategyNumber:
    1011                 :           0 :                         retval = !DatumGetBool(DirectFunctionCall2(box_overbelow,
    1012                 :             :                                                                                                            PointerGetDatum(key),
    1013                 :             :                                                                                                            PointerGetDatum(query)));
    1014                 :           0 :                         break;
    1015                 :             :                 case RTOverAboveStrategyNumber:
    1016                 :           0 :                         retval = !DatumGetBool(DirectFunctionCall2(box_below,
    1017                 :             :                                                                                                            PointerGetDatum(key),
    1018                 :             :                                                                                                            PointerGetDatum(query)));
    1019                 :           0 :                         break;
    1020                 :             :                 default:
    1021   [ #  #  #  # ]:           0 :                         elog(ERROR, "unrecognized strategy number: %d", strategy);
    1022                 :           0 :                         retval = false;         /* keep compiler quiet */
    1023                 :           0 :                         break;
    1024                 :             :         }
    1025                 :           0 :         return retval;
    1026                 :           0 : }
    1027                 :             : 
    1028                 :             : /**************************************************
    1029                 :             :  * Polygon ops
    1030                 :             :  **************************************************/
    1031                 :             : 
    1032                 :             : /*
    1033                 :             :  * GiST compress for polygons: represent a polygon by its bounding box
    1034                 :             :  */
    1035                 :             : Datum
    1036                 :           0 : gist_poly_compress(PG_FUNCTION_ARGS)
    1037                 :             : {
    1038                 :           0 :         GISTENTRY  *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
    1039                 :           0 :         GISTENTRY  *retval;
    1040                 :             : 
    1041         [ #  # ]:           0 :         if (entry->leafkey)
    1042                 :             :         {
    1043                 :           0 :                 POLYGON    *in = DatumGetPolygonP(entry->key);
    1044                 :           0 :                 BOX                *r;
    1045                 :             : 
    1046                 :           0 :                 r = palloc_object(BOX);
    1047                 :           0 :                 memcpy(r, &(in->boundbox), sizeof(BOX));
    1048                 :             : 
    1049                 :           0 :                 retval = palloc_object(GISTENTRY);
    1050                 :           0 :                 gistentryinit(*retval, PointerGetDatum(r),
    1051                 :             :                                           entry->rel, entry->page,
    1052                 :             :                                           entry->offset, false);
    1053                 :           0 :         }
    1054                 :             :         else
    1055                 :           0 :                 retval = entry;
    1056                 :           0 :         PG_RETURN_POINTER(retval);
    1057                 :           0 : }
    1058                 :             : 
    1059                 :             : /*
    1060                 :             :  * The GiST Consistent method for polygons
    1061                 :             :  */
    1062                 :             : Datum
    1063                 :           0 : gist_poly_consistent(PG_FUNCTION_ARGS)
    1064                 :             : {
    1065                 :           0 :         GISTENTRY  *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
    1066                 :           0 :         POLYGON    *query = PG_GETARG_POLYGON_P(1);
    1067                 :           0 :         StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2);
    1068                 :             : #ifdef NOT_USED
    1069                 :             :         Oid                     subtype = PG_GETARG_OID(3);
    1070                 :             : #endif
    1071                 :           0 :         bool       *recheck = (bool *) PG_GETARG_POINTER(4);
    1072                 :           0 :         bool            result;
    1073                 :             : 
    1074                 :             :         /* All cases served by this function are inexact */
    1075                 :           0 :         *recheck = true;
    1076                 :             : 
    1077   [ #  #  #  # ]:           0 :         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                 :           0 :         result = rtree_internal_consistent(DatumGetBoxP(entry->key),
    1086                 :           0 :                                                                            &(query->boundbox), strategy);
    1087                 :             : 
    1088                 :             :         /* Avoid memory leak if supplied poly is toasted */
    1089         [ #  # ]:           0 :         PG_FREE_IF_COPY(query, 1);
    1090                 :             : 
    1091                 :           0 :         PG_RETURN_BOOL(result);
    1092                 :           0 : }
    1093                 :             : 
    1094                 :             : /**************************************************
    1095                 :             :  * Circle ops
    1096                 :             :  **************************************************/
    1097                 :             : 
    1098                 :             : /*
    1099                 :             :  * GiST compress for circles: represent a circle by its bounding box
    1100                 :             :  */
    1101                 :             : Datum
    1102                 :           0 : gist_circle_compress(PG_FUNCTION_ARGS)
    1103                 :             : {
    1104                 :           0 :         GISTENTRY  *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
    1105                 :           0 :         GISTENTRY  *retval;
    1106                 :             : 
    1107         [ #  # ]:           0 :         if (entry->leafkey)
    1108                 :             :         {
    1109                 :           0 :                 CIRCLE     *in = DatumGetCircleP(entry->key);
    1110                 :           0 :                 BOX                *r;
    1111                 :             : 
    1112                 :           0 :                 r = palloc_object(BOX);
    1113                 :           0 :                 r->high.x = float8_pl(in->center.x, in->radius);
    1114                 :           0 :                 r->low.x = float8_mi(in->center.x, in->radius);
    1115                 :           0 :                 r->high.y = float8_pl(in->center.y, in->radius);
    1116                 :           0 :                 r->low.y = float8_mi(in->center.y, in->radius);
    1117                 :             : 
    1118                 :           0 :                 retval = palloc_object(GISTENTRY);
    1119                 :           0 :                 gistentryinit(*retval, PointerGetDatum(r),
    1120                 :             :                                           entry->rel, entry->page,
    1121                 :             :                                           entry->offset, false);
    1122                 :           0 :         }
    1123                 :             :         else
    1124                 :           0 :                 retval = entry;
    1125                 :           0 :         PG_RETURN_POINTER(retval);
    1126                 :           0 : }
    1127                 :             : 
    1128                 :             : /*
    1129                 :             :  * The GiST Consistent method for circles
    1130                 :             :  */
    1131                 :             : Datum
    1132                 :           0 : gist_circle_consistent(PG_FUNCTION_ARGS)
    1133                 :             : {
    1134                 :           0 :         GISTENTRY  *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
    1135                 :           0 :         CIRCLE     *query = PG_GETARG_CIRCLE_P(1);
    1136                 :           0 :         StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2);
    1137                 :             : #ifdef NOT_USED
    1138                 :             :         Oid                     subtype = PG_GETARG_OID(3);
    1139                 :             : #endif
    1140                 :           0 :         bool       *recheck = (bool *) PG_GETARG_POINTER(4);
    1141                 :           0 :         BOX                     bbox;
    1142                 :           0 :         bool            result;
    1143                 :             : 
    1144                 :             :         /* All cases served by this function are inexact */
    1145                 :           0 :         *recheck = true;
    1146                 :             : 
    1147   [ #  #  #  # ]:           0 :         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                 :           0 :         bbox.high.x = float8_pl(query->center.x, query->radius);
    1156                 :           0 :         bbox.low.x = float8_mi(query->center.x, query->radius);
    1157                 :           0 :         bbox.high.y = float8_pl(query->center.y, query->radius);
    1158                 :           0 :         bbox.low.y = float8_mi(query->center.y, query->radius);
    1159                 :             : 
    1160                 :           0 :         result = rtree_internal_consistent(DatumGetBoxP(entry->key),
    1161                 :           0 :                                                                            &bbox, strategy);
    1162                 :             : 
    1163                 :           0 :         PG_RETURN_BOOL(result);
    1164                 :           0 : }
    1165                 :             : 
    1166                 :             : /**************************************************
    1167                 :             :  * Point ops
    1168                 :             :  **************************************************/
    1169                 :             : 
    1170                 :             : Datum
    1171                 :           0 : gist_point_compress(PG_FUNCTION_ARGS)
    1172                 :             : {
    1173                 :           0 :         GISTENTRY  *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
    1174                 :             : 
    1175         [ #  # ]:           0 :         if (entry->leafkey)                  /* Point, actually */
    1176                 :             :         {
    1177                 :           0 :                 BOX                *box = palloc_object(BOX);
    1178                 :           0 :                 Point      *point = DatumGetPointP(entry->key);
    1179                 :           0 :                 GISTENTRY  *retval = palloc_object(GISTENTRY);
    1180                 :             : 
    1181                 :           0 :                 box->high = box->low = *point;
    1182                 :             : 
    1183                 :           0 :                 gistentryinit(*retval, BoxPGetDatum(box),
    1184                 :             :                                           entry->rel, entry->page, entry->offset, false);
    1185                 :             : 
    1186                 :           0 :                 PG_RETURN_POINTER(retval);
    1187                 :           0 :         }
    1188                 :             : 
    1189                 :           0 :         PG_RETURN_POINTER(entry);
    1190                 :           0 : }
    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                 :           0 : gist_point_fetch(PG_FUNCTION_ARGS)
    1200                 :             : {
    1201                 :           0 :         GISTENTRY  *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
    1202                 :           0 :         BOX                *in = DatumGetBoxP(entry->key);
    1203                 :           0 :         Point      *r;
    1204                 :           0 :         GISTENTRY  *retval;
    1205                 :             : 
    1206                 :           0 :         retval = palloc_object(GISTENTRY);
    1207                 :             : 
    1208                 :           0 :         r = palloc_object(Point);
    1209                 :           0 :         r->x = in->high.x;
    1210                 :           0 :         r->y = in->high.y;
    1211                 :           0 :         gistentryinit(*retval, PointerGetDatum(r),
    1212                 :             :                                   entry->rel, entry->page,
    1213                 :             :                                   entry->offset, false);
    1214                 :             : 
    1215                 :           0 :         PG_RETURN_POINTER(retval);
    1216                 :           0 : }
    1217                 :             : 
    1218                 :             : 
    1219                 :             : #define point_point_distance(p1,p2) \
    1220                 :             :         DatumGetFloat8(DirectFunctionCall2(point_distance, \
    1221                 :             :                                                                            PointPGetDatum(p1), PointPGetDatum(p2)))
    1222                 :             : 
    1223                 :             : static float8
    1224                 :           0 : computeDistance(bool isLeaf, BOX *box, Point *point)
    1225                 :             : {
    1226                 :           0 :         float8          result = 0.0;
    1227                 :             : 
    1228         [ #  # ]:           0 :         if (isLeaf)
    1229                 :             :         {
    1230                 :             :                 /* simple point to point distance */
    1231                 :           0 :                 result = point_point_distance(point, &box->low);
    1232                 :           0 :         }
    1233   [ #  #  #  # ]:           0 :         else if (point->x <= box->high.x && point->x >= box->low.x &&
    1234   [ #  #  #  # ]:           0 :                          point->y <= box->high.y && point->y >= box->low.y)
    1235                 :             :         {
    1236                 :             :                 /* point inside the box */
    1237                 :           0 :                 result = 0.0;
    1238                 :           0 :         }
    1239   [ #  #  #  # ]:           0 :         else if (point->x <= box->high.x && point->x >= box->low.x)
    1240                 :             :         {
    1241                 :             :                 /* point is over or below box */
    1242         [ #  # ]:           0 :                 Assert(box->low.y <= box->high.y);
    1243         [ #  # ]:           0 :                 if (point->y > box->high.y)
    1244                 :           0 :                         result = float8_mi(point->y, box->high.y);
    1245         [ #  # ]:           0 :                 else if (point->y < box->low.y)
    1246                 :           0 :                         result = float8_mi(box->low.y, point->y);
    1247                 :             :                 else
    1248   [ #  #  #  # ]:           0 :                         elog(ERROR, "inconsistent point values");
    1249                 :           0 :         }
    1250   [ #  #  #  # ]:           0 :         else if (point->y <= box->high.y && point->y >= box->low.y)
    1251                 :             :         {
    1252                 :             :                 /* point is to left or right of box */
    1253         [ #  # ]:           0 :                 Assert(box->low.x <= box->high.x);
    1254         [ #  # ]:           0 :                 if (point->x > box->high.x)
    1255                 :           0 :                         result = float8_mi(point->x, box->high.x);
    1256         [ #  # ]:           0 :                 else if (point->x < box->low.x)
    1257                 :           0 :                         result = float8_mi(box->low.x, point->x);
    1258                 :             :                 else
    1259   [ #  #  #  # ]:           0 :                         elog(ERROR, "inconsistent point values");
    1260                 :           0 :         }
    1261                 :             :         else
    1262                 :             :         {
    1263                 :             :                 /* closest point will be a vertex */
    1264                 :           0 :                 Point           p;
    1265                 :           0 :                 float8          subresult;
    1266                 :             : 
    1267                 :           0 :                 result = point_point_distance(point, &box->low);
    1268                 :             : 
    1269                 :           0 :                 subresult = point_point_distance(point, &box->high);
    1270         [ #  # ]:           0 :                 if (result > subresult)
    1271                 :           0 :                         result = subresult;
    1272                 :             : 
    1273                 :           0 :                 p.x = box->low.x;
    1274                 :           0 :                 p.y = box->high.y;
    1275                 :           0 :                 subresult = point_point_distance(point, &p);
    1276         [ #  # ]:           0 :                 if (result > subresult)
    1277                 :           0 :                         result = subresult;
    1278                 :             : 
    1279                 :           0 :                 p.x = box->high.x;
    1280                 :           0 :                 p.y = box->low.y;
    1281                 :           0 :                 subresult = point_point_distance(point, &p);
    1282         [ #  # ]:           0 :                 if (result > subresult)
    1283                 :           0 :                         result = subresult;
    1284                 :           0 :         }
    1285                 :             : 
    1286                 :           0 :         return result;
    1287                 :           0 : }
    1288                 :             : 
    1289                 :             : static bool
    1290                 :           0 : gist_point_consistent_internal(StrategyNumber strategy,
    1291                 :             :                                                            bool isLeaf, BOX *key, Point *query)
    1292                 :             : {
    1293                 :           0 :         bool            result = false;
    1294                 :             : 
    1295   [ #  #  #  #  :           0 :         switch (strategy)
                   #  # ]
    1296                 :             :         {
    1297                 :             :                 case RTLeftStrategyNumber:
    1298                 :           0 :                         result = FPlt(key->low.x, query->x);
    1299                 :           0 :                         break;
    1300                 :             :                 case RTRightStrategyNumber:
    1301                 :           0 :                         result = FPgt(key->high.x, query->x);
    1302                 :           0 :                         break;
    1303                 :             :                 case RTAboveStrategyNumber:
    1304                 :           0 :                         result = FPgt(key->high.y, query->y);
    1305                 :           0 :                         break;
    1306                 :             :                 case RTBelowStrategyNumber:
    1307                 :           0 :                         result = FPlt(key->low.y, query->y);
    1308                 :           0 :                         break;
    1309                 :             :                 case RTSameStrategyNumber:
    1310         [ #  # ]:           0 :                         if (isLeaf)
    1311                 :             :                         {
    1312                 :             :                                 /* key.high must equal key.low, so we can disregard it */
    1313         [ #  # ]:           0 :                                 result = (FPeq(key->low.x, query->x) &&
    1314                 :           0 :                                                   FPeq(key->low.y, query->y));
    1315                 :           0 :                         }
    1316                 :             :                         else
    1317                 :             :                         {
    1318         [ #  # ]:           0 :                                 result = (FPle(query->x, key->high.x) &&
    1319         [ #  # ]:           0 :                                                   FPge(query->x, key->low.x) &&
    1320         [ #  # ]:           0 :                                                   FPle(query->y, key->high.y) &&
    1321                 :           0 :                                                   FPge(query->y, key->low.y));
    1322                 :             :                         }
    1323                 :           0 :                         break;
    1324                 :             :                 default:
    1325   [ #  #  #  # ]:           0 :                         elog(ERROR, "unrecognized strategy number: %d", strategy);
    1326                 :           0 :                         result = false;         /* keep compiler quiet */
    1327                 :           0 :                         break;
    1328                 :             :         }
    1329                 :             : 
    1330                 :           0 :         return result;
    1331                 :           0 : }
    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                 :           0 : gist_point_consistent(PG_FUNCTION_ARGS)
    1341                 :             : {
    1342                 :           0 :         GISTENTRY  *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
    1343                 :           0 :         StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2);
    1344                 :           0 :         bool       *recheck = (bool *) PG_GETARG_POINTER(4);
    1345                 :           0 :         bool            result;
    1346                 :           0 :         StrategyNumber strategyGroup;
    1347                 :             : 
    1348                 :             :         /*
    1349                 :             :          * We have to remap these strategy numbers to get this klugy
    1350                 :             :          * classification logic to work.
    1351                 :             :          */
    1352         [ #  # ]:           0 :         if (strategy == RTOldBelowStrategyNumber)
    1353                 :           0 :                 strategy = RTBelowStrategyNumber;
    1354         [ #  # ]:           0 :         else if (strategy == RTOldAboveStrategyNumber)
    1355                 :           0 :                 strategy = RTAboveStrategyNumber;
    1356                 :             : 
    1357                 :           0 :         strategyGroup = strategy / GeoStrategyNumberOffset;
    1358   [ #  #  #  #  :           0 :         switch (strategyGroup)
                      # ]
    1359                 :             :         {
    1360                 :             :                 case PointStrategyNumberGroup:
    1361                 :           0 :                         result = gist_point_consistent_internal(strategy % GeoStrategyNumberOffset,
    1362                 :           0 :                                                                                                         GIST_LEAF(entry),
    1363                 :           0 :                                                                                                         DatumGetBoxP(entry->key),
    1364                 :           0 :                                                                                                         PG_GETARG_POINT_P(1));
    1365                 :           0 :                         *recheck = false;
    1366                 :           0 :                         break;
    1367                 :             :                 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                 :           0 :                                 BOX                *query,
    1382                 :             :                                                    *key;
    1383                 :             : 
    1384                 :           0 :                                 query = PG_GETARG_BOX_P(1);
    1385                 :           0 :                                 key = DatumGetBoxP(entry->key);
    1386                 :             : 
    1387         [ #  # ]:           0 :                                 result = (key->high.x >= query->low.x &&
    1388         [ #  # ]:           0 :                                                   key->low.x <= query->high.x &&
    1389         [ #  # ]:           0 :                                                   key->high.y >= query->low.y &&
    1390                 :           0 :                                                   key->low.y <= query->high.y);
    1391                 :           0 :                                 *recheck = false;
    1392                 :           0 :                         }
    1393                 :           0 :                         break;
    1394                 :             :                 case PolygonStrategyNumberGroup:
    1395                 :             :                         {
    1396                 :           0 :                                 POLYGON    *query = PG_GETARG_POLYGON_P(1);
    1397                 :             : 
    1398                 :           0 :                                 result = DatumGetBool(DirectFunctionCall5(gist_poly_consistent,
    1399                 :             :                                                                                                                   PointerGetDatum(entry),
    1400                 :             :                                                                                                                   PolygonPGetDatum(query),
    1401                 :             :                                                                                                                   Int16GetDatum(RTOverlapStrategyNumber),
    1402                 :             :                                                                                                                   0, PointerGetDatum(recheck)));
    1403                 :             : 
    1404   [ #  #  #  # ]:           0 :                                 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                 :           0 :                                         BOX                *box = DatumGetBoxP(entry->key);
    1411                 :             : 
    1412         [ #  # ]:           0 :                                         Assert(box->high.x == box->low.x
    1413                 :             :                                                    && box->high.y == box->low.y);
    1414                 :           0 :                                         result = DatumGetBool(DirectFunctionCall2(poly_contain_pt,
    1415                 :             :                                                                                                                           PolygonPGetDatum(query),
    1416                 :             :                                                                                                                           PointPGetDatum(&box->high)));
    1417                 :           0 :                                         *recheck = false;
    1418                 :           0 :                                 }
    1419                 :           0 :                         }
    1420                 :           0 :                         break;
    1421                 :             :                 case CircleStrategyNumberGroup:
    1422                 :             :                         {
    1423                 :           0 :                                 CIRCLE     *query = PG_GETARG_CIRCLE_P(1);
    1424                 :             : 
    1425                 :           0 :                                 result = DatumGetBool(DirectFunctionCall5(gist_circle_consistent,
    1426                 :             :                                                                                                                   PointerGetDatum(entry),
    1427                 :             :                                                                                                                   CirclePGetDatum(query),
    1428                 :             :                                                                                                                   Int16GetDatum(RTOverlapStrategyNumber),
    1429                 :             :                                                                                                                   0, PointerGetDatum(recheck)));
    1430                 :             : 
    1431   [ #  #  #  # ]:           0 :                                 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                 :           0 :                                         BOX                *box = DatumGetBoxP(entry->key);
    1438                 :             : 
    1439         [ #  # ]:           0 :                                         Assert(box->high.x == box->low.x
    1440                 :             :                                                    && box->high.y == box->low.y);
    1441                 :           0 :                                         result = DatumGetBool(DirectFunctionCall2(circle_contain_pt,
    1442                 :             :                                                                                                                           CirclePGetDatum(query),
    1443                 :             :                                                                                                                           PointPGetDatum(&box->high)));
    1444                 :           0 :                                         *recheck = false;
    1445                 :           0 :                                 }
    1446                 :           0 :                         }
    1447                 :           0 :                         break;
    1448                 :             :                 default:
    1449   [ #  #  #  # ]:           0 :                         elog(ERROR, "unrecognized strategy number: %d", strategy);
    1450                 :           0 :                         result = false;         /* keep compiler quiet */
    1451                 :           0 :                         break;
    1452                 :             :         }
    1453                 :             : 
    1454                 :           0 :         PG_RETURN_BOOL(result);
    1455                 :           0 : }
    1456                 :             : 
    1457                 :             : Datum
    1458                 :           0 : gist_point_distance(PG_FUNCTION_ARGS)
    1459                 :             : {
    1460                 :           0 :         GISTENTRY  *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
    1461                 :           0 :         StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2);
    1462                 :           0 :         float8          distance;
    1463                 :           0 :         StrategyNumber strategyGroup = strategy / GeoStrategyNumberOffset;
    1464                 :             : 
    1465         [ #  # ]:           0 :         switch (strategyGroup)
    1466                 :             :         {
    1467                 :             :                 case PointStrategyNumberGroup:
    1468                 :           0 :                         distance = computeDistance(GIST_LEAF(entry),
    1469                 :           0 :                                                                            DatumGetBoxP(entry->key),
    1470                 :           0 :                                                                            PG_GETARG_POINT_P(1));
    1471                 :           0 :                         break;
    1472                 :             :                 default:
    1473   [ #  #  #  # ]:           0 :                         elog(ERROR, "unrecognized strategy number: %d", strategy);
    1474                 :           0 :                         distance = 0.0;         /* keep compiler quiet */
    1475                 :           0 :                         break;
    1476                 :             :         }
    1477                 :             : 
    1478                 :           0 :         PG_RETURN_FLOAT8(distance);
    1479                 :           0 : }
    1480                 :             : 
    1481                 :             : static float8
    1482                 :           0 : gist_bbox_distance(GISTENTRY *entry, Datum query, StrategyNumber strategy)
    1483                 :             : {
    1484                 :           0 :         float8          distance;
    1485                 :           0 :         StrategyNumber strategyGroup = strategy / GeoStrategyNumberOffset;
    1486                 :             : 
    1487         [ #  # ]:           0 :         switch (strategyGroup)
    1488                 :             :         {
    1489                 :             :                 case PointStrategyNumberGroup:
    1490                 :           0 :                         distance = computeDistance(false,
    1491                 :           0 :                                                                            DatumGetBoxP(entry->key),
    1492                 :           0 :                                                                            DatumGetPointP(query));
    1493                 :           0 :                         break;
    1494                 :             :                 default:
    1495   [ #  #  #  # ]:           0 :                         elog(ERROR, "unrecognized strategy number: %d", strategy);
    1496                 :           0 :                         distance = 0.0;         /* keep compiler quiet */
    1497                 :           0 :         }
    1498                 :             : 
    1499                 :           0 :         return distance;
    1500                 :           0 : }
    1501                 :             : 
    1502                 :             : Datum
    1503                 :           0 : gist_box_distance(PG_FUNCTION_ARGS)
    1504                 :             : {
    1505                 :           0 :         GISTENTRY  *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
    1506                 :           0 :         Datum           query = PG_GETARG_DATUM(1);
    1507                 :           0 :         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                 :           0 :         float8          distance;
    1513                 :             : 
    1514                 :           0 :         distance = gist_bbox_distance(entry, query, strategy);
    1515                 :             : 
    1516                 :           0 :         PG_RETURN_FLOAT8(distance);
    1517                 :           0 : }
    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                 :           0 : gist_circle_distance(PG_FUNCTION_ARGS)
    1531                 :             : {
    1532                 :           0 :         GISTENTRY  *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
    1533                 :           0 :         Datum           query = PG_GETARG_DATUM(1);
    1534                 :           0 :         StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2);
    1535                 :             : #ifdef NOT_USED
    1536                 :             :         Oid                     subtype = PG_GETARG_OID(3);
    1537                 :             : #endif
    1538                 :           0 :         bool       *recheck = (bool *) PG_GETARG_POINTER(4);
    1539                 :           0 :         float8          distance;
    1540                 :             : 
    1541                 :           0 :         distance = gist_bbox_distance(entry, query, strategy);
    1542                 :           0 :         *recheck = true;
    1543                 :             : 
    1544                 :           0 :         PG_RETURN_FLOAT8(distance);
    1545                 :           0 : }
    1546                 :             : 
    1547                 :             : Datum
    1548                 :           0 : gist_poly_distance(PG_FUNCTION_ARGS)
    1549                 :             : {
    1550                 :           0 :         GISTENTRY  *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
    1551                 :           0 :         Datum           query = PG_GETARG_DATUM(1);
    1552                 :           0 :         StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2);
    1553                 :             : #ifdef NOT_USED
    1554                 :             :         Oid                     subtype = PG_GETARG_OID(3);
    1555                 :             : #endif
    1556                 :           0 :         bool       *recheck = (bool *) PG_GETARG_POINTER(4);
    1557                 :           0 :         float8          distance;
    1558                 :             : 
    1559                 :           0 :         distance = gist_bbox_distance(entry, query, strategy);
    1560                 :           0 :         *recheck = true;
    1561                 :             : 
    1562                 :           0 :         PG_RETURN_FLOAT8(distance);
    1563                 :           0 : }
    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                 :           0 : point_zorder_internal(float4 x, float4 y)
    1582                 :             : {
    1583                 :           0 :         uint32          ix = ieee_float32_to_uint32(x);
    1584                 :           0 :         uint32          iy = ieee_float32_to_uint32(y);
    1585                 :             : 
    1586                 :             :         /* Interleave the bits */
    1587                 :           0 :         return part_bits32_by2(ix) | (part_bits32_by2(iy) << 1);
    1588                 :           0 : }
    1589                 :             : 
    1590                 :             : /* Interleave 32 bits with zeroes */
    1591                 :             : static uint64
    1592                 :           0 : part_bits32_by2(uint32 x)
    1593                 :             : {
    1594                 :           0 :         uint64          n = x;
    1595                 :             : 
    1596                 :           0 :         n = (n | (n << 16)) & UINT64CONST(0x0000FFFF0000FFFF);
    1597                 :           0 :         n = (n | (n << 8)) & UINT64CONST(0x00FF00FF00FF00FF);
    1598                 :           0 :         n = (n | (n << 4)) & UINT64CONST(0x0F0F0F0F0F0F0F0F);
    1599                 :           0 :         n = (n | (n << 2)) & UINT64CONST(0x3333333333333333);
    1600                 :           0 :         n = (n | (n << 1)) & UINT64CONST(0x5555555555555555);
    1601                 :             : 
    1602                 :           0 :         return n;
    1603                 :           0 : }
    1604                 :             : 
    1605                 :             : /*
    1606                 :             :  * Convert a 32-bit IEEE float to uint32 in a way that preserves the ordering
    1607                 :             :  */
    1608                 :             : static uint32
    1609                 :           0 : 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   [ #  #  #  #  :           0 :         if (isnan(f))
                   #  # ]
    1652                 :           0 :                 return 0xFFFFFFFF;
    1653                 :             :         else
    1654                 :             :         {
    1655                 :           0 :                 union
    1656                 :             :                 {
    1657                 :             :                         float           f;
    1658                 :             :                         uint32          i;
    1659                 :             :                 }                       u;
    1660                 :             : 
    1661                 :           0 :                 u.f = f;
    1662                 :             : 
    1663                 :             :                 /* Check the sign bit */
    1664         [ #  # ]:           0 :                 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         [ #  # ]:           0 :                         Assert(f <= 0);              /* can be -0 */
    1671                 :           0 :                         u.i ^= 0xFFFFFFFF;
    1672                 :           0 :                 }
    1673                 :             :                 else
    1674                 :             :                 {
    1675                 :             :                         /* Map the positive value (or 0) to range 80000000-FFFFFFFF */
    1676                 :           0 :                         u.i |= 0x80000000;
    1677                 :             :                 }
    1678                 :             : 
    1679                 :           0 :                 return u.i;
    1680                 :           0 :         }
    1681                 :           0 : }
    1682                 :             : 
    1683                 :             : /*
    1684                 :             :  * Compare the Z-order of points
    1685                 :             :  */
    1686                 :             : static int
    1687                 :           0 : gist_bbox_zorder_cmp(Datum a, Datum b, SortSupport ssup)
    1688                 :             : {
    1689                 :           0 :         Point      *p1 = &(DatumGetBoxP(a)->low);
    1690                 :           0 :         Point      *p2 = &(DatumGetBoxP(b)->low);
    1691                 :           0 :         uint64          z1;
    1692                 :           0 :         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   [ #  #  #  # ]:           0 :         if (p1->x == p2->x && p1->y == p2->y)
    1700                 :           0 :                 return 0;
    1701                 :             : 
    1702                 :           0 :         z1 = point_zorder_internal(p1->x, p1->y);
    1703                 :           0 :         z2 = point_zorder_internal(p2->x, p2->y);
    1704         [ #  # ]:           0 :         if (z1 > z2)
    1705                 :           0 :                 return 1;
    1706         [ #  # ]:           0 :         else if (z1 < z2)
    1707                 :           0 :                 return -1;
    1708                 :             :         else
    1709                 :           0 :                 return 0;
    1710                 :           0 : }
    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                 :           0 : gist_bbox_zorder_abbrev_convert(Datum original, SortSupport ssup)
    1721                 :             : {
    1722                 :           0 :         Point      *p = &(DatumGetBoxP(original)->low);
    1723                 :           0 :         uint64          z;
    1724                 :             : 
    1725                 :           0 :         z = point_zorder_internal(p->x, p->y);
    1726                 :             : 
    1727                 :           0 :         return UInt64GetDatum(z);
    1728                 :           0 : }
    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                 :           0 : gist_bbox_zorder_abbrev_abort(int memtupcount, SortSupport ssup)
    1739                 :             : {
    1740                 :           0 :         return false;
    1741                 :             : }
    1742                 :             : 
    1743                 :             : /*
    1744                 :             :  * Sort support routine for fast GiST index build by sorting.
    1745                 :             :  */
    1746                 :             : Datum
    1747                 :           0 : gist_point_sortsupport(PG_FUNCTION_ARGS)
    1748                 :             : {
    1749                 :           0 :         SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0);
    1750                 :             : 
    1751         [ #  # ]:           0 :         if (ssup->abbreviate)
    1752                 :             :         {
    1753                 :           0 :                 ssup->comparator = ssup_datum_unsigned_cmp;
    1754                 :           0 :                 ssup->abbrev_converter = gist_bbox_zorder_abbrev_convert;
    1755                 :           0 :                 ssup->abbrev_abort = gist_bbox_zorder_abbrev_abort;
    1756                 :           0 :                 ssup->abbrev_full_comparator = gist_bbox_zorder_cmp;
    1757                 :           0 :         }
    1758                 :             :         else
    1759                 :             :         {
    1760                 :           0 :                 ssup->comparator = gist_bbox_zorder_cmp;
    1761                 :             :         }
    1762                 :           0 :         PG_RETURN_VOID();
    1763                 :           0 : }
        

Generated by: LCOV version 2.3.2-1