LCOV - differential code coverage report
Current view: top level - contrib/ltree - lquery_op.c (source / functions) Coverage Total Hit UBC GNC CBC DUB DCB
Current: 0e5ff9b9b45a657aea12440478dc002e9b01f138 vs 0123ce131fca454009439dfa3b2266d1d40737d7 Lines: 90.8 % 131 119 12 2 117 6 3
Current Date: 2026-03-14 14:10:32 -0400 Functions: 84.6 % 13 11 2 1 10
Baseline: lcov-20260315-024220-baseline Branches: 83.7 % 98 82 16 2 80 11 3
Baseline Date: 2026-03-14 15:27:56 +0100 Line coverage date bins:
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
(7,30] days: 83.8 % 37 31 6 31
(30,360] days: 100.0 % 4 4 2 2
(360..) days: 93.3 % 90 84 6 84
Function coverage date bins:
(7,30] days: 100.0 % 2 2 1 1
(360..) days: 81.8 % 11 9 2 9
Branch coverage date bins:
(7,30] days: 81.2 % 32 26 6 26
(30,360] days: 100.0 % 2 2 2
(360..) days: 84.4 % 64 54 10 54

 Age         Owner                    Branch data    TLA  Line data    Source code
                                  1                 :                : /*
                                  2                 :                :  * op function for ltree and lquery
                                  3                 :                :  * Teodor Sigaev <teodor@stack.net>
                                  4                 :                :  * contrib/ltree/lquery_op.c
                                  5                 :                :  */
                                  6                 :                : #include "postgres.h"
                                  7                 :                : 
                                  8                 :                : #include <ctype.h>
                                  9                 :                : 
                                 10                 :                : #include "catalog/pg_collation.h"
                                 11                 :                : #include "ltree.h"
                                 12                 :                : #include "miscadmin.h"
                                 13                 :                : #include "utils/array.h"
                                 14                 :                : #include "utils/formatting.h"
                                 15                 :                : 
 8629 bruce@momjian.us           16                 :CBC           3 : PG_FUNCTION_INFO_V1(ltq_regex);
                                 17                 :              2 : PG_FUNCTION_INFO_V1(ltq_rregex);
                                 18                 :                : 
 8425                            19                 :              3 : PG_FUNCTION_INFO_V1(lt_q_regex);
                                 20                 :              2 : PG_FUNCTION_INFO_V1(lt_q_rregex);
                                 21                 :                : 
                                 22                 :                : #define NEXTVAL(x) ( (lquery*)( (char*)(x) + INTALIGN( VARSIZE(x) ) ) )
                                 23                 :                : 
                                 24                 :                : static char *
 7319 neilc@samurai.com          25                 :             36 : getlexeme(char *start, char *end, int *len)
                                 26                 :                : {
                                 27                 :                :     char       *ptr;
                                 28                 :                : 
 1256 tgl@sss.pgh.pa.us          29   [ +  +  +  + ]:             44 :     while (start < end && t_iseq(start, '_'))
   67 tmunro@postgresql.or       30                 :              8 :         start += pg_mblen_range(start, end);
                                 31                 :                : 
 8629 bruce@momjian.us           32                 :             36 :     ptr = start;
 6467 teodor@sigaev.ru           33         [ +  + ]:             36 :     if (ptr >= end)
 8629 bruce@momjian.us           34                 :              8 :         return NULL;
                                 35                 :                : 
 1256 tgl@sss.pgh.pa.us          36   [ +  +  +  + ]:            114 :     while (ptr < end && !t_iseq(ptr, '_'))
   67 tmunro@postgresql.or       37                 :             86 :         ptr += pg_mblen_range(ptr, end);
                                 38                 :                : 
 8629 bruce@momjian.us           39                 :             28 :     *len = ptr - start;
                                 40                 :             28 :     return start;
                                 41                 :                : }
                                 42                 :                : 
                                 43                 :                : bool
   17 jdavis@postgresql.or       44                 :              8 : compare_subnode(ltree_level *t, char *qn, int len, bool prefix, bool ci)
                                 45                 :                : {
 8593 bruce@momjian.us           46                 :              8 :     char       *endt = t->name + t->len;
                                 47                 :              8 :     char       *endq = qn + len;
                                 48                 :                :     char       *tn;
                                 49                 :                :     int         lent,
                                 50                 :                :                 lenq;
                                 51                 :                :     bool        isok;
                                 52                 :                : 
 7319 neilc@samurai.com          53         [ +  + ]:             16 :     while ((qn = getlexeme(qn, endq, &lenq)) != NULL)
                                 54                 :                :     {
 8593 bruce@momjian.us           55                 :             12 :         tn = t->name;
 8629                            56                 :             12 :         isok = false;
 7319 neilc@samurai.com          57         [ +  + ]:             20 :         while ((tn = getlexeme(tn, endt, &lent)) != NULL)
                                 58                 :                :         {
   17 jdavis@postgresql.or       59         [ +  + ]:             16 :             if (ltree_label_match(qn, lenq, tn, lent, prefix, ci))
                                 60                 :                :             {
 8593 bruce@momjian.us           61                 :              8 :                 isok = true;
 8629                            62                 :              8 :                 break;
                                 63                 :                :             }
                                 64                 :              8 :             tn += lent;
                                 65                 :                :         }
                                 66                 :                : 
 8593                            67         [ +  + ]:             12 :         if (!isok)
 8629                            68                 :              4 :             return false;
                                 69                 :              8 :         qn += lenq;
                                 70                 :                :     }
                                 71                 :                : 
                                 72                 :              4 :     return true;
                                 73                 :                : }
                                 74                 :                : 
                                 75                 :                : /*
                                 76                 :                :  * Check if the label matches the predicate string. If 'prefix' is true, then
                                 77                 :                :  * the predicate string is treated as a prefix. If 'ci' is true, then the
                                 78                 :                :  * predicate string is case-insensitive (and locale-aware).
                                 79                 :                :  */
                                 80                 :                : bool
   17 jdavis@postgresql.or       81                 :         197863 : ltree_label_match(const char *pred, size_t pred_len, const char *label,
                                 82                 :                :                   size_t label_len, bool prefix, bool ci)
                                 83                 :                : {
                                 84                 :                :     static pg_locale_t locale = NULL;
                                 85                 :                :     char       *fpred;          /* casefolded predicate */
                                 86                 :         197863 :     size_t      fpred_len = pred_len;
                                 87                 :                :     char       *flabel;         /* casefolded label */
                                 88                 :         197863 :     size_t      flabel_len = label_len;
                                 89                 :                :     size_t      len;
                                 90                 :                :     bool        res;
                                 91                 :                : 
                                 92                 :                :     /* fast path for binary match or binary prefix match */
                                 93   [ +  +  +  +  :         197863 :     if ((pred_len == label_len || (prefix && pred_len < label_len)) &&
                                              +  - ]
                                 94         [ +  + ]:          94223 :         strncmp(pred, label, pred_len) == 0)
                                 95                 :           9195 :         return true;
                                 96         [ +  + ]:         188668 :     else if (!ci)
                                 97                 :         188638 :         return false;
                                 98                 :                : 
                                 99                 :                :     /*
                                100                 :                :      * Slow path for case-insensitive comparison: case fold and then compare.
                                101                 :                :      * This path is necessary even if pred_len > label_len, because the byte
                                102                 :                :      * lengths may change after casefolding.
                                103                 :                :      */
   89 jdavis@postgresql.or      104         [ +  + ]:GNC          30 :     if (!locale)
                                105                 :              1 :         locale = pg_database_locale();
                                106                 :                : 
   17 jdavis@postgresql.or      107                 :CBC          30 :     fpred = palloc(fpred_len + 1);
                                108                 :             30 :     len = pg_strfold(fpred, fpred_len + 1, pred, pred_len, locale);
                                109         [ -  + ]:             30 :     if (len > fpred_len)
                                110                 :                :     {
                                111                 :                :         /* grow buffer if needed and retry */
   17 jdavis@postgresql.or      112                 :UBC           0 :         fpred_len = len;
                                113                 :              0 :         fpred = repalloc(fpred, fpred_len + 1);
                                114                 :              0 :         len = pg_strfold(fpred, fpred_len + 1, pred, pred_len, locale);
                                115                 :                :     }
   17 jdavis@postgresql.or      116         [ -  + ]:CBC          30 :     Assert(len <= fpred_len);
                                117                 :             30 :     fpred_len = len;
                                118                 :                : 
                                119                 :             30 :     flabel = palloc(flabel_len + 1);
                                120                 :             30 :     len = pg_strfold(flabel, flabel_len + 1, label, label_len, locale);
                                121         [ -  + ]:             30 :     if (len > flabel_len)
                                122                 :                :     {
                                123                 :                :         /* grow buffer if needed and retry */
   17 jdavis@postgresql.or      124                 :UBC           0 :         flabel_len = len;
                                125                 :              0 :         flabel = repalloc(flabel, flabel_len + 1);
                                126                 :              0 :         len = pg_strfold(flabel, flabel_len + 1, label, label_len, locale);
                                127                 :                :     }
   17 jdavis@postgresql.or      128         [ -  + ]:CBC          30 :     Assert(len <= flabel_len);
                                129                 :             30 :     flabel_len = len;
                                130                 :                : 
                                131   [ +  +  +  +  :             30 :     if ((fpred_len == flabel_len || (prefix && fpred_len < flabel_len)) &&
                                              +  - ]
                                132         [ +  + ]:             25 :         strncmp(fpred, flabel, fpred_len) == 0)
                                133                 :             13 :         res = true;
                                134                 :                :     else
                                135                 :             17 :         res = false;
                                136                 :                : 
                                137                 :             30 :     pfree(fpred);
                                138                 :             30 :     pfree(flabel);
                                139                 :                : 
 6467 teodor@sigaev.ru          140                 :             30 :     return res;
                                141                 :                : }
                                142                 :                : 
                                143                 :                : /*
                                144                 :                :  * See if an lquery_level matches an ltree_level
                                145                 :                :  *
                                146                 :                :  * This accounts for all flags including LQL_NOT, but does not
                                147                 :                :  * consider repetition counts.
                                148                 :                :  */
                                149                 :                : static bool
 6121 bruce@momjian.us          150                 :         216853 : checkLevel(lquery_level *curq, ltree_level *curt)
                                151                 :                : {
 8629                           152                 :         216853 :     lquery_variant *curvar = LQL_FIRST(curq);
                                153                 :                :     bool        success;
                                154                 :                : 
 2175 tgl@sss.pgh.pa.us         155                 :         216853 :     success = (curq->flag & LQL_NOT) ? false : true;
                                156                 :                : 
                                157                 :                :     /* numvar == 0 means '*' which matches anything */
                                158         [ +  + ]:         216853 :     if (curq->numvar == 0)
                                159                 :          83195 :         return success;
                                160                 :                : 
                                161         [ +  + ]:         260767 :     for (int i = 0; i < curq->numvar; i++)
                                162                 :                :     {
   17 jdavis@postgresql.or      163                 :         133663 :         bool        prefix = (curvar->flag & LVAR_ANYEND);
                                164                 :         133663 :         bool        ci = (curvar->flag & LVAR_INCASE);
                                165                 :                : 
 7319 neilc@samurai.com         166         [ +  + ]:         133663 :         if (curvar->flag & LVAR_SUBLEXEME)
                                167                 :                :         {
   17 jdavis@postgresql.or      168         [ +  + ]:              4 :             if (compare_subnode(curt, curvar->name, curvar->len, prefix, ci))
 2175 tgl@sss.pgh.pa.us         169                 :              3 :                 return success;
                                170                 :                :         }
   17 jdavis@postgresql.or      171         [ +  + ]:         133659 :         else if (ltree_label_match(curvar->name, curvar->len, curt->name,
                                172                 :         133659 :                                    curt->len, prefix, ci))
 2175 tgl@sss.pgh.pa.us         173                 :           6551 :             return success;
                                174                 :                : 
 8593 bruce@momjian.us          175                 :         127109 :         curvar = LVAR_NEXT(curvar);
                                176                 :                :     }
 2175 tgl@sss.pgh.pa.us         177                 :         127104 :     return !success;
                                178                 :                : }
                                179                 :                : 
                                180                 :                : /*
                                181                 :                :  * Try to match an lquery (of qlen items) to an ltree (of tlen items)
                                182                 :                :  */
                                183                 :                : static bool
                                184                 :         143524 : checkCond(lquery_level *curq, int qlen,
                                185                 :                :           ltree_level *curt, int tlen)
                                186                 :                : {
                                187                 :                :     /* Since this function recurses, it could be driven to stack overflow */
                                188                 :         143524 :     check_stack_depth();
                                189                 :                : 
                                190                 :                :     /* Pathological patterns could take awhile, too */
                                191         [ -  + ]:         143524 :     CHECK_FOR_INTERRUPTS();
                                192                 :                : 
                                193                 :                :     /* Loop while we have query items to consider */
                                194         [ +  + ]:         162803 :     while (qlen > 0)
                                195                 :                :     {
                                196                 :                :         int         low,
                                197                 :                :                     high;
                                198                 :                :         lquery_level *nextq;
                                199                 :                : 
                                200                 :                :         /*
                                201                 :                :          * Get min and max repetition counts for this query item, dealing with
                                202                 :                :          * the backwards-compatibility hack that the low/high fields aren't
                                203                 :                :          * meaningful for non-'*' items unless LQL_COUNT is set.
                                204                 :                :          */
                                205   [ +  +  +  + ]:         159090 :         if ((curq->flag & LQL_COUNT) || curq->numvar == 0)
                                206                 :          13307 :             low = curq->low, high = curq->high;
                                207                 :                :         else
                                208                 :         145783 :             low = high = 1;
                                209                 :                : 
                                210                 :                :         /*
                                211                 :                :          * We may limit "high" to the remaining text length; this avoids
                                212                 :                :          * separate tests below.
                                213                 :                :          */
                                214         [ +  + ]:         159090 :         if (high > tlen)
                                215                 :          24988 :             high = tlen;
                                216                 :                : 
                                217                 :                :         /* Fail if a match of required number of items is impossible */
                                218         [ +  + ]:         159090 :         if (high < low)
                                219                 :          12173 :             return false;
                                220                 :                : 
                                221                 :                :         /*
                                222                 :                :          * Recursively check the rest of the pattern against each possible
                                223                 :                :          * start point following some of this item's match(es).
                                224                 :                :          */
                                225                 :         146917 :         nextq = LQL_NEXT(curq);
 8629 bruce@momjian.us          226                 :         146917 :         qlen--;
                                227                 :                : 
 2175 tgl@sss.pgh.pa.us         228         [ +  + ]:         236726 :         for (int matchcnt = 0; matchcnt < high; matchcnt++)
                                229                 :                :         {
                                230                 :                :             /*
                                231                 :                :              * If we've consumed an acceptable number of matches of this item,
                                232                 :                :              * and the rest of the pattern matches beginning here, we're good.
                                233                 :                :              */
                                234   [ +  +  +  + ]:         217447 :             if (matchcnt >= low && checkCond(nextq, qlen, curt, tlen))
                                235                 :            594 :                 return true;
                                236                 :                : 
                                237                 :                :             /*
                                238                 :                :              * Otherwise, try to match one more text item to this query item.
                                239                 :                :              */
                                240         [ +  + ]:         216853 :             if (!checkLevel(curq, curt))
 2178                           241                 :         127044 :                 return false;
                                242                 :                : 
 2175                           243                 :          89809 :             curt = LEVEL_NEXT(curt);
                                244                 :          89809 :             tlen--;
                                245                 :                :         }
                                246                 :                : 
                                247                 :                :         /*
                                248                 :                :          * Once we've consumed "high" matches, we can succeed only if the rest
                                249                 :                :          * of the pattern matches beginning here.  Loop around (if you prefer,
                                250                 :                :          * think of this as tail recursion).
                                251                 :                :          */
                                252                 :          19279 :         curq = nextq;
                                253                 :                :     }
                                254                 :                : 
                                255                 :                :     /*
                                256                 :                :      * Once we're out of query items, we match only if there's no remaining
                                257                 :                :      * text either.
                                258                 :                :      */
                                259                 :           3713 :     return (tlen == 0);
                                260                 :                : }
                                261                 :                : 
                                262                 :                : Datum
 8593 bruce@momjian.us          263                 :          60206 : ltq_regex(PG_FUNCTION_ARGS)
                                264                 :                : {
 3100 tgl@sss.pgh.pa.us         265                 :          60206 :     ltree      *tree = PG_GETARG_LTREE_P(0);
                                266                 :          60206 :     lquery     *query = PG_GETARG_LQUERY_P(1);
                                267                 :                :     bool        res;
                                268                 :                : 
 2175                           269                 :          60206 :     res = checkCond(LQUERY_FIRST(query), query->numlevel,
                                270                 :          60206 :                     LTREE_FIRST(tree), tree->numlevel);
                                271                 :                : 
 8593 bruce@momjian.us          272         [ +  + ]:          60206 :     PG_FREE_IF_COPY(tree, 0);
                                273         [ -  + ]:          60206 :     PG_FREE_IF_COPY(query, 1);
                                274                 :          60206 :     PG_RETURN_BOOL(res);
                                275                 :                : }
                                276                 :                : 
                                277                 :                : Datum
 8593 bruce@momjian.us          278                 :UBC           0 : ltq_rregex(PG_FUNCTION_ARGS)
                                279                 :                : {
                                280                 :              0 :     PG_RETURN_DATUM(DirectFunctionCall2(ltq_regex,
                                281                 :                :                                         PG_GETARG_DATUM(1),
                                282                 :                :                                         PG_GETARG_DATUM(0)
                                283                 :                :                                         ));
                                284                 :                : }
                                285                 :                : 
                                286                 :                : Datum
 8425 bruce@momjian.us          287                 :CBC        1587 : lt_q_regex(PG_FUNCTION_ARGS)
                                288                 :                : {
 3100 tgl@sss.pgh.pa.us         289                 :           1587 :     ltree      *tree = PG_GETARG_LTREE_P(0);
 8259 bruce@momjian.us          290                 :           1587 :     ArrayType  *_query = PG_GETARG_ARRAYTYPE_P(1);
                                291         [ -  + ]:           1587 :     lquery     *query = (lquery *) ARR_DATA_PTR(_query);
                                292                 :           1587 :     bool        res = false;
                                293                 :           1587 :     int         num = ArrayGetNItems(ARR_NDIM(_query), ARR_DIMS(_query));
                                294                 :                : 
 5863 tgl@sss.pgh.pa.us         295         [ -  + ]:           1587 :     if (ARR_NDIM(_query) > 1)
 8259 bruce@momjian.us          296         [ #  # ]:UBC           0 :         ereport(ERROR,
                                297                 :                :                 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
                                298                 :                :                  errmsg("array must be one-dimensional")));
 5544 tgl@sss.pgh.pa.us         299         [ -  + ]:CBC        1587 :     if (array_contains_nulls(_query))
 7421 tgl@sss.pgh.pa.us         300         [ #  # ]:UBC           0 :         ereport(ERROR,
                                301                 :                :                 (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
                                302                 :                :                  errmsg("array must not contain nulls")));
                                303                 :                : 
 8259 bruce@momjian.us          304         [ +  + ]:CBC        4736 :     while (num > 0)
                                305                 :                :     {
 8425                           306         [ +  + ]:           3163 :         if (DatumGetBool(DirectFunctionCall2(ltq_regex,
                                307                 :                :                                              PointerGetDatum(tree), PointerGetDatum(query))))
                                308                 :                :         {
                                309                 :                : 
                                310                 :             14 :             res = true;
                                311                 :             14 :             break;
                                312                 :                :         }
                                313                 :           3149 :         num--;
                                314                 :           3149 :         query = NEXTVAL(query);
                                315                 :                :     }
                                316                 :                : 
                                317         [ +  + ]:           1587 :     PG_FREE_IF_COPY(tree, 0);
                                318         [ -  + ]:           1587 :     PG_FREE_IF_COPY(_query, 1);
                                319                 :           1587 :     PG_RETURN_BOOL(res);
                                320                 :                : }
                                321                 :                : 
                                322                 :                : Datum
 8425 bruce@momjian.us          323                 :UBC           0 : lt_q_rregex(PG_FUNCTION_ARGS)
                                324                 :                : {
                                325                 :              0 :     PG_RETURN_DATUM(DirectFunctionCall2(lt_q_regex,
                                326                 :                :                                         PG_GETARG_DATUM(1),
                                327                 :                :                                         PG_GETARG_DATUM(0)
                                328                 :                :                                         ));
                                329                 :                : }
        

Generated by: LCOV version 2.4-beta