Branch data Line data Source code
1 : : /*-------------------------------------------------------------------------
2 : : *
3 : : * like.c
4 : : * like expression handling code.
5 : : *
6 : : * NOTES
7 : : * A big hack of the regexp.c code!! Contributed by
8 : : * Keith Parks <emkxp01@mtcc.demon.co.uk> (7/95).
9 : : *
10 : : * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
11 : : * Portions Copyright (c) 1994, Regents of the University of California
12 : : *
13 : : * IDENTIFICATION
14 : : * src/backend/utils/adt/like.c
15 : : *
16 : : *-------------------------------------------------------------------------
17 : : */
18 : : #include "postgres.h"
19 : :
20 : : #include <ctype.h>
21 : :
22 : : #include "catalog/pg_collation.h"
23 : : #include "mb/pg_wchar.h"
24 : : #include "miscadmin.h"
25 : : #include "utils/fmgrprotos.h"
26 : : #include "utils/pg_locale.h"
27 : : #include "varatt.h"
28 : :
29 : :
30 : : #define LIKE_TRUE 1
31 : : #define LIKE_FALSE 0
32 : : #define LIKE_ABORT (-1)
33 : :
34 : :
35 : : static int SB_MatchText(const char *t, int tlen, const char *p, int plen,
36 : : pg_locale_t locale);
37 : : static text *SB_do_like_escape(text *pat, text *esc);
38 : :
39 : : static int MB_MatchText(const char *t, int tlen, const char *p, int plen,
40 : : pg_locale_t locale);
41 : : static text *MB_do_like_escape(text *pat, text *esc);
42 : :
43 : : static int UTF8_MatchText(const char *t, int tlen, const char *p, int plen,
44 : : pg_locale_t locale);
45 : :
46 : : static int C_IMatchText(const char *t, int tlen, const char *p, int plen,
47 : : pg_locale_t locale);
48 : :
49 : : static int GenericMatchText(const char *s, int slen, const char *p, int plen, Oid collation);
50 : : static int Generic_Text_IC_like(text *str, text *pat, Oid collation);
51 : :
52 : : /*--------------------
53 : : * Support routine for MatchText. Compares given multibyte streams
54 : : * as wide characters. If they match, returns 1 otherwise returns 0.
55 : : *--------------------
56 : : */
57 : : static inline int
58 : 910 : wchareq(const char *p1, int p1len, const char *p2, int p2len)
59 : : {
60 : : int p1clen;
61 : :
62 : : /* Optimization: quickly compare the first byte. */
63 [ + + ]: 910 : if (*p1 != *p2)
64 : 705 : return 0;
65 : :
66 : 205 : p1clen = pg_mblen_with_len(p1, p1len);
67 [ - + ]: 205 : if (pg_mblen_with_len(p2, p2len) != p1clen)
68 : 0 : return 0;
69 : :
70 : : /* They are the same length */
71 [ + + ]: 410 : while (p1clen--)
72 : : {
73 [ - + ]: 205 : if (*p1++ != *p2++)
74 : 0 : return 0;
75 : : }
76 : 205 : return 1;
77 : : }
78 : :
79 : : /*
80 : : * Formerly we had a routine iwchareq() here that tried to do case-insensitive
81 : : * comparison of multibyte characters. It did not work at all, however,
82 : : * because it relied on tolower() which has a single-byte API ... and
83 : : * towlower() wouldn't be much better since we have no suitably cheap way
84 : : * of getting a single character transformed to the system's wchar_t format.
85 : : * So now, we just downcase the strings using lower() and apply regular LIKE
86 : : * comparison. This should be revisited when we install better locale support.
87 : : *
88 : : * We do handle case-insensitive matching for the C locale using
89 : : * fold-on-the-fly processing, however.
90 : : */
91 : :
92 : :
93 : : #define NextByte(p, plen) ((p)++, (plen)--)
94 : :
95 : : /* Set up to compile like_match.c for multibyte characters */
96 : : #define CHAREQ(p1, p1len, p2, p2len) wchareq((p1), (p1len), (p2), (p2len))
97 : : #define NextChar(p, plen) \
98 : : do { int __l = pg_mblen_with_len((p), (plen)); (p) +=__l; (plen) -=__l; } while (0)
99 : : #define CopyAdvChar(dst, src, srclen) \
100 : : do { int __l = pg_mblen_with_len((src), (srclen)); \
101 : : (srclen) -= __l; \
102 : : while (__l-- > 0) \
103 : : *(dst)++ = *(src)++; \
104 : : } while (0)
105 : :
106 : : #define MatchText MB_MatchText
107 : : #define do_like_escape MB_do_like_escape
108 : :
109 : : #include "like_match.c"
110 : :
111 : : /* Set up to compile like_match.c for single-byte characters */
112 : : #define CHAREQ(p1, p1len, p2, p2len) (*(p1) == *(p2))
113 : : #define NextChar(p, plen) NextByte((p), (plen))
114 : : #define CopyAdvChar(dst, src, srclen) (*(dst)++ = *(src)++, (srclen)--)
115 : :
116 : : #define MatchText SB_MatchText
117 : : #define do_like_escape SB_do_like_escape
118 : :
119 : : #include "like_match.c"
120 : :
121 : : /* setup to compile like_match.c for case-insensitive matches in C locale */
122 : : #define MATCH_LOWER
123 : : #define NextChar(p, plen) NextByte((p), (plen))
124 : : #define MatchText C_IMatchText
125 : :
126 : : #include "like_match.c"
127 : :
128 : : /* setup to compile like_match.c for UTF8 encoding, using fast NextChar */
129 : :
130 : : #define NextChar(p, plen) \
131 : : do { (p)++; (plen)--; } while ((plen) > 0 && (*(p) & 0xC0) == 0x80 )
132 : : #define MatchText UTF8_MatchText
133 : :
134 : : #include "like_match.c"
135 : :
136 : : /* Generic for all cases not requiring inline case-folding */
137 : : static inline int
138 : 711001 : GenericMatchText(const char *s, int slen, const char *p, int plen, Oid collation)
139 : : {
140 : : pg_locale_t locale;
141 : :
142 [ - + ]: 711001 : if (!OidIsValid(collation))
143 : : {
144 : : /*
145 : : * This typically means that the parser could not resolve a conflict
146 : : * of implicit collations, so report it that way.
147 : : */
148 [ # # ]: 0 : ereport(ERROR,
149 : : (errcode(ERRCODE_INDETERMINATE_COLLATION),
150 : : errmsg("could not determine which collation to use for LIKE"),
151 : : errhint("Use the COLLATE clause to set the collation explicitly.")));
152 : : }
153 : :
154 : 711001 : locale = pg_newlocale_from_collation(collation);
155 : :
156 [ + + ]: 711001 : if (pg_database_encoding_max_length() == 1)
157 : 40464 : return SB_MatchText(s, slen, p, plen, locale);
158 [ + - ]: 670537 : else if (GetDatabaseEncoding() == PG_UTF8)
159 : 670537 : return UTF8_MatchText(s, slen, p, plen, locale);
160 : : else
161 : 0 : return MB_MatchText(s, slen, p, plen, locale);
162 : : }
163 : :
164 : : static inline int
165 : 46372 : Generic_Text_IC_like(text *str, text *pat, Oid collation)
166 : : {
167 : : char *s,
168 : : *p;
169 : : int slen,
170 : : plen;
171 : : pg_locale_t locale;
172 : :
173 [ - + ]: 46372 : if (!OidIsValid(collation))
174 : : {
175 : : /*
176 : : * This typically means that the parser could not resolve a conflict
177 : : * of implicit collations, so report it that way.
178 : : */
179 [ # # ]: 0 : ereport(ERROR,
180 : : (errcode(ERRCODE_INDETERMINATE_COLLATION),
181 : : errmsg("could not determine which collation to use for ILIKE"),
182 : : errhint("Use the COLLATE clause to set the collation explicitly.")));
183 : : }
184 : :
185 : 46372 : locale = pg_newlocale_from_collation(collation);
186 : :
187 [ + + ]: 46372 : if (!locale->deterministic)
188 [ + - ]: 8 : ereport(ERROR,
189 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
190 : : errmsg("nondeterministic collations are not supported for ILIKE")));
191 : :
192 : : /*
193 : : * For efficiency reasons, in the C locale we don't call lower() on the
194 : : * pattern and text, but instead lowercase each character lazily. This
195 : : * only works for single-byte encodings, otherwise "_" may incorrectly
196 : : * match an incomplete byte sequence.
197 : : *
198 : : * XXX: use casefolding instead?
199 : : */
200 : :
201 [ + + - + ]: 46364 : if (locale->ctype_is_c && pg_database_encoding_max_length() == 1)
202 : : {
203 : 0 : p = VARDATA_ANY(pat);
204 : 0 : plen = VARSIZE_ANY_EXHDR(pat);
205 : 0 : s = VARDATA_ANY(str);
206 : 0 : slen = VARSIZE_ANY_EXHDR(str);
207 : 0 : return C_IMatchText(s, slen, p, plen, locale);
208 : : }
209 : : else
210 : : {
211 : 46364 : pat = DatumGetTextPP(DirectFunctionCall1Coll(lower, collation,
212 : : PointerGetDatum(pat)));
213 : 46364 : p = VARDATA_ANY(pat);
214 : 46364 : plen = VARSIZE_ANY_EXHDR(pat);
215 : 46364 : str = DatumGetTextPP(DirectFunctionCall1Coll(lower, collation,
216 : : PointerGetDatum(str)));
217 : 46364 : s = VARDATA_ANY(str);
218 : 46364 : slen = VARSIZE_ANY_EXHDR(str);
219 : :
220 [ + - ]: 46364 : if (GetDatabaseEncoding() == PG_UTF8)
221 : 46364 : return UTF8_MatchText(s, slen, p, plen, 0);
222 [ # # ]: 0 : else if (pg_database_encoding_max_length() > 1)
223 : 0 : return MB_MatchText(s, slen, p, plen, 0);
224 : : else
225 : 0 : return SB_MatchText(s, slen, p, plen, 0);
226 : : }
227 : : }
228 : :
229 : : /*
230 : : * interface routines called by the function manager
231 : : */
232 : :
233 : : Datum
234 : 128141 : namelike(PG_FUNCTION_ARGS)
235 : : {
236 : 128141 : Name str = PG_GETARG_NAME(0);
237 : 128141 : text *pat = PG_GETARG_TEXT_PP(1);
238 : : bool result;
239 : : char *s,
240 : : *p;
241 : : int slen,
242 : : plen;
243 : :
244 : 128141 : s = NameStr(*str);
245 : 128141 : slen = strlen(s);
246 : 128141 : p = VARDATA_ANY(pat);
247 : 128141 : plen = VARSIZE_ANY_EXHDR(pat);
248 : :
249 : 128141 : result = (GenericMatchText(s, slen, p, plen, PG_GET_COLLATION()) == LIKE_TRUE);
250 : :
251 : 128141 : PG_RETURN_BOOL(result);
252 : : }
253 : :
254 : : Datum
255 : 3780 : namenlike(PG_FUNCTION_ARGS)
256 : : {
257 : 3780 : Name str = PG_GETARG_NAME(0);
258 : 3780 : text *pat = PG_GETARG_TEXT_PP(1);
259 : : bool result;
260 : : char *s,
261 : : *p;
262 : : int slen,
263 : : plen;
264 : :
265 : 3780 : s = NameStr(*str);
266 : 3780 : slen = strlen(s);
267 : 3780 : p = VARDATA_ANY(pat);
268 : 3780 : plen = VARSIZE_ANY_EXHDR(pat);
269 : :
270 : 3780 : result = (GenericMatchText(s, slen, p, plen, PG_GET_COLLATION()) != LIKE_TRUE);
271 : :
272 : 3780 : PG_RETURN_BOOL(result);
273 : : }
274 : :
275 : : Datum
276 : 366240 : textlike(PG_FUNCTION_ARGS)
277 : : {
278 : 366240 : text *str = PG_GETARG_TEXT_PP(0);
279 : 366240 : text *pat = PG_GETARG_TEXT_PP(1);
280 : : bool result;
281 : : char *s,
282 : : *p;
283 : : int slen,
284 : : plen;
285 : :
286 : 366240 : s = VARDATA_ANY(str);
287 : 366240 : slen = VARSIZE_ANY_EXHDR(str);
288 : 366240 : p = VARDATA_ANY(pat);
289 : 366240 : plen = VARSIZE_ANY_EXHDR(pat);
290 : :
291 : 366240 : result = (GenericMatchText(s, slen, p, plen, PG_GET_COLLATION()) == LIKE_TRUE);
292 : :
293 : 366236 : PG_RETURN_BOOL(result);
294 : : }
295 : :
296 : : Datum
297 : 212840 : textnlike(PG_FUNCTION_ARGS)
298 : : {
299 : 212840 : text *str = PG_GETARG_TEXT_PP(0);
300 : 212840 : text *pat = PG_GETARG_TEXT_PP(1);
301 : : bool result;
302 : : char *s,
303 : : *p;
304 : : int slen,
305 : : plen;
306 : :
307 : 212840 : s = VARDATA_ANY(str);
308 : 212840 : slen = VARSIZE_ANY_EXHDR(str);
309 : 212840 : p = VARDATA_ANY(pat);
310 : 212840 : plen = VARSIZE_ANY_EXHDR(pat);
311 : :
312 : 212840 : result = (GenericMatchText(s, slen, p, plen, PG_GET_COLLATION()) != LIKE_TRUE);
313 : :
314 : 212840 : PG_RETURN_BOOL(result);
315 : : }
316 : :
317 : : Datum
318 : 10 : bytealike(PG_FUNCTION_ARGS)
319 : : {
320 : 10 : bytea *str = PG_GETARG_BYTEA_PP(0);
321 : 10 : bytea *pat = PG_GETARG_BYTEA_PP(1);
322 : : bool result;
323 : : char *s,
324 : : *p;
325 : : int slen,
326 : : plen;
327 : :
328 : 10 : s = VARDATA_ANY(str);
329 : 10 : slen = VARSIZE_ANY_EXHDR(str);
330 : 10 : p = VARDATA_ANY(pat);
331 : 10 : plen = VARSIZE_ANY_EXHDR(pat);
332 : :
333 : 10 : result = (SB_MatchText(s, slen, p, plen, 0) == LIKE_TRUE);
334 : :
335 : 10 : PG_RETURN_BOOL(result);
336 : : }
337 : :
338 : : Datum
339 : 10 : byteanlike(PG_FUNCTION_ARGS)
340 : : {
341 : 10 : bytea *str = PG_GETARG_BYTEA_PP(0);
342 : 10 : bytea *pat = PG_GETARG_BYTEA_PP(1);
343 : : bool result;
344 : : char *s,
345 : : *p;
346 : : int slen,
347 : : plen;
348 : :
349 : 10 : s = VARDATA_ANY(str);
350 : 10 : slen = VARSIZE_ANY_EXHDR(str);
351 : 10 : p = VARDATA_ANY(pat);
352 : 10 : plen = VARSIZE_ANY_EXHDR(pat);
353 : :
354 : 10 : result = (SB_MatchText(s, slen, p, plen, 0) != LIKE_TRUE);
355 : :
356 : 10 : PG_RETURN_BOOL(result);
357 : : }
358 : :
359 : : /*
360 : : * Case-insensitive versions
361 : : */
362 : :
363 : : Datum
364 : 12049 : nameiclike(PG_FUNCTION_ARGS)
365 : : {
366 : 12049 : Name str = PG_GETARG_NAME(0);
367 : 12049 : text *pat = PG_GETARG_TEXT_PP(1);
368 : : bool result;
369 : : text *strtext;
370 : :
371 : 12049 : strtext = DatumGetTextPP(DirectFunctionCall1(name_text,
372 : : NameGetDatum(str)));
373 : 12049 : result = (Generic_Text_IC_like(strtext, pat, PG_GET_COLLATION()) == LIKE_TRUE);
374 : :
375 : 12049 : PG_RETURN_BOOL(result);
376 : : }
377 : :
378 : : Datum
379 : 5 : nameicnlike(PG_FUNCTION_ARGS)
380 : : {
381 : 5 : Name str = PG_GETARG_NAME(0);
382 : 5 : text *pat = PG_GETARG_TEXT_PP(1);
383 : : bool result;
384 : : text *strtext;
385 : :
386 : 5 : strtext = DatumGetTextPP(DirectFunctionCall1(name_text,
387 : : NameGetDatum(str)));
388 : 5 : result = (Generic_Text_IC_like(strtext, pat, PG_GET_COLLATION()) != LIKE_TRUE);
389 : :
390 : 5 : PG_RETURN_BOOL(result);
391 : : }
392 : :
393 : : Datum
394 : 34282 : texticlike(PG_FUNCTION_ARGS)
395 : : {
396 : 34282 : text *str = PG_GETARG_TEXT_PP(0);
397 : 34282 : text *pat = PG_GETARG_TEXT_PP(1);
398 : : bool result;
399 : :
400 : 34282 : result = (Generic_Text_IC_like(str, pat, PG_GET_COLLATION()) == LIKE_TRUE);
401 : :
402 : 34274 : PG_RETURN_BOOL(result);
403 : : }
404 : :
405 : : Datum
406 : 36 : texticnlike(PG_FUNCTION_ARGS)
407 : : {
408 : 36 : text *str = PG_GETARG_TEXT_PP(0);
409 : 36 : text *pat = PG_GETARG_TEXT_PP(1);
410 : : bool result;
411 : :
412 : 36 : result = (Generic_Text_IC_like(str, pat, PG_GET_COLLATION()) != LIKE_TRUE);
413 : :
414 : 36 : PG_RETURN_BOOL(result);
415 : : }
416 : :
417 : : /*
418 : : * like_escape() --- given a pattern and an ESCAPE string,
419 : : * convert the pattern to use Postgres' standard backslash escape convention.
420 : : */
421 : : Datum
422 : 186 : like_escape(PG_FUNCTION_ARGS)
423 : : {
424 : 186 : text *pat = PG_GETARG_TEXT_PP(0);
425 : 186 : text *esc = PG_GETARG_TEXT_PP(1);
426 : : text *result;
427 : :
428 [ - + ]: 186 : if (pg_database_encoding_max_length() == 1)
429 : 0 : result = SB_do_like_escape(pat, esc);
430 : : else
431 : 186 : result = MB_do_like_escape(pat, esc);
432 : :
433 : 186 : PG_RETURN_TEXT_P(result);
434 : : }
435 : :
436 : : /*
437 : : * like_escape_bytea() --- given a pattern and an ESCAPE string,
438 : : * convert the pattern to use Postgres' standard backslash escape convention.
439 : : */
440 : : Datum
441 : 10 : like_escape_bytea(PG_FUNCTION_ARGS)
442 : : {
443 : 10 : bytea *pat = PG_GETARG_BYTEA_PP(0);
444 : 10 : bytea *esc = PG_GETARG_BYTEA_PP(1);
445 : 10 : bytea *result = SB_do_like_escape((text *) pat, (text *) esc);
446 : :
447 : 10 : PG_RETURN_BYTEA_P((bytea *) result);
448 : : }
|