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

Generated by: LCOV version 1.13