LCOV - code coverage report
Current view: top level - src/backend/access/spgist - spgkdtreeproc.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 94.1 % 152 143
Test Date: 2026-02-17 17:20:33 Functions: 100.0 % 7 7
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /*-------------------------------------------------------------------------
       2              :  *
       3              :  * spgkdtreeproc.c
       4              :  *    implementation of k-d tree over points for SP-GiST
       5              :  *
       6              :  *
       7              :  * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
       8              :  * Portions Copyright (c) 1994, Regents of the University of California
       9              :  *
      10              :  * IDENTIFICATION
      11              :  *          src/backend/access/spgist/spgkdtreeproc.c
      12              :  *
      13              :  *-------------------------------------------------------------------------
      14              :  */
      15              : 
      16              : #include "postgres.h"
      17              : 
      18              : #include "access/spgist.h"
      19              : #include "access/spgist_private.h"
      20              : #include "access/stratnum.h"
      21              : #include "catalog/pg_type.h"
      22              : #include "utils/float.h"
      23              : #include "utils/fmgrprotos.h"
      24              : #include "utils/geo_decls.h"
      25              : 
      26              : 
      27              : Datum
      28           13 : spg_kd_config(PG_FUNCTION_ARGS)
      29              : {
      30              : #ifdef NOT_USED
      31              :     spgConfigIn *cfgin = (spgConfigIn *) PG_GETARG_POINTER(0);
      32              : #endif
      33           13 :     spgConfigOut *cfg = (spgConfigOut *) PG_GETARG_POINTER(1);
      34              : 
      35           13 :     cfg->prefixType = FLOAT8OID;
      36           13 :     cfg->labelType = VOIDOID;    /* we don't need node labels */
      37           13 :     cfg->canReturnData = true;
      38           13 :     cfg->longValuesOK = false;
      39           13 :     PG_RETURN_VOID();
      40              : }
      41              : 
      42              : static int
      43       290751 : getSide(double coord, bool isX, Point *tst)
      44              : {
      45       290751 :     double      tstcoord = (isX) ? tst->x : tst->y;
      46              : 
      47       290751 :     if (coord == tstcoord)
      48        16089 :         return 0;
      49       274662 :     else if (coord > tstcoord)
      50        71916 :         return 1;
      51              :     else
      52       202746 :         return -1;
      53              : }
      54              : 
      55              : Datum
      56       290751 : spg_kd_choose(PG_FUNCTION_ARGS)
      57              : {
      58       290751 :     spgChooseIn *in = (spgChooseIn *) PG_GETARG_POINTER(0);
      59       290751 :     spgChooseOut *out = (spgChooseOut *) PG_GETARG_POINTER(1);
      60       290751 :     Point      *inPoint = DatumGetPointP(in->datum);
      61              :     double      coord;
      62              : 
      63       290751 :     if (in->allTheSame)
      64            0 :         elog(ERROR, "allTheSame should not occur for k-d trees");
      65              : 
      66              :     Assert(in->hasPrefix);
      67       290751 :     coord = DatumGetFloat8(in->prefixDatum);
      68              : 
      69              :     Assert(in->nNodes == 2);
      70              : 
      71       290751 :     out->resultType = spgMatchNode;
      72       290751 :     out->result.matchNode.nodeN =
      73       290751 :         (getSide(coord, in->level % 2, inPoint) > 0) ? 0 : 1;
      74       290751 :     out->result.matchNode.levelAdd = 1;
      75       290751 :     out->result.matchNode.restDatum = PointPGetDatum(inPoint);
      76              : 
      77       290751 :     PG_RETURN_VOID();
      78              : }
      79              : 
      80              : typedef struct SortedPoint
      81              : {
      82              :     Point      *p;
      83              :     int         i;
      84              : } SortedPoint;
      85              : 
      86              : static int
      87       153813 : x_cmp(const void *a, const void *b)
      88              : {
      89       153813 :     const SortedPoint *pa = a;
      90       153813 :     const SortedPoint *pb = b;
      91              : 
      92       153813 :     if (pa->p->x == pb->p->x)
      93         4431 :         return 0;
      94       149382 :     return (pa->p->x > pb->p->x) ? 1 : -1;
      95              : }
      96              : 
      97              : static int
      98       158487 : y_cmp(const void *a, const void *b)
      99              : {
     100       158487 :     const SortedPoint *pa = a;
     101       158487 :     const SortedPoint *pb = b;
     102              : 
     103       158487 :     if (pa->p->y == pb->p->y)
     104         2709 :         return 0;
     105       155778 :     return (pa->p->y > pb->p->y) ? 1 : -1;
     106              : }
     107              : 
     108              : 
     109              : Datum
     110          345 : spg_kd_picksplit(PG_FUNCTION_ARGS)
     111              : {
     112          345 :     spgPickSplitIn *in = (spgPickSplitIn *) PG_GETARG_POINTER(0);
     113          345 :     spgPickSplitOut *out = (spgPickSplitOut *) PG_GETARG_POINTER(1);
     114              :     int         i;
     115              :     int         middle;
     116              :     SortedPoint *sorted;
     117              :     double      coord;
     118              : 
     119          345 :     sorted = palloc_array(SortedPoint, in->nTuples);
     120        53037 :     for (i = 0; i < in->nTuples; i++)
     121              :     {
     122        52692 :         sorted[i].p = DatumGetPointP(in->datums[i]);
     123        52692 :         sorted[i].i = i;
     124              :     }
     125              : 
     126          345 :     qsort(sorted, in->nTuples, sizeof(*sorted),
     127              :           (in->level % 2) ? x_cmp : y_cmp);
     128          345 :     middle = in->nTuples >> 1;
     129          345 :     coord = (in->level % 2) ? sorted[middle].p->x : sorted[middle].p->y;
     130              : 
     131          345 :     out->hasPrefix = true;
     132          345 :     out->prefixDatum = Float8GetDatum(coord);
     133              : 
     134          345 :     out->nNodes = 2;
     135          345 :     out->nodeLabels = NULL;      /* we don't need node labels */
     136              : 
     137          345 :     out->mapTuplesToNodes = palloc_array(int, in->nTuples);
     138          345 :     out->leafTupleDatums = palloc_array(Datum, in->nTuples);
     139              : 
     140              :     /*
     141              :      * Note: points that have coordinates exactly equal to coord may get
     142              :      * classified into either node, depending on where they happen to fall in
     143              :      * the sorted list.  This is okay as long as the inner_consistent function
     144              :      * descends into both sides for such cases.  This is better than the
     145              :      * alternative of trying to have an exact boundary, because it keeps the
     146              :      * tree balanced even when we have many instances of the same point value.
     147              :      * So we should never trigger the allTheSame logic.
     148              :      */
     149        53037 :     for (i = 0; i < in->nTuples; i++)
     150              :     {
     151        52692 :         Point      *p = sorted[i].p;
     152        52692 :         int         n = sorted[i].i;
     153              : 
     154        52692 :         out->mapTuplesToNodes[n] = (i < middle) ? 0 : 1;
     155        52692 :         out->leafTupleDatums[n] = PointPGetDatum(p);
     156              :     }
     157              : 
     158          345 :     PG_RETURN_VOID();
     159              : }
     160              : 
     161              : Datum
     162         3042 : spg_kd_inner_consistent(PG_FUNCTION_ARGS)
     163              : {
     164         3042 :     spgInnerConsistentIn *in = (spgInnerConsistentIn *) PG_GETARG_POINTER(0);
     165         3042 :     spgInnerConsistentOut *out = (spgInnerConsistentOut *) PG_GETARG_POINTER(1);
     166              :     double      coord;
     167              :     int         which;
     168              :     int         i;
     169              :     BOX         bboxes[2];
     170              : 
     171              :     Assert(in->hasPrefix);
     172         3042 :     coord = DatumGetFloat8(in->prefixDatum);
     173              : 
     174         3042 :     if (in->allTheSame)
     175            0 :         elog(ERROR, "allTheSame should not occur for k-d trees");
     176              : 
     177              :     Assert(in->nNodes == 2);
     178              : 
     179              :     /* "which" is a bitmask of children that satisfy all constraints */
     180         3042 :     which = (1 << 1) | (1 << 2);
     181              : 
     182         5352 :     for (i = 0; i < in->nkeys; i++)
     183              :     {
     184         2310 :         Point      *query = DatumGetPointP(in->scankeys[i].sk_argument);
     185              :         BOX        *boxQuery;
     186              : 
     187         2310 :         switch (in->scankeys[i].sk_strategy)
     188              :         {
     189          420 :             case RTLeftStrategyNumber:
     190          420 :                 if ((in->level % 2) != 0 && FPlt(query->x, coord))
     191           18 :                     which &= (1 << 1);
     192          420 :                 break;
     193          336 :             case RTRightStrategyNumber:
     194          336 :                 if ((in->level % 2) != 0 && FPgt(query->x, coord))
     195           12 :                     which &= (1 << 2);
     196          336 :                 break;
     197           18 :             case RTSameStrategyNumber:
     198           18 :                 if ((in->level % 2) != 0)
     199              :                 {
     200            6 :                     if (FPlt(query->x, coord))
     201            6 :                         which &= (1 << 1);
     202            0 :                     else if (FPgt(query->x, coord))
     203            0 :                         which &= (1 << 2);
     204              :                 }
     205              :                 else
     206              :                 {
     207           12 :                     if (FPlt(query->y, coord))
     208            6 :                         which &= (1 << 1);
     209            6 :                     else if (FPgt(query->y, coord))
     210            6 :                         which &= (1 << 2);
     211              :                 }
     212           18 :                 break;
     213          624 :             case RTBelowStrategyNumber:
     214              :             case RTOldBelowStrategyNumber:
     215          624 :                 if ((in->level % 2) == 0 && FPlt(query->y, coord))
     216          126 :                     which &= (1 << 1);
     217          624 :                 break;
     218          612 :             case RTAboveStrategyNumber:
     219              :             case RTOldAboveStrategyNumber:
     220          612 :                 if ((in->level % 2) == 0 && FPgt(query->y, coord))
     221          210 :                     which &= (1 << 2);
     222          612 :                 break;
     223          300 :             case RTContainedByStrategyNumber:
     224              : 
     225              :                 /*
     226              :                  * For this operator, the query is a box not a point.  We
     227              :                  * cheat to the extent of assuming that DatumGetPointP won't
     228              :                  * do anything that would be bad for a pointer-to-box.
     229              :                  */
     230          300 :                 boxQuery = DatumGetBoxP(in->scankeys[i].sk_argument);
     231              : 
     232          300 :                 if ((in->level % 2) != 0)
     233              :                 {
     234          150 :                     if (FPlt(boxQuery->high.x, coord))
     235           45 :                         which &= (1 << 1);
     236          105 :                     else if (FPgt(boxQuery->low.x, coord))
     237            0 :                         which &= (1 << 2);
     238              :                 }
     239              :                 else
     240              :                 {
     241          150 :                     if (FPlt(boxQuery->high.y, coord))
     242           15 :                         which &= (1 << 1);
     243          135 :                     else if (FPgt(boxQuery->low.y, coord))
     244           15 :                         which &= (1 << 2);
     245              :                 }
     246          300 :                 break;
     247            0 :             default:
     248            0 :                 elog(ERROR, "unrecognized strategy number: %d",
     249              :                      in->scankeys[i].sk_strategy);
     250              :                 break;
     251              :         }
     252              : 
     253         2310 :         if (which == 0)
     254            0 :             break;              /* no need to consider remaining conditions */
     255              :     }
     256              : 
     257              :     /* We must descend into the children identified by which */
     258         3042 :     out->nNodes = 0;
     259              : 
     260              :     /* Fast-path for no matching children */
     261         3042 :     if (!which)
     262            0 :         PG_RETURN_VOID();
     263              : 
     264         3042 :     out->nodeNumbers = palloc_array(int, 2);
     265              : 
     266              :     /*
     267              :      * When ordering scan keys are specified, we've to calculate distance for
     268              :      * them.  In order to do that, we need calculate bounding boxes for both
     269              :      * children nodes.  Calculation of those bounding boxes on non-zero level
     270              :      * require knowledge of bounding box of upper node.  So, we save bounding
     271              :      * boxes to traversalValues.
     272              :      */
     273         3042 :     if (in->norderbys > 0)
     274              :     {
     275              :         BOX         infArea;
     276              :         BOX        *area;
     277              : 
     278          792 :         out->distances = palloc_array(double *, in->nNodes);
     279          792 :         out->traversalValues = palloc_array(void *, in->nNodes);
     280              : 
     281          792 :         if (in->level == 0)
     282              :         {
     283           18 :             float8      inf = get_float8_infinity();
     284              : 
     285           18 :             infArea.high.x = inf;
     286           18 :             infArea.high.y = inf;
     287           18 :             infArea.low.x = -inf;
     288           18 :             infArea.low.y = -inf;
     289           18 :             area = &infArea;
     290              :         }
     291              :         else
     292              :         {
     293          774 :             area = (BOX *) in->traversalValue;
     294              :             Assert(area);
     295              :         }
     296              : 
     297          792 :         bboxes[0].low = area->low;
     298          792 :         bboxes[1].high = area->high;
     299              : 
     300          792 :         if (in->level % 2)
     301              :         {
     302              :             /* split box by x */
     303          366 :             bboxes[0].high.x = bboxes[1].low.x = coord;
     304          366 :             bboxes[0].high.y = area->high.y;
     305          366 :             bboxes[1].low.y = area->low.y;
     306              :         }
     307              :         else
     308              :         {
     309              :             /* split box by y */
     310          426 :             bboxes[0].high.y = bboxes[1].low.y = coord;
     311          426 :             bboxes[0].high.x = area->high.x;
     312          426 :             bboxes[1].low.x = area->low.x;
     313              :         }
     314              :     }
     315              : 
     316         9126 :     for (i = 1; i <= 2; i++)
     317              :     {
     318         6084 :         if (which & (1 << i))
     319              :         {
     320         5625 :             out->nodeNumbers[out->nNodes] = i - 1;
     321              : 
     322         5625 :             if (in->norderbys > 0)
     323              :             {
     324         1569 :                 MemoryContext oldCtx = MemoryContextSwitchTo(in->traversalMemoryContext);
     325         1569 :                 BOX        *box = box_copy(&bboxes[i - 1]);
     326              : 
     327         1569 :                 MemoryContextSwitchTo(oldCtx);
     328              : 
     329         1569 :                 out->traversalValues[out->nNodes] = box;
     330              : 
     331         1569 :                 out->distances[out->nNodes] = spg_key_orderbys_distances(BoxPGetDatum(box), false,
     332              :                                                                          in->orderbys, in->norderbys);
     333              :             }
     334              : 
     335         5625 :             out->nNodes++;
     336              :         }
     337              :     }
     338              : 
     339              :     /* Set up level increments, too */
     340         3042 :     out->levelAdds = palloc_array(int, 2);
     341         3042 :     out->levelAdds[0] = 1;
     342         3042 :     out->levelAdds[1] = 1;
     343              : 
     344         3042 :     PG_RETURN_VOID();
     345              : }
     346              : 
     347              : /*
     348              :  * spg_kd_leaf_consistent() is the same as spg_quad_leaf_consistent(),
     349              :  * since we support the same operators and the same leaf data type.
     350              :  * So we just borrow that function.
     351              :  */
        

Generated by: LCOV version 2.0-1