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 : }
|