Coverage Report

Created: 2026-10-16 06:48

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/postgres/src/backend/tsearch/regis.c
Line
Count
Source
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
0
#define RS_IN_ONEOF 1
21
0
#define RS_IN_ONEOF_IN  2
22
0
#define RS_IN_NONEOF  3
23
0
#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
RS_isRegis(const char *str)
32
0
{
33
0
  int     state = RS_IN_WAIT;
34
0
  const char *c = str;
35
36
0
  while (*c)
37
0
  {
38
0
    if (state == RS_IN_WAIT)
39
0
    {
40
0
      if (t_isalpha_cstr(c))
41
0
         /* okay */ ;
42
0
      else if (t_iseq(c, '['))
43
0
        state = RS_IN_ONEOF;
44
0
      else
45
0
        return false;
46
0
    }
47
0
    else if (state == RS_IN_ONEOF)
48
0
    {
49
0
      if (t_iseq(c, '^'))
50
0
        state = RS_IN_NONEOF;
51
0
      else if (t_isalpha_cstr(c))
52
0
        state = RS_IN_ONEOF_IN;
53
0
      else
54
0
        return false;
55
0
    }
56
0
    else if (state == RS_IN_ONEOF_IN || state == RS_IN_NONEOF)
57
0
    {
58
0
      if (t_isalpha_cstr(c))
59
0
         /* okay */ ;
60
0
      else if (t_iseq(c, ']'))
61
0
        state = RS_IN_WAIT;
62
0
      else
63
0
        return false;
64
0
    }
65
0
    else
66
0
      elog(ERROR, "internal error in RS_isRegis: state %d", state);
67
0
    c += pg_mblen_cstr(c);
68
0
  }
69
70
0
  return (state == RS_IN_WAIT);
71
0
}
72
73
static RegisNode *
74
newRegisNode(RegisNode *prev, int len)
75
0
{
76
0
  RegisNode  *ptr;
77
78
0
  ptr = (RegisNode *) palloc0(RNHDRSZ + len + 1);
79
0
  if (prev)
80
0
    prev->next = ptr;
81
0
  return ptr;
82
0
}
83
84
void
85
RS_compile(Regis *r, bool issuffix, const char *str)
86
0
{
87
0
  int     len = strlen(str);
88
0
  int     state = RS_IN_WAIT;
89
0
  const char *c = str;
90
0
  RegisNode  *ptr = NULL;
91
92
0
  memset(r, 0, sizeof(Regis));
93
0
  r->issuffix = (issuffix) ? 1 : 0;
94
95
0
  while (*c)
96
0
  {
97
0
    if (state == RS_IN_WAIT)
98
0
    {
99
0
      if (t_isalpha_cstr(c))
100
0
      {
101
0
        if (ptr)
102
0
          ptr = newRegisNode(ptr, len);
103
0
        else
104
0
          ptr = r->node = newRegisNode(NULL, len);
105
0
        ptr->type = RSF_ONEOF;
106
0
        ptr->len = ts_copychar_cstr(ptr->data, c);
107
0
      }
108
0
      else if (t_iseq(c, '['))
109
0
      {
110
0
        if (ptr)
111
0
          ptr = newRegisNode(ptr, len);
112
0
        else
113
0
          ptr = r->node = newRegisNode(NULL, len);
114
0
        ptr->type = RSF_ONEOF;
115
0
        state = RS_IN_ONEOF;
116
0
      }
117
0
      else        /* shouldn't get here */
118
0
        elog(ERROR, "invalid regis pattern: \"%s\"", str);
119
0
    }
120
0
    else if (state == RS_IN_ONEOF)
121
0
    {
122
0
      if (t_iseq(c, '^'))
123
0
      {
124
0
        ptr->type = RSF_NONEOF;
125
0
        state = RS_IN_NONEOF;
126
0
      }
127
0
      else if (t_isalpha_cstr(c))
128
0
      {
129
0
        ptr->len = ts_copychar_cstr(ptr->data, c);
130
0
        state = RS_IN_ONEOF_IN;
131
0
      }
132
0
      else        /* shouldn't get here */
133
0
        elog(ERROR, "invalid regis pattern: \"%s\"", str);
134
0
    }
135
0
    else if (state == RS_IN_ONEOF_IN || state == RS_IN_NONEOF)
136
0
    {
137
0
      if (t_isalpha_cstr(c))
138
0
        ptr->len += ts_copychar_cstr(ptr->data + ptr->len, c);
139
0
      else if (t_iseq(c, ']'))
140
0
        state = RS_IN_WAIT;
141
0
      else        /* shouldn't get here */
142
0
        elog(ERROR, "invalid regis pattern: \"%s\"", str);
143
0
    }
144
0
    else
145
0
      elog(ERROR, "internal error in RS_compile: state %d", state);
146
0
    c += pg_mblen_cstr(c);
147
0
  }
148
149
0
  if (state != RS_IN_WAIT) /* shouldn't get here */
150
0
    elog(ERROR, "invalid regis pattern: \"%s\"", str);
151
152
0
  ptr = r->node;
153
0
  while (ptr)
154
0
  {
155
0
    r->nchar++;
156
0
    ptr = ptr->next;
157
0
  }
158
0
}
159
160
void
161
RS_free(Regis *r)
162
0
{
163
0
  RegisNode  *ptr = r->node,
164
0
         *tmp;
165
166
0
  while (ptr)
167
0
  {
168
0
    tmp = ptr->next;
169
0
    pfree(ptr);
170
0
    ptr = tmp;
171
0
  }
172
173
0
  r->node = NULL;
174
0
}
175
176
static bool
177
mb_strchr(char *str, char *c)
178
0
{
179
0
  int     clen,
180
0
        plen,
181
0
        i;
182
0
  char     *ptr = str;
183
0
  bool    res = false;
184
185
0
  clen = pg_mblen_cstr(c);
186
0
  while (*ptr && !res)
187
0
  {
188
0
    plen = pg_mblen_cstr(ptr);
189
0
    if (plen == clen)
190
0
    {
191
0
      i = plen;
192
0
      res = true;
193
0
      while (i--)
194
0
        if (*(ptr + i) != *(c + i))
195
0
        {
196
0
          res = false;
197
0
          break;
198
0
        }
199
0
    }
200
201
0
    ptr += plen;
202
0
  }
203
204
0
  return res;
205
0
}
206
207
bool
208
RS_execute(Regis *r, char *str)
209
0
{
210
0
  RegisNode  *ptr = r->node;
211
0
  char     *c = str;
212
0
  int     len = 0;
213
214
0
  while (*c)
215
0
  {
216
0
    len++;
217
0
    c += pg_mblen_cstr(c);
218
0
  }
219
220
0
  if (len < r->nchar)
221
0
    return 0;
222
223
0
  c = str;
224
0
  if (r->issuffix)
225
0
  {
226
0
    len -= r->nchar;
227
0
    while (len-- > 0)
228
0
      c += pg_mblen_cstr(c);
229
0
  }
230
231
232
0
  while (ptr)
233
0
  {
234
0
    switch (ptr->type)
235
0
    {
236
0
      case RSF_ONEOF:
237
0
        if (!mb_strchr((char *) ptr->data, c))
238
0
          return false;
239
0
        break;
240
0
      case RSF_NONEOF:
241
0
        if (mb_strchr((char *) ptr->data, c))
242
0
          return false;
243
0
        break;
244
0
      default:
245
0
        elog(ERROR, "unrecognized regis node type: %d", ptr->type);
246
0
    }
247
0
    ptr = ptr->next;
248
0
    c += pg_mblen_cstr(c);
249
0
  }
250
251
0
  return true;
252
0
}