LCOV - code coverage report
Current view: top level - contrib/ltree - _ltree_gist.c (source / functions) Hit Total Coverage
Test: PostgreSQL 19devel Lines: 240 271 88.6 %
Date: 2026-02-08 20:18:15 Functions: 24 25 96.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*
       2             :  * contrib/ltree/_ltree_gist.c
       3             :  *
       4             :  *
       5             :  * GiST support for ltree[]
       6             :  * Teodor Sigaev <teodor@stack.net>
       7             :  */
       8             : #include "postgres.h"
       9             : 
      10             : #include "access/gist.h"
      11             : #include "access/reloptions.h"
      12             : #include "access/stratnum.h"
      13             : #include "crc32.h"
      14             : #include "ltree.h"
      15             : #include "port/pg_bitutils.h"
      16             : #include "utils/array.h"
      17             : 
      18           6 : PG_FUNCTION_INFO_V1(_ltree_compress);
      19           6 : PG_FUNCTION_INFO_V1(_ltree_same);
      20           6 : PG_FUNCTION_INFO_V1(_ltree_union);
      21           6 : PG_FUNCTION_INFO_V1(_ltree_penalty);
      22           6 : PG_FUNCTION_INFO_V1(_ltree_picksplit);
      23           6 : PG_FUNCTION_INFO_V1(_ltree_consistent);
      24           6 : PG_FUNCTION_INFO_V1(_ltree_gist_options);
      25             : 
      26             : #define GETENTRY(vec,pos) ((ltree_gist *) DatumGetPointer((vec)->vector[(pos)].key))
      27             : #define NEXTVAL(x) ( (ltree*)( (char*)(x) + INTALIGN( VARSIZE(x) ) ) )
      28             : 
      29             : #define WISH_F(a,b,c) (double)( -(double)(((a)-(b))*((a)-(b))*((a)-(b)))*(c) )
      30             : 
      31             : 
      32             : static void
      33       14116 : hashing(BITVECP sign, ltree *t, int siglen)
      34             : {
      35       14116 :     int         tlen = t->numlevel;
      36       14116 :     ltree_level *cur = LTREE_FIRST(t);
      37             :     int         hash;
      38             : 
      39      106932 :     while (tlen > 0)
      40             :     {
      41       92816 :         hash = ltree_crc32_sz(cur->name, cur->len);
      42       92816 :         AHASH(sign, hash, siglen);
      43       92816 :         cur = LEVEL_NEXT(cur);
      44       92816 :         tlen--;
      45             :     }
      46       14116 : }
      47             : 
      48             : Datum
      49       11776 : _ltree_compress(PG_FUNCTION_ARGS)
      50             : {
      51       11776 :     GISTENTRY  *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
      52       11776 :     GISTENTRY  *retval = entry;
      53       11776 :     int         siglen = LTREE_GET_ASIGLEN();
      54             : 
      55       11776 :     if (entry->leafkey)
      56             :     {                           /* ltree */
      57             :         ltree_gist *key;
      58        4000 :         ArrayType  *val = DatumGetArrayTypeP(entry->key);
      59        4000 :         int         num = ArrayGetNItems(ARR_NDIM(val), ARR_DIMS(val));
      60        4000 :         ltree      *item = (ltree *) ARR_DATA_PTR(val);
      61             : 
      62        4000 :         if (ARR_NDIM(val) > 1)
      63           0 :             ereport(ERROR,
      64             :                     (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
      65             :                      errmsg("array must be one-dimensional")));
      66        4000 :         if (array_contains_nulls(val))
      67           0 :             ereport(ERROR,
      68             :                     (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
      69             :                      errmsg("array must not contain nulls")));
      70             : 
      71        4000 :         key = ltree_gist_alloc(false, NULL, siglen, NULL, NULL);
      72             : 
      73       18116 :         while (num > 0)
      74             :         {
      75       14116 :             hashing(LTG_SIGN(key), item, siglen);
      76       14116 :             num--;
      77       14116 :             item = NEXTVAL(item);
      78             :         }
      79             : 
      80        4000 :         retval = palloc_object(GISTENTRY);
      81        4000 :         gistentryinit(*retval, PointerGetDatum(key),
      82             :                       entry->rel, entry->page,
      83             :                       entry->offset, false);
      84             :     }
      85        7776 :     else if (!LTG_ISALLTRUE(DatumGetPointer(entry->key)))
      86             :     {
      87             :         int32       i;
      88             :         ltree_gist *key;
      89        7776 :         BITVECP     sign = LTG_SIGN(DatumGetPointer(entry->key));
      90             : 
      91        7776 :         ALOOPBYTE(siglen)
      92             :         {
      93        7776 :             if ((sign[i] & 0xff) != 0xff)
      94        7776 :                 PG_RETURN_POINTER(retval);
      95             :         }
      96             : 
      97           0 :         key = ltree_gist_alloc(true, sign, siglen, NULL, NULL);
      98           0 :         retval = palloc_object(GISTENTRY);
      99           0 :         gistentryinit(*retval, PointerGetDatum(key),
     100             :                       entry->rel, entry->page,
     101             :                       entry->offset, false);
     102             :     }
     103        4000 :     PG_RETURN_POINTER(retval);
     104             : }
     105             : 
     106             : Datum
     107       15502 : _ltree_same(PG_FUNCTION_ARGS)
     108             : {
     109       15502 :     ltree_gist *a = (ltree_gist *) PG_GETARG_POINTER(0);
     110       15502 :     ltree_gist *b = (ltree_gist *) PG_GETARG_POINTER(1);
     111       15502 :     bool       *result = (bool *) PG_GETARG_POINTER(2);
     112       15502 :     int         siglen = LTREE_GET_ASIGLEN();
     113             : 
     114       15502 :     if (LTG_ISALLTRUE(a) && LTG_ISALLTRUE(b))
     115           0 :         *result = true;
     116       15502 :     else if (LTG_ISALLTRUE(a))
     117           0 :         *result = false;
     118       15502 :     else if (LTG_ISALLTRUE(b))
     119           0 :         *result = false;
     120             :     else
     121             :     {
     122             :         int32       i;
     123       15502 :         BITVECP     sa = LTG_SIGN(a),
     124       15502 :                     sb = LTG_SIGN(b);
     125             : 
     126       15502 :         *result = true;
     127    21962644 :         ALOOPBYTE(siglen)
     128             :         {
     129    21951326 :             if (sa[i] != sb[i])
     130             :             {
     131        4184 :                 *result = false;
     132        4184 :                 break;
     133             :             }
     134             :         }
     135             :     }
     136       15502 :     PG_RETURN_POINTER(result);
     137             : }
     138             : 
     139             : static int32
     140       31004 : unionkey(BITVECP sbase, ltree_gist *add, int siglen)
     141             : {
     142             :     int32       i;
     143       31004 :     BITVECP     sadd = LTG_SIGN(add);
     144             : 
     145       31004 :     if (LTG_ISALLTRUE(add))
     146           0 :         return 1;
     147             : 
     148    55924844 :     ALOOPBYTE(siglen)
     149    55893840 :         sbase[i] |= sadd[i];
     150       31004 :     return 0;
     151             : }
     152             : 
     153             : Datum
     154       15502 : _ltree_union(PG_FUNCTION_ARGS)
     155             : {
     156       15502 :     GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
     157       15502 :     int        *size = (int *) PG_GETARG_POINTER(1);
     158       15502 :     int         siglen = LTREE_GET_ASIGLEN();
     159             :     int32       i;
     160       15502 :     ltree_gist *result = ltree_gist_alloc(false, NULL, siglen, NULL, NULL);
     161       15502 :     BITVECP     base = LTG_SIGN(result);
     162             : 
     163       46506 :     for (i = 0; i < entryvec->n; i++)
     164             :     {
     165       31004 :         if (unionkey(base, GETENTRY(entryvec, i), siglen))
     166             :         {
     167           0 :             result->flag |= LTG_ALLTRUE;
     168           0 :             SET_VARSIZE(result, LTG_HDRSIZE);
     169           0 :             break;
     170             :         }
     171             :     }
     172             : 
     173       15502 :     *size = VARSIZE(result);
     174             : 
     175       15502 :     PG_RETURN_POINTER(result);
     176             : }
     177             : 
     178             : static int32
     179           0 : sizebitvec(BITVECP sign, int siglen)
     180             : {
     181           0 :     return pg_popcount((const char *) sign, siglen);
     182             : }
     183             : 
     184             : static int
     185      250582 : hemdistsign(BITVECP a, BITVECP b, int siglen)
     186             : {
     187             :     int         i,
     188             :                 diff,
     189      250582 :                 dist = 0;
     190             : 
     191   118360246 :     ALOOPBYTE(siglen)
     192             :     {
     193   118109664 :         diff = (unsigned char) (a[i] ^ b[i]);
     194             :         /* Using the popcount functions here isn't likely to win */
     195   118109664 :         dist += pg_number_of_ones[diff];
     196             :     }
     197      250582 :     return dist;
     198             : }
     199             : 
     200             : static int
     201      250582 : hemdist(ltree_gist *a, ltree_gist *b, int siglen)
     202             : {
     203      250582 :     if (LTG_ISALLTRUE(a))
     204             :     {
     205           0 :         if (LTG_ISALLTRUE(b))
     206           0 :             return 0;
     207             :         else
     208           0 :             return ASIGLENBIT(siglen) - sizebitvec(LTG_SIGN(b), siglen);
     209             :     }
     210      250582 :     else if (LTG_ISALLTRUE(b))
     211           0 :         return ASIGLENBIT(siglen) - sizebitvec(LTG_SIGN(a), siglen);
     212             : 
     213      250582 :     return hemdistsign(LTG_SIGN(a), LTG_SIGN(b), siglen);
     214             : }
     215             : 
     216             : 
     217             : Datum
     218       38692 : _ltree_penalty(PG_FUNCTION_ARGS)
     219             : {
     220       38692 :     ltree_gist *origval = (ltree_gist *) DatumGetPointer(((GISTENTRY *) PG_GETARG_POINTER(0))->key);
     221       38692 :     ltree_gist *newval = (ltree_gist *) DatumGetPointer(((GISTENTRY *) PG_GETARG_POINTER(1))->key);
     222       38692 :     float      *penalty = (float *) PG_GETARG_POINTER(2);
     223       38692 :     int         siglen = LTREE_GET_ASIGLEN();
     224             : 
     225       38692 :     *penalty = hemdist(origval, newval, siglen);
     226       38692 :     PG_RETURN_POINTER(penalty);
     227             : }
     228             : 
     229             : typedef struct
     230             : {
     231             :     OffsetNumber pos;
     232             :     int32       cost;
     233             : } SPLITCOST;
     234             : 
     235             : static int
     236       15922 : comparecost(const void *a, const void *b)
     237             : {
     238       15922 :     return ((const SPLITCOST *) a)->cost - ((const SPLITCOST *) b)->cost;
     239             : }
     240             : 
     241             : Datum
     242        1796 : _ltree_picksplit(PG_FUNCTION_ARGS)
     243             : {
     244        1796 :     GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
     245        1796 :     GIST_SPLITVEC *v = (GIST_SPLITVEC *) PG_GETARG_POINTER(1);
     246        1796 :     int         siglen = LTREE_GET_ASIGLEN();
     247             :     OffsetNumber k,
     248             :                 j;
     249             :     ltree_gist *datum_l,
     250             :                *datum_r;
     251             :     BITVECP     union_l,
     252             :                 union_r;
     253             :     int32       size_alpha,
     254             :                 size_beta;
     255             :     int32       size_waste,
     256        1796 :                 waste = -1;
     257             :     int32       nbytes;
     258        1796 :     OffsetNumber seed_1 = 0,
     259        1796 :                 seed_2 = 0;
     260             :     OffsetNumber *left,
     261             :                *right;
     262             :     OffsetNumber maxoff;
     263             :     BITVECP     ptr;
     264             :     int         i;
     265             :     SPLITCOST  *costvector;
     266             :     ltree_gist *_k,
     267             :                *_j;
     268             : 
     269        1796 :     maxoff = entryvec->n - 2;
     270        1796 :     nbytes = (maxoff + 2) * sizeof(OffsetNumber);
     271        1796 :     v->spl_left = (OffsetNumber *) palloc(nbytes);
     272        1796 :     v->spl_right = (OffsetNumber *) palloc(nbytes);
     273             : 
     274        7854 :     for (k = FirstOffsetNumber; k < maxoff; k = OffsetNumberNext(k))
     275             :     {
     276        6058 :         _k = GETENTRY(entryvec, k);
     277      186532 :         for (j = OffsetNumberNext(k); j <= maxoff; j = OffsetNumberNext(j))
     278             :         {
     279      180474 :             size_waste = hemdist(_k, GETENTRY(entryvec, j), siglen);
     280      180474 :             if (size_waste > waste)
     281             :             {
     282        3184 :                 waste = size_waste;
     283        3184 :                 seed_1 = k;
     284        3184 :                 seed_2 = j;
     285             :             }
     286             :         }
     287             :     }
     288             : 
     289        1796 :     left = v->spl_left;
     290        1796 :     v->spl_nleft = 0;
     291        1796 :     right = v->spl_right;
     292        1796 :     v->spl_nright = 0;
     293             : 
     294        1796 :     if (seed_1 == 0 || seed_2 == 0)
     295             :     {
     296           0 :         seed_1 = 1;
     297           0 :         seed_2 = 2;
     298             :     }
     299             : 
     300             :     /* form initial .. */
     301        1796 :     datum_l = ltree_gist_alloc(LTG_ISALLTRUE(GETENTRY(entryvec, seed_1)),
     302        1796 :                                LTG_SIGN(GETENTRY(entryvec, seed_1)),
     303             :                                siglen, NULL, NULL);
     304             : 
     305        1796 :     datum_r = ltree_gist_alloc(LTG_ISALLTRUE(GETENTRY(entryvec, seed_2)),
     306        1796 :                                LTG_SIGN(GETENTRY(entryvec, seed_2)),
     307             :                                siglen, NULL, NULL);
     308             : 
     309        1796 :     maxoff = OffsetNumberNext(maxoff);
     310             :     /* sort before ... */
     311        1796 :     costvector = palloc_array(SPLITCOST, maxoff);
     312       11446 :     for (j = FirstOffsetNumber; j <= maxoff; j = OffsetNumberNext(j))
     313             :     {
     314        9650 :         costvector[j - 1].pos = j;
     315        9650 :         _j = GETENTRY(entryvec, j);
     316        9650 :         size_alpha = hemdist(datum_l, _j, siglen);
     317        9650 :         size_beta = hemdist(datum_r, _j, siglen);
     318        9650 :         costvector[j - 1].cost = abs(size_alpha - size_beta);
     319             :     }
     320        1796 :     qsort(costvector, maxoff, sizeof(SPLITCOST), comparecost);
     321             : 
     322        1796 :     union_l = LTG_SIGN(datum_l);
     323        1796 :     union_r = LTG_SIGN(datum_r);
     324             : 
     325       11446 :     for (k = 0; k < maxoff; k++)
     326             :     {
     327        9650 :         j = costvector[k].pos;
     328        9650 :         if (j == seed_1)
     329             :         {
     330        1796 :             *left++ = j;
     331        1796 :             v->spl_nleft++;
     332        1796 :             continue;
     333             :         }
     334        7854 :         else if (j == seed_2)
     335             :         {
     336        1796 :             *right++ = j;
     337        1796 :             v->spl_nright++;
     338        1796 :             continue;
     339             :         }
     340        6058 :         _j = GETENTRY(entryvec, j);
     341        6058 :         size_alpha = hemdist(datum_l, _j, siglen);
     342        6058 :         size_beta = hemdist(datum_r, _j, siglen);
     343             : 
     344        6058 :         if (size_alpha < size_beta + WISH_F(v->spl_nleft, v->spl_nright, 0.00001))
     345             :         {
     346        3068 :             if (LTG_ISALLTRUE(datum_l) || LTG_ISALLTRUE(_j))
     347             :             {
     348           0 :                 if (!LTG_ISALLTRUE(datum_l))
     349           0 :                     memset(union_l, 0xff, siglen);
     350             :             }
     351             :             else
     352             :             {
     353        3068 :                 ptr = LTG_SIGN(_j);
     354     3526084 :                 ALOOPBYTE(siglen)
     355     3523016 :                     union_l[i] |= ptr[i];
     356             :             }
     357        3068 :             *left++ = j;
     358        3068 :             v->spl_nleft++;
     359             :         }
     360             :         else
     361             :         {
     362        2990 :             if (LTG_ISALLTRUE(datum_r) || LTG_ISALLTRUE(_j))
     363             :             {
     364           0 :                 if (!LTG_ISALLTRUE(datum_r))
     365           0 :                     memset(union_r, 0xff, siglen);
     366             :             }
     367             :             else
     368             :             {
     369        2990 :                 ptr = LTG_SIGN(_j);
     370     3747374 :                 ALOOPBYTE(siglen)
     371     3744384 :                     union_r[i] |= ptr[i];
     372             :             }
     373        2990 :             *right++ = j;
     374        2990 :             v->spl_nright++;
     375             :         }
     376             :     }
     377             : 
     378        1796 :     *right = *left = FirstOffsetNumber;
     379             : 
     380        1796 :     v->spl_ldatum = PointerGetDatum(datum_l);
     381        1796 :     v->spl_rdatum = PointerGetDatum(datum_r);
     382             : 
     383        1796 :     PG_RETURN_POINTER(v);
     384             : }
     385             : 
     386             : static bool
     387        5146 : gist_te(ltree_gist *key, ltree *query, int siglen)
     388             : {
     389        5146 :     ltree_level *curq = LTREE_FIRST(query);
     390        5146 :     BITVECP     sign = LTG_SIGN(key);
     391        5146 :     int         qlen = query->numlevel;
     392             :     unsigned int hv;
     393             : 
     394        5146 :     if (LTG_ISALLTRUE(key))
     395           0 :         return true;
     396             : 
     397       17314 :     while (qlen > 0)
     398             :     {
     399       13258 :         hv = ltree_crc32_sz(curq->name, curq->len);
     400       13258 :         if (!GETBIT(sign, AHASHVAL(hv, siglen)))
     401        1090 :             return false;
     402       12168 :         curq = LEVEL_NEXT(curq);
     403       12168 :         qlen--;
     404             :     }
     405             : 
     406        4056 :     return true;
     407             : }
     408             : 
     409             : typedef struct LtreeSignature
     410             : {
     411             :     BITVECP     sign;
     412             :     int         siglen;
     413             : } LtreeSignature;
     414             : 
     415             : static bool
     416        7718 : checkcondition_bit(void *cxt, ITEM *val)
     417             : {
     418        7718 :     LtreeSignature *sig = cxt;
     419             : 
     420        7718 :     return (FLG_CANLOOKSIGN(val->flag)) ? GETBIT(sig->sign, AHASHVAL(val->val, sig->siglen)) : true;
     421             : }
     422             : 
     423             : static bool
     424        4608 : gist_qtxt(ltree_gist *key, ltxtquery *query, int siglen)
     425             : {
     426             :     LtreeSignature sig;
     427             : 
     428        4608 :     if (LTG_ISALLTRUE(key))
     429           0 :         return true;
     430             : 
     431        4608 :     sig.sign = LTG_SIGN(key);
     432        4608 :     sig.siglen = siglen;
     433             : 
     434        4608 :     return ltree_execute(GETQUERY(query),
     435             :                          &sig, false,
     436             :                          checkcondition_bit);
     437             : }
     438             : 
     439             : static bool
     440       31022 : gist_qe(ltree_gist *key, lquery *query, int siglen)
     441             : {
     442       31022 :     lquery_level *curq = LQUERY_FIRST(query);
     443       31022 :     BITVECP     sign = LTG_SIGN(key);
     444       31022 :     int         qlen = query->numlevel;
     445             : 
     446       31022 :     if (LTG_ISALLTRUE(key))
     447           0 :         return true;
     448             : 
     449       93406 :     while (qlen > 0)
     450             :     {
     451       74860 :         if (curq->numvar && LQL_CANLOOKSIGN(curq))
     452             :         {
     453       52396 :             bool        isexist = false;
     454       52396 :             int         vlen = curq->numvar;
     455       52396 :             lquery_variant *curv = LQL_FIRST(curq);
     456             : 
     457       64872 :             while (vlen > 0)
     458             :             {
     459       52396 :                 if (GETBIT(sign, AHASHVAL(curv->val, siglen)))
     460             :                 {
     461       39920 :                     isexist = true;
     462       39920 :                     break;
     463             :                 }
     464       12476 :                 curv = LVAR_NEXT(curv);
     465       12476 :                 vlen--;
     466             :             }
     467       52396 :             if (!isexist)
     468       12476 :                 return false;
     469             :         }
     470             : 
     471       62384 :         curq = LQL_NEXT(curq);
     472       62384 :         qlen--;
     473             :     }
     474             : 
     475       18546 :     return true;
     476             : }
     477             : 
     478             : static bool
     479        4940 : _arrq_cons(ltree_gist *key, ArrayType *_query, int siglen)
     480             : {
     481        4940 :     lquery     *query = (lquery *) ARR_DATA_PTR(_query);
     482        4940 :     int         num = ArrayGetNItems(ARR_NDIM(_query), ARR_DIMS(_query));
     483             : 
     484        4940 :     if (ARR_NDIM(_query) > 1)
     485           0 :         ereport(ERROR,
     486             :                 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
     487             :                  errmsg("array must be one-dimensional")));
     488        4940 :     if (array_contains_nulls(_query))
     489           0 :         ereport(ERROR,
     490             :                 (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
     491             :                  errmsg("array must not contain nulls")));
     492             : 
     493        9146 :     while (num > 0)
     494             :     {
     495        7278 :         if (gist_qe(key, query, siglen))
     496        3072 :             return true;
     497        4206 :         num--;
     498        4206 :         query = (lquery *) NEXTVAL(query);
     499             :     }
     500        1868 :     return false;
     501             : }
     502             : 
     503             : Datum
     504       38438 : _ltree_consistent(PG_FUNCTION_ARGS)
     505             : {
     506       38438 :     GISTENTRY  *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
     507       38438 :     void       *query = PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
     508       38438 :     StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2);
     509             : #ifdef NOT_USED
     510             :     Oid         subtype = PG_GETARG_OID(3);
     511             : #endif
     512       38438 :     bool       *recheck = (bool *) PG_GETARG_POINTER(4);
     513       38438 :     int         siglen = LTREE_GET_ASIGLEN();
     514       38438 :     ltree_gist *key = (ltree_gist *) DatumGetPointer(entry->key);
     515       38438 :     bool        res = false;
     516             : 
     517             :     /* All cases served by this function are inexact */
     518       38438 :     *recheck = true;
     519             : 
     520       38438 :     switch (strategy)
     521             :     {
     522        5146 :         case 10:
     523             :         case 11:
     524        5146 :             res = gist_te(key, (ltree *) query, siglen);
     525        5146 :             break;
     526       23744 :         case 12:
     527             :         case 13:
     528       23744 :             res = gist_qe(key, (lquery *) query, siglen);
     529       23744 :             break;
     530        4608 :         case 14:
     531             :         case 15:
     532        4608 :             res = gist_qtxt(key, (ltxtquery *) query, siglen);
     533        4608 :             break;
     534        4940 :         case 16:
     535             :         case 17:
     536        4940 :             res = _arrq_cons(key, (ArrayType *) query, siglen);
     537        4940 :             break;
     538           0 :         default:
     539             :             /* internal error */
     540           0 :             elog(ERROR, "unrecognized StrategyNumber: %d", strategy);
     541             :     }
     542       38438 :     PG_FREE_IF_COPY(query, 1);
     543       38438 :     PG_RETURN_BOOL(res);
     544             : }
     545             : 
     546             : Datum
     547          20 : _ltree_gist_options(PG_FUNCTION_ARGS)
     548             : {
     549          20 :     local_relopts *relopts = (local_relopts *) PG_GETARG_POINTER(0);
     550             : 
     551          20 :     init_local_reloptions(relopts, sizeof(LtreeGistOptions));
     552          20 :     add_local_int_reloption(relopts, "siglen", "signature length",
     553             :                             LTREE_ASIGLEN_DEFAULT, 1, LTREE_ASIGLEN_MAX,
     554             :                             offsetof(LtreeGistOptions, siglen));
     555             : 
     556          20 :     PG_RETURN_VOID();
     557             : }

Generated by: LCOV version 1.16