Age Owner Branch data TLA 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-2025, 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 SB_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
2402 peter@eisentraut.org 58 :CBC 462 : wchareq(const char *p1, const char *p2)
59 : : {
60 : : int p1_len;
61 : :
62 : : /* Optimization: quickly compare the first byte. */
7266 bruce@momjian.us 63 [ + + ]: 462 : if (*p1 != *p2)
7178 neilc@samurai.com 64 : 348 : return 0;
65 : :
7409 bruce@momjian.us 66 : 114 : p1_len = pg_mblen(p1);
67 [ - + ]: 114 : if (pg_mblen(p2) != p1_len)
7178 neilc@samurai.com 68 :UBC 0 : return 0;
69 : :
70 : : /* They are the same length */
7409 bruce@momjian.us 71 [ + + ]:CBC 228 : while (p1_len--)
72 : : {
9122 tgl@sss.pgh.pa.us 73 [ - + ]: 114 : if (*p1++ != *p2++)
7178 neilc@samurai.com 74 :UBC 0 : return 0;
75 : : }
7178 neilc@samurai.com 76 :CBC 114 : 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 : :
89 : : /*
90 : : * We do handle case-insensitive matching for single-byte encodings using
91 : : * fold-on-the-fly processing, however.
92 : : */
93 : : static char
358 peter@eisentraut.org 94 :GBC 16076 : SB_lower_char(unsigned char c, pg_locale_t locale)
95 : : {
96 [ + - ]: 16076 : if (locale->ctype_is_c)
5264 tgl@sss.pgh.pa.us 97 : 16076 : return pg_ascii_tolower(c);
278 jdavis@postgresql.or 98 [ # # ]:UBC 0 : else if (locale->is_default)
99 : 0 : return pg_tolower(c);
100 : : else
67 jdavis@postgresql.or 101 :UNC 0 : return char_tolower(c, locale);
102 : : }
103 : :
104 : :
105 : : #define NextByte(p, plen) ((p)++, (plen)--)
106 : :
107 : : /* Set up to compile like_match.c for multibyte characters */
108 : : #define CHAREQ(p1, p2) wchareq((p1), (p2))
109 : : #define NextChar(p, plen) \
110 : : do { int __l = pg_mblen(p); (p) +=__l; (plen) -=__l; } while (0)
111 : : #define CopyAdvChar(dst, src, srclen) \
112 : : do { int __l = pg_mblen(src); \
113 : : (srclen) -= __l; \
114 : : while (__l-- > 0) \
115 : : *(dst)++ = *(src)++; \
116 : : } while (0)
117 : :
118 : : #define MatchText MB_MatchText
119 : : #define do_like_escape MB_do_like_escape
120 : :
121 : : #include "like_match.c"
122 : :
123 : : /* Set up to compile like_match.c for single-byte characters */
124 : : #define CHAREQ(p1, p2) (*(p1) == *(p2))
125 : : #define NextChar(p, plen) NextByte((p), (plen))
126 : : #define CopyAdvChar(dst, src, srclen) (*(dst)++ = *(src)++, (srclen)--)
127 : :
128 : : #define MatchText SB_MatchText
129 : : #define do_like_escape SB_do_like_escape
130 : :
131 : : #include "like_match.c"
132 : :
133 : : /* setup to compile like_match.c for single byte case insensitive matches */
134 : : #define MATCH_LOWER(t, locale) SB_lower_char((unsigned char) (t), locale)
135 : : #define NextChar(p, plen) NextByte((p), (plen))
136 : : #define MatchText SB_IMatchText
137 : :
138 : : #include "like_match.c"
139 : :
140 : : /* setup to compile like_match.c for UTF8 encoding, using fast NextChar */
141 : :
142 : : #define NextChar(p, plen) \
143 : : do { (p)++; (plen)--; } while ((plen) > 0 && (*(p) & 0xC0) == 0x80 )
144 : : #define MatchText UTF8_MatchText
145 : :
146 : : #include "like_match.c"
147 : :
148 : : /* Generic for all cases not requiring inline case-folding */
149 : : static inline int
2360 peter@eisentraut.org 150 :CBC 545528 : GenericMatchText(const char *s, int slen, const char *p, int plen, Oid collation)
151 : : {
152 : : pg_locale_t locale;
153 : :
283 154 [ - + ]: 545528 : if (!OidIsValid(collation))
155 : : {
156 : : /*
157 : : * This typically means that the parser could not resolve a conflict
158 : : * of implicit collations, so report it that way.
159 : : */
283 peter@eisentraut.org 160 [ # # ]:UBC 0 : ereport(ERROR,
161 : : (errcode(ERRCODE_INDETERMINATE_COLLATION),
162 : : errmsg("could not determine which collation to use for LIKE"),
163 : : errhint("Use the COLLATE clause to set the collation explicitly.")));
164 : : }
165 : :
283 peter@eisentraut.org 166 :CBC 545528 : locale = pg_newlocale_from_collation(collation);
167 : :
6671 andrew@dunslane.net 168 [ + + ]: 545528 : if (pg_database_encoding_max_length() == 1)
283 peter@eisentraut.org 169 : 40519 : return SB_MatchText(s, slen, p, plen, locale);
6671 andrew@dunslane.net 170 [ + - ]: 505009 : else if (GetDatabaseEncoding() == PG_UTF8)
283 peter@eisentraut.org 171 : 505009 : return UTF8_MatchText(s, slen, p, plen, locale);
172 : : else
283 peter@eisentraut.org 173 :UBC 0 : return MB_MatchText(s, slen, p, plen, locale);
174 : : }
175 : :
176 : : static inline int
5324 peter_e@gmx.net 177 :CBC 42088 : Generic_Text_IC_like(text *str, text *pat, Oid collation)
178 : : {
179 : : char *s,
180 : : *p;
181 : : int slen,
182 : : plen;
183 : : pg_locale_t locale;
184 : :
1325 peter@eisentraut.org 185 [ - + ]: 42088 : if (!OidIsValid(collation))
186 : : {
187 : : /*
188 : : * This typically means that the parser could not resolve a conflict
189 : : * of implicit collations, so report it that way.
190 : : */
1325 peter@eisentraut.org 191 [ # # ]:UBC 0 : ereport(ERROR,
192 : : (errcode(ERRCODE_INDETERMINATE_COLLATION),
193 : : errmsg("could not determine which collation to use for ILIKE"),
194 : : errhint("Use the COLLATE clause to set the collation explicitly.")));
195 : : }
196 : :
397 jdavis@postgresql.or 197 :CBC 42088 : locale = pg_newlocale_from_collation(collation);
198 : :
359 199 [ + + ]: 42088 : if (!locale->deterministic)
1325 peter@eisentraut.org 200 [ + - ]: 6 : ereport(ERROR,
201 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
202 : : errmsg("nondeterministic collations are not supported for ILIKE")));
203 : :
204 : : /*
205 : : * For efficiency reasons, in the single byte case we don't call lower()
206 : : * on the pattern and text, but instead call SB_lower_char on each
207 : : * character. In the multi-byte case we don't have much choice :-(. Also,
208 : : * ICU does not support single-character case folding, so we go the long
209 : : * way.
210 : : */
211 : :
67 jdavis@postgresql.or 212 [ + + + + ]:GNC 76331 : if (locale->ctype_is_c ||
213 [ - + ]: 68402 : (char_tolower_enabled(locale) &&
214 : 34153 : pg_database_encoding_max_length() == 1))
215 : : {
216 : 7833 : p = VARDATA_ANY(pat);
217 : 7833 : plen = VARSIZE_ANY_EXHDR(pat);
218 : 7833 : s = VARDATA_ANY(str);
219 : 7833 : slen = VARSIZE_ANY_EXHDR(str);
220 : 7833 : return SB_IMatchText(s, slen, p, plen, locale);
221 : : }
222 : : else
223 : : {
3100 noah@leadboat.com 224 :CBC 34249 : pat = DatumGetTextPP(DirectFunctionCall1Coll(lower, collation,
225 : : PointerGetDatum(pat)));
226 [ - + ]: 34249 : p = VARDATA_ANY(pat);
227 [ - + - - : 34249 : plen = VARSIZE_ANY_EXHDR(pat);
- - - - -
+ ]
228 : 34249 : str = DatumGetTextPP(DirectFunctionCall1Coll(lower, collation,
229 : : PointerGetDatum(str)));
230 [ - + ]: 34249 : s = VARDATA_ANY(str);
231 [ - + - - : 34249 : slen = VARSIZE_ANY_EXHDR(str);
- - - - -
+ ]
6559 andrew@dunslane.net 232 [ + - ]: 34249 : if (GetDatabaseEncoding() == PG_UTF8)
358 peter@eisentraut.org 233 : 34249 : return UTF8_MatchText(s, slen, p, plen, 0);
234 : : else
358 peter@eisentraut.org 235 :UBC 0 : return MB_MatchText(s, slen, p, plen, 0);
236 : : }
237 : : }
238 : :
239 : : /*
240 : : * interface routines called by the function manager
241 : : */
242 : :
243 : : Datum
9122 tgl@sss.pgh.pa.us 244 :CBC 89603 : namelike(PG_FUNCTION_ARGS)
245 : : {
9159 lockhart@fourpalms.o 246 : 89603 : Name str = PG_GETARG_NAME(0);
6560 tgl@sss.pgh.pa.us 247 : 89603 : text *pat = PG_GETARG_TEXT_PP(1);
248 : : bool result;
249 : : char *s,
250 : : *p;
251 : : int slen,
252 : : plen;
253 : :
9159 lockhart@fourpalms.o 254 : 89603 : s = NameStr(*str);
255 : 89603 : slen = strlen(s);
6560 tgl@sss.pgh.pa.us 256 [ - + ]: 89603 : p = VARDATA_ANY(pat);
257 [ - + - - : 89603 : plen = VARSIZE_ANY_EXHDR(pat);
- - - - -
+ ]
258 : :
2360 peter@eisentraut.org 259 : 89603 : result = (GenericMatchText(s, slen, p, plen, PG_GET_COLLATION()) == LIKE_TRUE);
260 : :
9159 lockhart@fourpalms.o 261 : 89603 : PG_RETURN_BOOL(result);
262 : : }
263 : :
264 : : Datum
9122 tgl@sss.pgh.pa.us 265 : 2701 : namenlike(PG_FUNCTION_ARGS)
266 : : {
9159 lockhart@fourpalms.o 267 : 2701 : Name str = PG_GETARG_NAME(0);
6560 tgl@sss.pgh.pa.us 268 : 2701 : text *pat = PG_GETARG_TEXT_PP(1);
269 : : bool result;
270 : : char *s,
271 : : *p;
272 : : int slen,
273 : : plen;
274 : :
9159 lockhart@fourpalms.o 275 : 2701 : s = NameStr(*str);
276 : 2701 : slen = strlen(s);
6560 tgl@sss.pgh.pa.us 277 [ - + ]: 2701 : p = VARDATA_ANY(pat);
278 [ - + - - : 2701 : plen = VARSIZE_ANY_EXHDR(pat);
- - - - -
+ ]
279 : :
2360 peter@eisentraut.org 280 : 2701 : result = (GenericMatchText(s, slen, p, plen, PG_GET_COLLATION()) != LIKE_TRUE);
281 : :
9159 lockhart@fourpalms.o 282 : 2701 : PG_RETURN_BOOL(result);
283 : : }
284 : :
285 : : Datum
9162 286 : 292241 : textlike(PG_FUNCTION_ARGS)
287 : : {
6560 tgl@sss.pgh.pa.us 288 : 292241 : text *str = PG_GETARG_TEXT_PP(0);
289 : 292241 : text *pat = PG_GETARG_TEXT_PP(1);
290 : : bool result;
291 : : char *s,
292 : : *p;
293 : : int slen,
294 : : plen;
295 : :
296 [ + + ]: 292241 : s = VARDATA_ANY(str);
297 [ - + - - : 292241 : slen = VARSIZE_ANY_EXHDR(str);
- - - - +
+ ]
298 [ - + ]: 292241 : p = VARDATA_ANY(pat);
299 [ - + - - : 292241 : plen = VARSIZE_ANY_EXHDR(pat);
- - - - -
+ ]
300 : :
2360 peter@eisentraut.org 301 : 292241 : result = (GenericMatchText(s, slen, p, plen, PG_GET_COLLATION()) == LIKE_TRUE);
302 : :
9159 lockhart@fourpalms.o 303 : 292238 : PG_RETURN_BOOL(result);
304 : : }
305 : :
306 : : Datum
9122 tgl@sss.pgh.pa.us 307 : 160983 : textnlike(PG_FUNCTION_ARGS)
308 : : {
6560 309 : 160983 : text *str = PG_GETARG_TEXT_PP(0);
310 : 160983 : text *pat = PG_GETARG_TEXT_PP(1);
311 : : bool result;
312 : : char *s,
313 : : *p;
314 : : int slen,
315 : : plen;
316 : :
317 [ + + ]: 160983 : s = VARDATA_ANY(str);
318 [ - + - - : 160983 : slen = VARSIZE_ANY_EXHDR(str);
- - - - +
+ ]
319 [ - + ]: 160983 : p = VARDATA_ANY(pat);
320 [ - + - - : 160983 : plen = VARSIZE_ANY_EXHDR(pat);
- - - - -
+ ]
321 : :
2360 peter@eisentraut.org 322 : 160983 : result = (GenericMatchText(s, slen, p, plen, PG_GET_COLLATION()) != LIKE_TRUE);
323 : :
9159 lockhart@fourpalms.o 324 : 160983 : PG_RETURN_BOOL(result);
325 : : }
326 : :
327 : : Datum
8758 bruce@momjian.us 328 : 6 : bytealike(PG_FUNCTION_ARGS)
329 : : {
6560 tgl@sss.pgh.pa.us 330 : 6 : bytea *str = PG_GETARG_BYTEA_PP(0);
331 : 6 : bytea *pat = PG_GETARG_BYTEA_PP(1);
332 : : bool result;
333 : : char *s,
334 : : *p;
335 : : int slen,
336 : : plen;
337 : :
338 [ - + ]: 6 : s = VARDATA_ANY(str);
339 [ - + - - : 6 : slen = VARSIZE_ANY_EXHDR(str);
- - - - -
+ ]
340 [ - + ]: 6 : p = VARDATA_ANY(pat);
341 [ - + - - : 6 : plen = VARSIZE_ANY_EXHDR(pat);
- - - - -
+ ]
342 : :
358 peter@eisentraut.org 343 : 6 : result = (SB_MatchText(s, slen, p, plen, 0) == LIKE_TRUE);
344 : :
8758 bruce@momjian.us 345 : 6 : PG_RETURN_BOOL(result);
346 : : }
347 : :
348 : : Datum
349 : 6 : byteanlike(PG_FUNCTION_ARGS)
350 : : {
6560 tgl@sss.pgh.pa.us 351 : 6 : bytea *str = PG_GETARG_BYTEA_PP(0);
352 : 6 : bytea *pat = PG_GETARG_BYTEA_PP(1);
353 : : bool result;
354 : : char *s,
355 : : *p;
356 : : int slen,
357 : : plen;
358 : :
359 [ - + ]: 6 : s = VARDATA_ANY(str);
360 [ - + - - : 6 : slen = VARSIZE_ANY_EXHDR(str);
- - - - -
+ ]
361 [ - + ]: 6 : p = VARDATA_ANY(pat);
362 [ - + - - : 6 : plen = VARSIZE_ANY_EXHDR(pat);
- - - - -
+ ]
363 : :
358 peter@eisentraut.org 364 : 6 : result = (SB_MatchText(s, slen, p, plen, 0) != LIKE_TRUE);
365 : :
8758 bruce@momjian.us 366 : 6 : PG_RETURN_BOOL(result);
367 : : }
368 : :
369 : : /*
370 : : * Case-insensitive versions
371 : : */
372 : :
373 : : Datum
9122 tgl@sss.pgh.pa.us 374 : 7826 : nameiclike(PG_FUNCTION_ARGS)
375 : : {
9159 lockhart@fourpalms.o 376 : 7826 : Name str = PG_GETARG_NAME(0);
6560 tgl@sss.pgh.pa.us 377 : 7826 : text *pat = PG_GETARG_TEXT_PP(1);
378 : : bool result;
379 : : text *strtext;
380 : :
3100 noah@leadboat.com 381 : 7826 : strtext = DatumGetTextPP(DirectFunctionCall1(name_text,
382 : : NameGetDatum(str)));
5324 peter_e@gmx.net 383 : 7826 : result = (Generic_Text_IC_like(strtext, pat, PG_GET_COLLATION()) == LIKE_TRUE);
384 : :
9159 lockhart@fourpalms.o 385 : 7826 : PG_RETURN_BOOL(result);
386 : : }
387 : :
388 : : Datum
9122 tgl@sss.pgh.pa.us 389 : 3 : nameicnlike(PG_FUNCTION_ARGS)
390 : : {
9159 lockhart@fourpalms.o 391 : 3 : Name str = PG_GETARG_NAME(0);
6560 tgl@sss.pgh.pa.us 392 : 3 : text *pat = PG_GETARG_TEXT_PP(1);
393 : : bool result;
394 : : text *strtext;
395 : :
3100 noah@leadboat.com 396 : 3 : strtext = DatumGetTextPP(DirectFunctionCall1(name_text,
397 : : NameGetDatum(str)));
5324 peter_e@gmx.net 398 : 3 : result = (Generic_Text_IC_like(strtext, pat, PG_GET_COLLATION()) != LIKE_TRUE);
399 : :
9159 lockhart@fourpalms.o 400 : 3 : PG_RETURN_BOOL(result);
401 : : }
402 : :
403 : : Datum
9122 tgl@sss.pgh.pa.us 404 : 34231 : texticlike(PG_FUNCTION_ARGS)
405 : : {
6560 406 : 34231 : text *str = PG_GETARG_TEXT_PP(0);
407 : 34231 : text *pat = PG_GETARG_TEXT_PP(1);
408 : : bool result;
409 : :
5324 peter_e@gmx.net 410 : 34231 : result = (Generic_Text_IC_like(str, pat, PG_GET_COLLATION()) == LIKE_TRUE);
411 : :
9159 lockhart@fourpalms.o 412 : 34225 : PG_RETURN_BOOL(result);
413 : : }
414 : :
415 : : Datum
9122 tgl@sss.pgh.pa.us 416 : 28 : texticnlike(PG_FUNCTION_ARGS)
417 : : {
6560 418 : 28 : text *str = PG_GETARG_TEXT_PP(0);
419 : 28 : text *pat = PG_GETARG_TEXT_PP(1);
420 : : bool result;
421 : :
5324 peter_e@gmx.net 422 : 28 : result = (Generic_Text_IC_like(str, pat, PG_GET_COLLATION()) != LIKE_TRUE);
423 : :
9159 lockhart@fourpalms.o 424 : 28 : PG_RETURN_BOOL(result);
425 : : }
426 : :
427 : : /*
428 : : * like_escape() --- given a pattern and an ESCAPE string,
429 : : * convert the pattern to use Postgres' standard backslash escape convention.
430 : : */
431 : : Datum
9122 tgl@sss.pgh.pa.us 432 : 106 : like_escape(PG_FUNCTION_ARGS)
433 : : {
6560 434 : 106 : text *pat = PG_GETARG_TEXT_PP(0);
435 : 106 : text *esc = PG_GETARG_TEXT_PP(1);
436 : : text *result;
437 : :
8738 ishii@postgresql.org 438 [ - + ]: 106 : if (pg_database_encoding_max_length() == 1)
6671 andrew@dunslane.net 439 :UBC 0 : result = SB_do_like_escape(pat, esc);
440 : : else
8717 bruce@momjian.us 441 :CBC 106 : result = MB_do_like_escape(pat, esc);
442 : :
9122 tgl@sss.pgh.pa.us 443 : 106 : PG_RETURN_TEXT_P(result);
444 : : }
445 : :
446 : : /*
447 : : * like_escape_bytea() --- given a pattern and an ESCAPE string,
448 : : * convert the pattern to use Postgres' standard backslash escape convention.
449 : : */
450 : : Datum
8758 bruce@momjian.us 451 : 6 : like_escape_bytea(PG_FUNCTION_ARGS)
452 : : {
6560 tgl@sss.pgh.pa.us 453 : 6 : bytea *pat = PG_GETARG_BYTEA_PP(0);
454 : 6 : bytea *esc = PG_GETARG_BYTEA_PP(1);
6505 bruce@momjian.us 455 : 6 : bytea *result = SB_do_like_escape((text *) pat, (text *) esc);
456 : :
457 : 6 : PG_RETURN_BYTEA_P((bytea *) result);
458 : : }
|