LCOV - code coverage report
Current view: top level - contrib/ltree - lquery_op.c (source / functions) Hit Total Coverage
Test: PostgreSQL 13devel Lines: 175 182 96.2 %
Date: 2019-09-19 02:07:14 Functions: 11 13 84.6 %
Legend: Lines: hit not hit

          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 "utils/formatting.h"
      12             : #include "ltree.h"
      13             : 
      14           6 : PG_FUNCTION_INFO_V1(ltq_regex);
      15           4 : PG_FUNCTION_INFO_V1(ltq_rregex);
      16             : 
      17           6 : PG_FUNCTION_INFO_V1(lt_q_regex);
      18           4 : PG_FUNCTION_INFO_V1(lt_q_rregex);
      19             : 
      20             : #define NEXTVAL(x) ( (lquery*)( (char*)(x) + INTALIGN( VARSIZE(x) ) ) )
      21             : 
      22             : typedef struct
      23             : {
      24             :     lquery_level *q;
      25             :     int         nq;
      26             :     ltree_level *t;
      27             :     int         nt;
      28             :     int         posq;
      29             :     int         post;
      30             : } FieldNot;
      31             : 
      32             : static char *
      33          66 : getlexeme(char *start, char *end, int *len)
      34             : {
      35             :     char       *ptr;
      36             :     int         charlen;
      37             : 
      38         148 :     while (start < end && (charlen = pg_mblen(start)) == 1 && t_iseq(start, '_'))
      39          16 :         start += charlen;
      40             : 
      41          66 :     ptr = start;
      42          66 :     if (ptr >= end)
      43          14 :         return NULL;
      44             : 
      45         266 :     while (ptr < end && !((charlen = pg_mblen(ptr)) == 1 && t_iseq(ptr, '_')))
      46         162 :         ptr += charlen;
      47             : 
      48          52 :     *len = ptr - start;
      49          52 :     return start;
      50             : }
      51             : 
      52             : bool
      53          14 : compare_subnode(ltree_level *t, char *qn, int len, int (*cmpptr) (const char *, const char *, size_t), bool anyend)
      54             : {
      55          14 :     char       *endt = t->name + t->len;
      56          14 :     char       *endq = qn + len;
      57             :     char       *tn;
      58             :     int         lent,
      59             :                 lenq;
      60             :     bool        isok;
      61             : 
      62          42 :     while ((qn = getlexeme(qn, endq, &lenq)) != NULL)
      63             :     {
      64          22 :         tn = t->name;
      65          22 :         isok = false;
      66          60 :         while ((tn = getlexeme(tn, endt, &lent)) != NULL)
      67             :         {
      68          30 :             if (
      69             :                 (
      70          60 :                  lent == lenq ||
      71          60 :                  (lent > lenq && anyend)
      72          30 :                  ) &&
      73          30 :                 (*cmpptr) (qn, tn, lenq) == 0)
      74             :             {
      75             : 
      76          14 :                 isok = true;
      77          14 :                 break;
      78             :             }
      79          16 :             tn += lent;
      80             :         }
      81             : 
      82          22 :         if (!isok)
      83           8 :             return false;
      84          14 :         qn += lenq;
      85             :     }
      86             : 
      87           6 :     return true;
      88             : }
      89             : 
      90             : int
      91          50 : ltree_strncasecmp(const char *a, const char *b, size_t s)
      92             : {
      93          50 :     char       *al = str_tolower(a, s, DEFAULT_COLLATION_OID);
      94          50 :     char       *bl = str_tolower(b, s, DEFAULT_COLLATION_OID);
      95             :     int         res;
      96             : 
      97          50 :     res = strncmp(al, bl, s);
      98             : 
      99          50 :     pfree(al);
     100          50 :     pfree(bl);
     101             : 
     102          50 :     return res;
     103             : }
     104             : 
     105             : static bool
     106      203130 : checkLevel(lquery_level *curq, ltree_level *curt)
     107             : {
     108             :     int         (*cmpptr) (const char *, const char *, size_t);
     109      203130 :     lquery_variant *curvar = LQL_FIRST(curq);
     110             :     int         i;
     111             : 
     112      397196 :     for (i = 0; i < curq->numvar; i++)
     113             :     {
     114      203140 :         cmpptr = (curvar->flag & LVAR_INCASE) ? ltree_strncasecmp : strncmp;
     115             : 
     116      203140 :         if (curvar->flag & LVAR_SUBLEXEME)
     117             :         {
     118           6 :             if (compare_subnode(curt, curvar->name, curvar->len, cmpptr, (curvar->flag & LVAR_ANYEND)))
     119           4 :                 return true;
     120             :         }
     121      203134 :         else if (
     122             :                  (
     123      321624 :                   curvar->len == curt->len ||
     124      218374 :                   (curt->len > curvar->len && (curvar->flag & LVAR_ANYEND))
     125       84650 :                   ) &&
     126       84650 :                  (*cmpptr) (curvar->name, curt->name, curvar->len) == 0)
     127             :         {
     128             : 
     129        9070 :             return true;
     130             :         }
     131      194066 :         curvar = LVAR_NEXT(curvar);
     132             :     }
     133      194056 :     return false;
     134             : }
     135             : 
     136             : /*
     137             : void
     138             : printFieldNot(FieldNot *fn ) {
     139             :     while(fn->q) {
     140             :         elog(NOTICE,"posQ:%d lenQ:%d posT:%d lenT:%d", fn->posq,fn->nq,fn->post,fn->nt);
     141             :         fn++;
     142             :     }
     143             : }
     144             : */
     145             : 
     146             : static struct
     147             : {
     148             :     bool        muse;
     149             :     uint32      high_pos;
     150             : }           SomeStack =
     151             : 
     152             : {
     153             :     false, 0,
     154             : };
     155             : 
     156             : static bool
     157       98860 : checkCond(lquery_level *curq, int query_numlevel, ltree_level *curt, int tree_numlevel, FieldNot *ptr)
     158             : {
     159       98860 :     uint32      low_pos = 0,
     160       98860 :                 high_pos = 0,
     161       98860 :                 cur_tpos = 0;
     162       98860 :     int         tlen = tree_numlevel,
     163       98860 :                 qlen = query_numlevel;
     164             :     int         isok;
     165       98860 :     lquery_level *prevq = NULL;
     166       98860 :     ltree_level *prevt = NULL;
     167             : 
     168       98860 :     if (SomeStack.muse)
     169             :     {
     170        3824 :         high_pos = SomeStack.high_pos;
     171        3824 :         qlen--;
     172        3824 :         prevq = curq;
     173        3824 :         curq = LQL_NEXT(curq);
     174        3824 :         SomeStack.muse = false;
     175             :     }
     176             : 
     177      226314 :     while (tlen > 0 && qlen > 0)
     178             :     {
     179      122890 :         if (curq->numvar)
     180             :         {
     181      103186 :             prevt = curt;
     182      207124 :             while (cur_tpos < low_pos)
     183             :             {
     184         774 :                 curt = LEVEL_NEXT(curt);
     185         774 :                 tlen--;
     186         774 :                 cur_tpos++;
     187         774 :                 if (tlen == 0)
     188          22 :                     return false;
     189         752 :                 if (ptr && ptr->q)
     190          16 :                     ptr->nt++;
     191             :             }
     192             : 
     193      103164 :             if (ptr && curq->flag & LQL_NOT)
     194             :             {
     195         116 :                 if (!(prevq && prevq->numvar == 0))
     196          36 :                     prevq = curq;
     197         116 :                 if (ptr->q == NULL)
     198             :                 {
     199          80 :                     ptr->t = prevt;
     200          80 :                     ptr->q = prevq;
     201          80 :                     ptr->nt = 1;
     202          80 :                     ptr->nq = 1 + ((prevq == curq) ? 0 : 1);
     203          80 :                     ptr->posq = query_numlevel - qlen - ((prevq == curq) ? 0 : 1);
     204          80 :                     ptr->post = cur_tpos;
     205             :                 }
     206             :                 else
     207             :                 {
     208          36 :                     ptr->nt++;
     209          36 :                     ptr->nq++;
     210             :                 }
     211             : 
     212         116 :                 if (qlen == 1 && ptr->q->numvar == 0)
     213           8 :                     ptr->nt = tree_numlevel - ptr->post;
     214         116 :                 curt = LEVEL_NEXT(curt);
     215         116 :                 tlen--;
     216         116 :                 cur_tpos++;
     217         232 :                 if (high_pos < cur_tpos)
     218          64 :                     high_pos++;
     219             :             }
     220             :             else
     221             :             {
     222      103048 :                 isok = false;
     223      408934 :                 while (cur_tpos <= high_pos && tlen > 0 && !isok)
     224             :                 {
     225      203130 :                     isok = checkLevel(curq, curt);
     226      203130 :                     curt = LEVEL_NEXT(curt);
     227      203130 :                     tlen--;
     228      203130 :                     cur_tpos++;
     229      203130 :                     if (isok && prevq && prevq->numvar == 0 && tlen > 0 && cur_tpos <= high_pos)
     230             :                     {
     231             :                         FieldNot    tmpptr;
     232             : 
     233        3824 :                         if (ptr)
     234          14 :                             memcpy(&tmpptr, ptr, sizeof(FieldNot));
     235        3824 :                         SomeStack.high_pos = high_pos - cur_tpos;
     236        3824 :                         SomeStack.muse = true;
     237        3824 :                         if (checkCond(prevq, qlen + 1, curt, tlen, (ptr) ? &tmpptr : NULL))
     238         292 :                             return true;
     239             :                     }
     240      202838 :                     if (!isok && ptr)
     241          78 :                         ptr->nt++;
     242             :                 }
     243      102756 :                 if (!isok)
     244       93974 :                     return false;
     245             : 
     246        8782 :                 if (ptr && ptr->q)
     247             :                 {
     248          28 :                     if (checkCond(ptr->q, ptr->nq, ptr->t, ptr->nt, NULL))
     249           8 :                         return false;
     250          20 :                     ptr->q = NULL;
     251             :                 }
     252        8774 :                 low_pos = cur_tpos;
     253        8774 :                 high_pos = cur_tpos;
     254             :             }
     255             :         }
     256             :         else
     257             :         {
     258       19704 :             low_pos = cur_tpos + curq->low;
     259       19704 :             high_pos = cur_tpos + curq->high;
     260       19704 :             if (ptr && ptr->q)
     261             :             {
     262          98 :                 ptr->nq++;
     263          98 :                 if (qlen == 1)
     264          42 :                     ptr->nt = tree_numlevel - ptr->post;
     265             :             }
     266             :         }
     267             : 
     268       28594 :         prevq = curq;
     269       28594 :         curq = LQL_NEXT(curq);
     270       28594 :         qlen--;
     271             :     }
     272             : 
     273        4564 :     if (low_pos > tree_numlevel || tree_numlevel > high_pos)
     274        3508 :         return false;
     275             : 
     276        2156 :     while (qlen > 0)
     277             :     {
     278         102 :         if (curq->numvar)
     279             :         {
     280          58 :             if (!(curq->flag & LQL_NOT))
     281          58 :                 return false;
     282             :         }
     283             :         else
     284             :         {
     285          44 :             low_pos = cur_tpos + curq->low;
     286          44 :             high_pos = cur_tpos + curq->high;
     287             :         }
     288             : 
     289          44 :         curq = LQL_NEXT(curq);
     290          44 :         qlen--;
     291             :     }
     292             : 
     293         998 :     if (low_pos > tree_numlevel || tree_numlevel > high_pos)
     294           0 :         return false;
     295             : 
     296         998 :     if (ptr && ptr->q && checkCond(ptr->q, ptr->nq, ptr->t, ptr->nt, NULL))
     297          22 :         return false;
     298             : 
     299         976 :     return true;
     300             : }
     301             : 
     302             : Datum
     303       94958 : ltq_regex(PG_FUNCTION_ARGS)
     304             : {
     305       94958 :     ltree      *tree = PG_GETARG_LTREE_P(0);
     306       94958 :     lquery     *query = PG_GETARG_LQUERY_P(1);
     307       94958 :     bool        res = false;
     308             : 
     309       94958 :     if (query->flag & LQUERY_HASNOT)
     310             :     {
     311             :         FieldNot    fn;
     312             : 
     313          80 :         fn.q = NULL;
     314             : 
     315          80 :         res = checkCond(LQUERY_FIRST(query), query->numlevel,
     316          80 :                         LTREE_FIRST(tree), tree->numlevel, &fn);
     317             :     }
     318             :     else
     319             :     {
     320       94878 :         res = checkCond(LQUERY_FIRST(query), query->numlevel,
     321       94878 :                         LTREE_FIRST(tree), tree->numlevel, NULL);
     322             :     }
     323             : 
     324       94958 :     PG_FREE_IF_COPY(tree, 0);
     325       94958 :     PG_FREE_IF_COPY(query, 1);
     326       94958 :     PG_RETURN_BOOL(res);
     327             : }
     328             : 
     329             : Datum
     330           0 : ltq_rregex(PG_FUNCTION_ARGS)
     331             : {
     332           0 :     PG_RETURN_DATUM(DirectFunctionCall2(ltq_regex,
     333             :                                         PG_GETARG_DATUM(1),
     334             :                                         PG_GETARG_DATUM(0)
     335             :                                         ));
     336             : }
     337             : 
     338             : Datum
     339        2628 : lt_q_regex(PG_FUNCTION_ARGS)
     340             : {
     341        2628 :     ltree      *tree = PG_GETARG_LTREE_P(0);
     342        2628 :     ArrayType  *_query = PG_GETARG_ARRAYTYPE_P(1);
     343        2628 :     lquery     *query = (lquery *) ARR_DATA_PTR(_query);
     344        2628 :     bool        res = false;
     345        2628 :     int         num = ArrayGetNItems(ARR_NDIM(_query), ARR_DIMS(_query));
     346             : 
     347        2628 :     if (ARR_NDIM(_query) > 1)
     348           0 :         ereport(ERROR,
     349             :                 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
     350             :                  errmsg("array must be one-dimensional")));
     351        2628 :     if (array_contains_nulls(_query))
     352           0 :         ereport(ERROR,
     353             :                 (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
     354             :                  errmsg("array must not contain nulls")));
     355             : 
     356       10476 :     while (num > 0)
     357             :     {
     358        5240 :         if (DatumGetBool(DirectFunctionCall2(ltq_regex,
     359             :                                              PointerGetDatum(tree), PointerGetDatum(query))))
     360             :         {
     361             : 
     362          20 :             res = true;
     363          20 :             break;
     364             :         }
     365        5220 :         num--;
     366        5220 :         query = NEXTVAL(query);
     367             :     }
     368             : 
     369        2628 :     PG_FREE_IF_COPY(tree, 0);
     370        2628 :     PG_FREE_IF_COPY(_query, 1);
     371        2628 :     PG_RETURN_BOOL(res);
     372             : }
     373             : 
     374             : Datum
     375           0 : lt_q_rregex(PG_FUNCTION_ARGS)
     376             : {
     377           0 :     PG_RETURN_DATUM(DirectFunctionCall2(lt_q_regex,
     378             :                                         PG_GETARG_DATUM(1),
     379             :                                         PG_GETARG_DATUM(0)
     380             :                                         ));
     381             : }

Generated by: LCOV version 1.13