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-02 04:14:39 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          402 : RS_isRegis(const char *str)
      32              : {
      33          402 :     int         state = RS_IN_WAIT;
      34          402 :     const char *c = str;
      35              : 
      36         2436 :     while (*c)
      37              :     {
      38         2100 :         if (state == RS_IN_WAIT)
      39              :         {
      40          532 :             if (t_isalpha_cstr(c))
      41              :                  /* okay */ ;
      42          418 :             else if (t_iseq(c, '['))
      43          352 :                 state = RS_IN_ONEOF;
      44              :             else
      45           66 :                 return false;
      46              :         }
      47         1568 :         else if (state == RS_IN_ONEOF)
      48              :         {
      49          352 :             if (t_iseq(c, '^'))
      50          352 :                 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         1216 :         else if (state == RS_IN_ONEOF_IN || state == RS_IN_NONEOF)
      57              :         {
      58         1568 :             if (t_isalpha_cstr(c))
      59              :                  /* okay */ ;
      60          352 :             else if (t_iseq(c, ']'))
      61          352 :                 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         2034 :         c += pg_mblen_cstr(c);
      68              :     }
      69              : 
      70          336 :     return (state == RS_IN_WAIT);
      71              : }
      72              : 
      73              : static RegisNode *
      74          384 : newRegisNode(RegisNode *prev, int len)
      75              : {
      76              :     RegisNode  *ptr;
      77              : 
      78          384 :     ptr = (RegisNode *) palloc0(RNHDRSZ + len + 1);
      79          384 :     if (prev)
      80           48 :         prev->next = ptr;
      81          384 :     return ptr;
      82              : }
      83              : 
      84              : void
      85          336 : RS_compile(Regis *r, bool issuffix, const char *str)
      86              : {
      87          336 :     int         len = strlen(str);
      88          336 :     int         state = RS_IN_WAIT;
      89          336 :     const char *c = str;
      90          336 :     RegisNode  *ptr = NULL;
      91              : 
      92          336 :     memset(r, 0, sizeof(Regis));
      93          336 :     r->issuffix = (issuffix) ? 1 : 0;
      94              : 
      95         2176 :     while (*c)
      96              :     {
      97         1840 :         if (state == RS_IN_WAIT)
      98              :         {
      99          384 :             if (t_isalpha_cstr(c))
     100              :             {
     101           48 :                 if (ptr)
     102           48 :                     ptr = newRegisNode(ptr, len);
     103              :                 else
     104            0 :                     ptr = r->node = newRegisNode(NULL, len);
     105           48 :                 ptr->type = RSF_ONEOF;
     106           48 :                 ptr->len = ts_copychar_cstr(ptr->data, c);
     107              :             }
     108          336 :             else if (t_iseq(c, '['))
     109              :             {
     110          336 :                 if (ptr)
     111            0 :                     ptr = newRegisNode(ptr, len);
     112              :                 else
     113          336 :                     ptr = r->node = newRegisNode(NULL, len);
     114          336 :                 ptr->type = RSF_ONEOF;
     115          336 :                 state = RS_IN_ONEOF;
     116              :             }
     117              :             else                /* shouldn't get here */
     118            0 :                 elog(ERROR, "invalid regis pattern: \"%s\"", str);
     119              :         }
     120         1456 :         else if (state == RS_IN_ONEOF)
     121              :         {
     122          336 :             if (t_iseq(c, '^'))
     123              :             {
     124          336 :                 ptr->type = RSF_NONEOF;
     125          336 :                 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         1120 :         else if (state == RS_IN_ONEOF_IN || state == RS_IN_NONEOF)
     136              :         {
     137         2240 :             if (t_isalpha_cstr(c))
     138          784 :                 ptr->len += ts_copychar_cstr(ptr->data + ptr->len, c);
     139          336 :             else if (t_iseq(c, ']'))
     140          336 :                 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         1840 :         c += pg_mblen_cstr(c);
     147              :     }
     148              : 
     149          336 :     if (state != RS_IN_WAIT)    /* shouldn't get here */
     150            0 :         elog(ERROR, "invalid regis pattern: \"%s\"", str);
     151              : 
     152          336 :     ptr = r->node;
     153          720 :     while (ptr)
     154              :     {
     155          384 :         r->nchar++;
     156          384 :         ptr = ptr->next;
     157              :     }
     158          336 : }
     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          354 : mb_strchr(char *str, char *c)
     178              : {
     179              :     int         clen,
     180              :                 plen,
     181              :                 i;
     182          354 :     char       *ptr = str;
     183          354 :     bool        res = false;
     184              : 
     185          354 :     clen = pg_mblen_cstr(c);
     186         1164 :     while (*ptr && !res)
     187              :     {
     188          810 :         plen = pg_mblen_cstr(ptr);
     189          810 :         if (plen == clen)
     190              :         {
     191          810 :             i = plen;
     192          810 :             res = true;
     193          828 :             while (i--)
     194          810 :                 if (*(ptr + i) != *(c + i))
     195              :                 {
     196          792 :                     res = false;
     197          792 :                     break;
     198              :                 }
     199              :         }
     200              : 
     201          810 :         ptr += plen;
     202              :     }
     203              : 
     204          354 :     return res;
     205              : }
     206              : 
     207              : bool
     208          354 : RS_execute(Regis *r, char *str)
     209              : {
     210          354 :     RegisNode  *ptr = r->node;
     211          354 :     char       *c = str;
     212          354 :     int         len = 0;
     213              : 
     214         2340 :     while (*c)
     215              :     {
     216         1986 :         len++;
     217         1986 :         c += pg_mblen_cstr(c);
     218              :     }
     219              : 
     220          354 :     if (len < r->nchar)
     221           18 :         return 0;
     222              : 
     223          336 :     c = str;
     224          336 :     if (r->issuffix)
     225              :     {
     226          336 :         len -= r->nchar;
     227         1968 :         while (len-- > 0)
     228         1632 :             c += pg_mblen_cstr(c);
     229              :     }
     230              : 
     231              : 
     232          690 :     while (ptr)
     233              :     {
     234          354 :         switch (ptr->type)
     235              :         {
     236           18 :             case RSF_ONEOF:
     237           18 :                 if (!mb_strchr((char *) ptr->data, c))
     238            0 :                     return false;
     239           18 :                 break;
     240          336 :             case RSF_NONEOF:
     241          336 :                 if (mb_strchr((char *) ptr->data, c))
     242            0 :                     return false;
     243          336 :                 break;
     244            0 :             default:
     245            0 :                 elog(ERROR, "unrecognized regis node type: %d", ptr->type);
     246              :         }
     247          354 :         ptr = ptr->next;
     248          354 :         c += pg_mblen_cstr(c);
     249              :     }
     250              : 
     251          336 :     return true;
     252              : }
        

Generated by: LCOV version 2.0-1