Age Owner Branch data TLA 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
6832 tgl@sss.pgh.pa.us 31 :CBC 522 : RS_isRegis(const char *str)
32 : : {
6679 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 : : {
118 tmunro@postgresql.or 40 [ + + ]: 691 : if (t_isalpha_cstr(c))
41 : : /* okay */ ;
6679 tgl@sss.pgh.pa.us 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;
118 tmunro@postgresql.or 51 [ # # ]:UBC 0 : else if (t_isalpha_cstr(c))
6679 tgl@sss.pgh.pa.us 52 : 0 : state = RS_IN_ONEOF_IN;
53 : : else
54 : 0 : return false;
55 : : }
6679 tgl@sss.pgh.pa.us 56 [ + - + - ]:CBC 1578 : else if (state == RS_IN_ONEOF_IN || state == RS_IN_NONEOF)
57 : : {
118 tmunro@postgresql.or 58 [ + + ]: 2035 : if (t_isalpha_cstr(c))
59 : : /* okay */ ;
6679 tgl@sss.pgh.pa.us 60 [ + - ]: 457 : else if (t_iseq(c, ']'))
61 : 457 : state = RS_IN_WAIT;
62 : : else
6679 tgl@sss.pgh.pa.us 63 :UBC 0 : return false;
64 : : }
65 : : else
66 [ # # ]: 0 : elog(ERROR, "internal error in RS_isRegis: state %d", state);
118 tmunro@postgresql.or 67 :CBC 2640 : c += pg_mblen_cstr(c);
68 : : }
69 : :
6679 tgl@sss.pgh.pa.us 70 : 436 : return (state == RS_IN_WAIT);
71 : : }
72 : :
73 : : static RegisNode *
6746 bruce@momjian.us 74 : 498 : newRegisNode(RegisNode *prev, int len)
75 : : {
76 : : RegisNode *ptr;
77 : :
6832 tgl@sss.pgh.pa.us 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
6679 85 : 436 : RS_compile(Regis *r, bool issuffix, const char *str)
86 : : {
6832 87 : 436 : int len = strlen(str);
88 : 436 : int state = RS_IN_WAIT;
6679 89 : 436 : const char *c = str;
6832 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 : : {
118 tmunro@postgresql.or 99 [ + + ]: 498 : if (t_isalpha_cstr(c))
100 : : {
6832 tgl@sss.pgh.pa.us 101 [ + - ]: 62 : if (ptr)
102 : 62 : ptr = newRegisNode(ptr, len);
103 : : else
6832 tgl@sss.pgh.pa.us 104 :UBC 0 : ptr = r->node = newRegisNode(NULL, len);
6832 tgl@sss.pgh.pa.us 105 :CBC 62 : ptr->type = RSF_ONEOF;
118 tmunro@postgresql.or 106 : 62 : ptr->len = ts_copychar_cstr(ptr->data, c);
107 : : }
6832 tgl@sss.pgh.pa.us 108 [ + - ]: 436 : else if (t_iseq(c, '['))
109 : : {
110 [ - + ]: 436 : if (ptr)
6832 tgl@sss.pgh.pa.us 111 :UBC 0 : ptr = newRegisNode(ptr, len);
112 : : else
6832 tgl@sss.pgh.pa.us 113 :CBC 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 */
6679 tgl@sss.pgh.pa.us 118 [ # # ]:UBC 0 : elog(ERROR, "invalid regis pattern: \"%s\"", str);
119 : : }
6832 tgl@sss.pgh.pa.us 120 [ + + ]:CBC 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 : : }
118 tmunro@postgresql.or 127 [ # # ]:UBC 0 : else if (t_isalpha_cstr(c))
128 : : {
129 : 0 : ptr->len = ts_copychar_cstr(ptr->data, c);
6832 tgl@sss.pgh.pa.us 130 : 0 : state = RS_IN_ONEOF_IN;
131 : : }
132 : : else /* shouldn't get here */
6679 133 [ # # ]: 0 : elog(ERROR, "invalid regis pattern: \"%s\"", str);
134 : : }
6832 tgl@sss.pgh.pa.us 135 [ + - + - ]:CBC 1452 : else if (state == RS_IN_ONEOF_IN || state == RS_IN_NONEOF)
136 : : {
118 tmunro@postgresql.or 137 [ + + ]: 2904 : if (t_isalpha_cstr(c))
138 : 1016 : ptr->len += ts_copychar_cstr(ptr->data + ptr->len, c);
6832 tgl@sss.pgh.pa.us 139 [ + - ]: 436 : else if (t_iseq(c, ']'))
140 : 436 : state = RS_IN_WAIT;
141 : : else /* shouldn't get here */
6679 tgl@sss.pgh.pa.us 142 [ # # ]:UBC 0 : elog(ERROR, "invalid regis pattern: \"%s\"", str);
143 : : }
144 : : else
6832 145 [ # # ]: 0 : elog(ERROR, "internal error in RS_compile: state %d", state);
118 tmunro@postgresql.or 146 :CBC 2386 : c += pg_mblen_cstr(c);
147 : : }
148 : :
6172 bruce@momjian.us 149 [ - + ]: 436 : if (state != RS_IN_WAIT) /* shouldn't get here */
6679 tgl@sss.pgh.pa.us 150 [ # # ]:UBC 0 : elog(ERROR, "invalid regis pattern: \"%s\"", str);
151 : :
6832 tgl@sss.pgh.pa.us 152 :CBC 436 : ptr = r->node;
153 [ + + ]: 934 : while (ptr)
154 : : {
155 : 498 : r->nchar++;
156 : 498 : ptr = ptr->next;
157 : : }
158 : 436 : }
159 : :
160 : : void
6746 bruce@momjian.us 161 :UBC 0 : RS_free(Regis *r)
162 : : {
6832 tgl@sss.pgh.pa.us 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
6832 tgl@sss.pgh.pa.us 177 :CBC 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 : :
118 tmunro@postgresql.or 185 : 590 : clen = pg_mblen_cstr(c);
6832 tgl@sss.pgh.pa.us 186 [ + + + - ]: 1940 : while (*ptr && !res)
187 : : {
118 tmunro@postgresql.or 188 : 1350 : plen = pg_mblen_cstr(ptr);
6832 tgl@sss.pgh.pa.us 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
6746 bruce@momjian.us 208 : 590 : RS_execute(Regis *r, char *str)
209 : : {
6832 tgl@sss.pgh.pa.us 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++;
118 tmunro@postgresql.or 217 : 3310 : c += pg_mblen_cstr(c);
218 : : }
219 : :
6832 tgl@sss.pgh.pa.us 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)
118 tmunro@postgresql.or 228 : 2720 : c += pg_mblen_cstr(c);
229 : : }
230 : :
231 : :
6832 tgl@sss.pgh.pa.us 232 [ + + ]: 1150 : while (ptr)
233 : : {
234 [ + + - ]: 590 : switch (ptr->type)
235 : : {
236 : 30 : case RSF_ONEOF:
5651 rhaas@postgresql.org 237 [ - + ]: 30 : if (!mb_strchr((char *) ptr->data, c))
6832 tgl@sss.pgh.pa.us 238 :UBC 0 : return false;
6832 tgl@sss.pgh.pa.us 239 :CBC 30 : break;
240 : 560 : case RSF_NONEOF:
5651 rhaas@postgresql.org 241 [ - + ]: 560 : if (mb_strchr((char *) ptr->data, c))
6832 tgl@sss.pgh.pa.us 242 :UBC 0 : return false;
6832 tgl@sss.pgh.pa.us 243 :CBC 560 : break;
6832 tgl@sss.pgh.pa.us 244 :UBC 0 : default:
245 [ # # ]: 0 : elog(ERROR, "unrecognized regis node type: %d", ptr->type);
246 : : }
6832 tgl@sss.pgh.pa.us 247 :CBC 590 : ptr = ptr->next;
118 tmunro@postgresql.or 248 : 590 : c += pg_mblen_cstr(c);
249 : : }
250 : :
6832 tgl@sss.pgh.pa.us 251 : 560 : return true;
252 : : }
|