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