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