LCOV - code coverage report
Current view: top level - src/backend/tsearch - regis.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 78.4 % 125 98
Test Date: 2026-03-27 22:16:19 Functions: 83.3 % 6 5
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /*-------------------------------------------------------------------------
       2              :  *
       3              :  * regis.c
       4              :  *      Fast regex subset
       5              :  *
       6              :  * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
       7              :  *
       8              :  *
       9              :  * IDENTIFICATION
      10              :  *    src/backend/tsearch/regis.c
      11              :  *
      12              :  *-------------------------------------------------------------------------
      13              :  */
      14              : 
      15              : #include "postgres.h"
      16              : 
      17              : #include "tsearch/dicts/regis.h"
      18              : #include "tsearch/ts_locale.h"
      19              : 
      20              : #define RS_IN_ONEOF 1
      21              : #define RS_IN_ONEOF_IN  2
      22              : #define RS_IN_NONEOF    3
      23              : #define RS_IN_WAIT  4
      24              : 
      25              : 
      26              : /*
      27              :  * Test whether a regex is of the subset supported here.
      28              :  * Keep this in sync with RS_compile!
      29              :  */
      30              : bool
      31          522 : RS_isRegis(const char *str)
      32              : {
      33          522 :     int         state = RS_IN_WAIT;
      34          522 :     const char *c = str;
      35              : 
      36         3162 :     while (*c)
      37              :     {
      38         2726 :         if (state == RS_IN_WAIT)
      39              :         {
      40          691 :             if (t_isalpha_cstr(c))
      41              :                  /* okay */ ;
      42          543 :             else if (t_iseq(c, '['))
      43          457 :                 state = RS_IN_ONEOF;
      44              :             else
      45           86 :                 return false;
      46              :         }
      47         2035 :         else if (state == RS_IN_ONEOF)
      48              :         {
      49          457 :             if (t_iseq(c, '^'))
      50          457 :                 state = RS_IN_NONEOF;
      51            0 :             else if (t_isalpha_cstr(c))
      52            0 :                 state = RS_IN_ONEOF_IN;
      53              :             else
      54            0 :                 return false;
      55              :         }
      56         1578 :         else if (state == RS_IN_ONEOF_IN || state == RS_IN_NONEOF)
      57              :         {
      58         2035 :             if (t_isalpha_cstr(c))
      59              :                  /* okay */ ;
      60          457 :             else if (t_iseq(c, ']'))
      61          457 :                 state = RS_IN_WAIT;
      62              :             else
      63            0 :                 return false;
      64              :         }
      65              :         else
      66            0 :             elog(ERROR, "internal error in RS_isRegis: state %d", state);
      67         2640 :         c += pg_mblen_cstr(c);
      68              :     }
      69              : 
      70          436 :     return (state == RS_IN_WAIT);
      71              : }
      72              : 
      73              : static RegisNode *
      74          498 : newRegisNode(RegisNode *prev, int len)
      75              : {
      76              :     RegisNode  *ptr;
      77              : 
      78          498 :     ptr = (RegisNode *) palloc0(RNHDRSZ + len + 1);
      79          498 :     if (prev)
      80           62 :         prev->next = ptr;
      81          498 :     return ptr;
      82              : }
      83              : 
      84              : void
      85          436 : RS_compile(Regis *r, bool issuffix, const char *str)
      86              : {
      87          436 :     int         len = strlen(str);
      88          436 :     int         state = RS_IN_WAIT;
      89          436 :     const char *c = str;
      90          436 :     RegisNode  *ptr = NULL;
      91              : 
      92          436 :     memset(r, 0, sizeof(Regis));
      93          436 :     r->issuffix = (issuffix) ? 1 : 0;
      94              : 
      95         2822 :     while (*c)
      96              :     {
      97         2386 :         if (state == RS_IN_WAIT)
      98              :         {
      99          498 :             if (t_isalpha_cstr(c))
     100              :             {
     101           62 :                 if (ptr)
     102           62 :                     ptr = newRegisNode(ptr, len);
     103              :                 else
     104            0 :                     ptr = r->node = newRegisNode(NULL, len);
     105           62 :                 ptr->type = RSF_ONEOF;
     106           62 :                 ptr->len = ts_copychar_cstr(ptr->data, c);
     107              :             }
     108          436 :             else if (t_iseq(c, '['))
     109              :             {
     110          436 :                 if (ptr)
     111            0 :                     ptr = newRegisNode(ptr, len);
     112              :                 else
     113          436 :                     ptr = r->node = newRegisNode(NULL, len);
     114          436 :                 ptr->type = RSF_ONEOF;
     115          436 :                 state = RS_IN_ONEOF;
     116              :             }
     117              :             else                /* shouldn't get here */
     118            0 :                 elog(ERROR, "invalid regis pattern: \"%s\"", str);
     119              :         }
     120         1888 :         else if (state == RS_IN_ONEOF)
     121              :         {
     122          436 :             if (t_iseq(c, '^'))
     123              :             {
     124          436 :                 ptr->type = RSF_NONEOF;
     125          436 :                 state = RS_IN_NONEOF;
     126              :             }
     127            0 :             else if (t_isalpha_cstr(c))
     128              :             {
     129            0 :                 ptr->len = ts_copychar_cstr(ptr->data, c);
     130            0 :                 state = RS_IN_ONEOF_IN;
     131              :             }
     132              :             else                /* shouldn't get here */
     133            0 :                 elog(ERROR, "invalid regis pattern: \"%s\"", str);
     134              :         }
     135         1452 :         else if (state == RS_IN_ONEOF_IN || state == RS_IN_NONEOF)
     136              :         {
     137         2904 :             if (t_isalpha_cstr(c))
     138         1016 :                 ptr->len += ts_copychar_cstr(ptr->data + ptr->len, c);
     139          436 :             else if (t_iseq(c, ']'))
     140          436 :                 state = RS_IN_WAIT;
     141              :             else                /* shouldn't get here */
     142            0 :                 elog(ERROR, "invalid regis pattern: \"%s\"", str);
     143              :         }
     144              :         else
     145            0 :             elog(ERROR, "internal error in RS_compile: state %d", state);
     146         2386 :         c += pg_mblen_cstr(c);
     147              :     }
     148              : 
     149          436 :     if (state != RS_IN_WAIT)    /* shouldn't get here */
     150            0 :         elog(ERROR, "invalid regis pattern: \"%s\"", str);
     151              : 
     152          436 :     ptr = r->node;
     153          934 :     while (ptr)
     154              :     {
     155          498 :         r->nchar++;
     156          498 :         ptr = ptr->next;
     157              :     }
     158          436 : }
     159              : 
     160              : void
     161            0 : RS_free(Regis *r)
     162              : {
     163            0 :     RegisNode  *ptr = r->node,
     164              :                *tmp;
     165              : 
     166            0 :     while (ptr)
     167              :     {
     168            0 :         tmp = ptr->next;
     169            0 :         pfree(ptr);
     170            0 :         ptr = tmp;
     171              :     }
     172              : 
     173            0 :     r->node = NULL;
     174            0 : }
     175              : 
     176              : static bool
     177          590 : mb_strchr(char *str, char *c)
     178              : {
     179              :     int         clen,
     180              :                 plen,
     181              :                 i;
     182          590 :     char       *ptr = str;
     183          590 :     bool        res = false;
     184              : 
     185          590 :     clen = pg_mblen_cstr(c);
     186         1940 :     while (*ptr && !res)
     187              :     {
     188         1350 :         plen = pg_mblen_cstr(ptr);
     189         1350 :         if (plen == clen)
     190              :         {
     191         1350 :             i = plen;
     192         1350 :             res = true;
     193         1380 :             while (i--)
     194         1350 :                 if (*(ptr + i) != *(c + i))
     195              :                 {
     196         1320 :                     res = false;
     197         1320 :                     break;
     198              :                 }
     199              :         }
     200              : 
     201         1350 :         ptr += plen;
     202              :     }
     203              : 
     204          590 :     return res;
     205              : }
     206              : 
     207              : bool
     208          590 : RS_execute(Regis *r, char *str)
     209              : {
     210          590 :     RegisNode  *ptr = r->node;
     211          590 :     char       *c = str;
     212          590 :     int         len = 0;
     213              : 
     214         3900 :     while (*c)
     215              :     {
     216         3310 :         len++;
     217         3310 :         c += pg_mblen_cstr(c);
     218              :     }
     219              : 
     220          590 :     if (len < r->nchar)
     221           30 :         return 0;
     222              : 
     223          560 :     c = str;
     224          560 :     if (r->issuffix)
     225              :     {
     226          560 :         len -= r->nchar;
     227         3280 :         while (len-- > 0)
     228         2720 :             c += pg_mblen_cstr(c);
     229              :     }
     230              : 
     231              : 
     232         1150 :     while (ptr)
     233              :     {
     234          590 :         switch (ptr->type)
     235              :         {
     236           30 :             case RSF_ONEOF:
     237           30 :                 if (!mb_strchr((char *) ptr->data, c))
     238            0 :                     return false;
     239           30 :                 break;
     240          560 :             case RSF_NONEOF:
     241          560 :                 if (mb_strchr((char *) ptr->data, c))
     242            0 :                     return false;
     243          560 :                 break;
     244            0 :             default:
     245            0 :                 elog(ERROR, "unrecognized regis node type: %d", ptr->type);
     246              :         }
     247          590 :         ptr = ptr->next;
     248          590 :         c += pg_mblen_cstr(c);
     249              :     }
     250              : 
     251          560 :     return true;
     252              : }
        

Generated by: LCOV version 2.0-1