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-2024, 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, bool locale_is_c);
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, bool locale_is_c);
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, bool locale_is_c);
45 : :
46 : : static int SB_IMatchText(const char *t, int tlen, const char *p, int plen,
47 : : pg_locale_t locale, bool locale_is_c);
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
1892 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. */
6756 bruce@momjian.us 63 [ + + ]: 462 : if (*p1 != *p2)
6668 neilc@samurai.com 64 : 348 : return 0;
65 : :
6899 bruce@momjian.us 66 : 114 : p1_len = pg_mblen(p1);
67 [ - + ]: 114 : if (pg_mblen(p2) != p1_len)
6668 neilc@samurai.com 68 :UBC 0 : return 0;
69 : :
70 : : /* They are the same length */
6899 bruce@momjian.us 71 [ + + ]:CBC 228 : while (p1_len--)
72 : : {
8612 tgl@sss.pgh.pa.us 73 [ - + ]: 114 : if (*p1++ != *p2++)
6668 neilc@samurai.com 74 :UBC 0 : return 0;
75 : : }
6668 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
4754 tgl@sss.pgh.pa.us 94 :UBC 0 : SB_lower_char(unsigned char c, pg_locale_t locale, bool locale_is_c)
95 : : {
96 [ # # ]: 0 : if (locale_is_c)
97 : 0 : return pg_ascii_tolower(c);
98 [ # # ]: 0 : else if (locale)
2579 peter_e@gmx.net 99 : 0 : return tolower_l(c, locale->info.lt);
100 : : else
4754 tgl@sss.pgh.pa.us 101 : 0 : return pg_tolower(c);
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) SB_lower_char((unsigned char) (t), locale, locale_is_c)
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
1850 peter@eisentraut.org 150 :CBC 480725 : GenericMatchText(const char *s, int slen, const char *p, int plen, Oid collation)
151 : : {
815 152 [ + - + + ]: 480725 : if (collation && !lc_ctype_is_c(collation))
153 : : {
1789 tgl@sss.pgh.pa.us 154 : 102836 : pg_locale_t locale = pg_newlocale_from_collation(collation);
155 : :
416 jdavis@postgresql.or 156 [ + + ]: 102836 : if (!pg_locale_deterministic(locale))
1850 peter@eisentraut.org 157 [ + - ]: 12 : ereport(ERROR,
158 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
159 : : errmsg("nondeterministic collations are not supported for LIKE")));
160 : : }
161 : :
6161 andrew@dunslane.net 162 [ + + ]: 480713 : if (pg_database_encoding_max_length() == 1)
4754 tgl@sss.pgh.pa.us 163 : 39518 : return SB_MatchText(s, slen, p, plen, 0, true);
6161 andrew@dunslane.net 164 [ + - ]: 441195 : else if (GetDatabaseEncoding() == PG_UTF8)
4754 tgl@sss.pgh.pa.us 165 : 441195 : return UTF8_MatchText(s, slen, p, plen, 0, true);
166 : : else
4754 tgl@sss.pgh.pa.us 167 :UBC 0 : return MB_MatchText(s, slen, p, plen, 0, true);
168 : : }
169 : :
170 : : static inline int
4814 peter_e@gmx.net 171 :CBC 41969 : Generic_Text_IC_like(text *str, text *pat, Oid collation)
172 : : {
173 : : char *s,
174 : : *p;
175 : : int slen,
176 : : plen;
2579 177 : 41969 : pg_locale_t locale = 0;
178 : 41969 : bool locale_is_c = false;
179 : :
815 peter@eisentraut.org 180 [ - + ]: 41969 : if (!OidIsValid(collation))
181 : : {
182 : : /*
183 : : * This typically means that the parser could not resolve a conflict
184 : : * of implicit collations, so report it that way.
185 : : */
815 peter@eisentraut.org 186 [ # # ]:UBC 0 : ereport(ERROR,
187 : : (errcode(ERRCODE_INDETERMINATE_COLLATION),
188 : : errmsg("could not determine which collation to use for ILIKE"),
189 : : errhint("Use the COLLATE clause to set the collation explicitly.")));
190 : : }
191 : :
2579 peter_e@gmx.net 192 [ + + ]:CBC 41969 : if (lc_ctype_is_c(collation))
193 : 7716 : locale_is_c = true;
194 : : else
195 : 34253 : locale = pg_newlocale_from_collation(collation);
196 : :
416 jdavis@postgresql.or 197 [ + + ]: 41969 : if (!pg_locale_deterministic(locale))
815 peter@eisentraut.org 198 [ + - ]: 6 : ereport(ERROR,
199 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
200 : : errmsg("nondeterministic collations are not supported for ILIKE")));
201 : :
202 : : /*
203 : : * For efficiency reasons, in the single byte case we don't call lower()
204 : : * on the pattern and text, but instead call SB_lower_char on each
205 : : * character. In the multi-byte case we don't have much choice :-(. Also,
206 : : * ICU does not support single-character case folding, so we go the long
207 : : * way.
208 : : */
209 : :
2579 peter_e@gmx.net 210 [ - + - - : 41963 : if (pg_database_encoding_max_length() > 1 || (locale && locale->provider == COLLPROVIDER_ICU))
- - ]
211 : : {
2590 noah@leadboat.com 212 : 41963 : pat = DatumGetTextPP(DirectFunctionCall1Coll(lower, collation,
213 : : PointerGetDatum(pat)));
214 [ - + ]: 41963 : p = VARDATA_ANY(pat);
215 [ - + - - : 41963 : plen = VARSIZE_ANY_EXHDR(pat);
- - - - -
+ ]
216 : 41963 : str = DatumGetTextPP(DirectFunctionCall1Coll(lower, collation,
217 : : PointerGetDatum(str)));
218 [ - + ]: 41963 : s = VARDATA_ANY(str);
219 [ - + - - : 41963 : slen = VARSIZE_ANY_EXHDR(str);
- - - - -
+ ]
6049 andrew@dunslane.net 220 [ + - ]: 41963 : if (GetDatabaseEncoding() == PG_UTF8)
4754 tgl@sss.pgh.pa.us 221 : 41963 : return UTF8_MatchText(s, slen, p, plen, 0, true);
222 : : else
4754 tgl@sss.pgh.pa.us 223 :UBC 0 : return MB_MatchText(s, slen, p, plen, 0, true);
224 : : }
225 : : else
226 : : {
6049 andrew@dunslane.net 227 [ # # ]: 0 : p = VARDATA_ANY(pat);
228 [ # # # # : 0 : plen = VARSIZE_ANY_EXHDR(pat);
# # # # #
# ]
229 [ # # ]: 0 : s = VARDATA_ANY(str);
230 [ # # # # : 0 : slen = VARSIZE_ANY_EXHDR(str);
# # # # #
# ]
4754 tgl@sss.pgh.pa.us 231 : 0 : return SB_IMatchText(s, slen, p, plen, locale, locale_is_c);
232 : : }
233 : : }
234 : :
235 : : /*
236 : : * interface routines called by the function manager
237 : : */
238 : :
239 : : Datum
8612 tgl@sss.pgh.pa.us 240 :CBC 82946 : namelike(PG_FUNCTION_ARGS)
241 : : {
8649 lockhart@fourpalms.o 242 : 82946 : Name str = PG_GETARG_NAME(0);
6050 tgl@sss.pgh.pa.us 243 : 82946 : text *pat = PG_GETARG_TEXT_PP(1);
244 : : bool result;
245 : : char *s,
246 : : *p;
247 : : int slen,
248 : : plen;
249 : :
8649 lockhart@fourpalms.o 250 : 82946 : s = NameStr(*str);
251 : 82946 : slen = strlen(s);
6050 tgl@sss.pgh.pa.us 252 [ - + ]: 82946 : p = VARDATA_ANY(pat);
253 [ - + - - : 82946 : plen = VARSIZE_ANY_EXHDR(pat);
- - - - -
+ ]
254 : :
1850 peter@eisentraut.org 255 : 82946 : result = (GenericMatchText(s, slen, p, plen, PG_GET_COLLATION()) == LIKE_TRUE);
256 : :
8649 lockhart@fourpalms.o 257 : 82946 : PG_RETURN_BOOL(result);
258 : : }
259 : :
260 : : Datum
8612 tgl@sss.pgh.pa.us 261 : 2667 : namenlike(PG_FUNCTION_ARGS)
262 : : {
8649 lockhart@fourpalms.o 263 : 2667 : Name str = PG_GETARG_NAME(0);
6050 tgl@sss.pgh.pa.us 264 : 2667 : text *pat = PG_GETARG_TEXT_PP(1);
265 : : bool result;
266 : : char *s,
267 : : *p;
268 : : int slen,
269 : : plen;
270 : :
8649 lockhart@fourpalms.o 271 : 2667 : s = NameStr(*str);
272 : 2667 : slen = strlen(s);
6050 tgl@sss.pgh.pa.us 273 [ - + ]: 2667 : p = VARDATA_ANY(pat);
274 [ - + - - : 2667 : plen = VARSIZE_ANY_EXHDR(pat);
- - - - -
+ ]
275 : :
1850 peter@eisentraut.org 276 : 2667 : result = (GenericMatchText(s, slen, p, plen, PG_GET_COLLATION()) != LIKE_TRUE);
277 : :
8649 lockhart@fourpalms.o 278 : 2667 : PG_RETURN_BOOL(result);
279 : : }
280 : :
281 : : Datum
8652 282 : 237963 : textlike(PG_FUNCTION_ARGS)
283 : : {
6050 tgl@sss.pgh.pa.us 284 : 237963 : text *str = PG_GETARG_TEXT_PP(0);
285 : 237963 : text *pat = PG_GETARG_TEXT_PP(1);
286 : : bool result;
287 : : char *s,
288 : : *p;
289 : : int slen,
290 : : plen;
291 : :
292 [ + + ]: 237963 : s = VARDATA_ANY(str);
293 [ - + - - : 237963 : slen = VARSIZE_ANY_EXHDR(str);
- - - - +
+ ]
294 [ - + ]: 237963 : p = VARDATA_ANY(pat);
295 [ - + - - : 237963 : plen = VARSIZE_ANY_EXHDR(pat);
- - - - -
+ ]
296 : :
1850 peter@eisentraut.org 297 : 237963 : result = (GenericMatchText(s, slen, p, plen, PG_GET_COLLATION()) == LIKE_TRUE);
298 : :
8649 lockhart@fourpalms.o 299 : 237951 : PG_RETURN_BOOL(result);
300 : : }
301 : :
302 : : Datum
8612 tgl@sss.pgh.pa.us 303 : 157149 : textnlike(PG_FUNCTION_ARGS)
304 : : {
6050 305 : 157149 : text *str = PG_GETARG_TEXT_PP(0);
306 : 157149 : text *pat = PG_GETARG_TEXT_PP(1);
307 : : bool result;
308 : : char *s,
309 : : *p;
310 : : int slen,
311 : : plen;
312 : :
313 [ + + ]: 157149 : s = VARDATA_ANY(str);
314 [ - + - - : 157149 : slen = VARSIZE_ANY_EXHDR(str);
- - - - +
+ ]
315 [ - + ]: 157149 : p = VARDATA_ANY(pat);
316 [ - + - - : 157149 : plen = VARSIZE_ANY_EXHDR(pat);
- - - - -
+ ]
317 : :
1850 peter@eisentraut.org 318 : 157149 : result = (GenericMatchText(s, slen, p, plen, PG_GET_COLLATION()) != LIKE_TRUE);
319 : :
8649 lockhart@fourpalms.o 320 : 157149 : PG_RETURN_BOOL(result);
321 : : }
322 : :
323 : : Datum
8248 bruce@momjian.us 324 : 6 : bytealike(PG_FUNCTION_ARGS)
325 : : {
6050 tgl@sss.pgh.pa.us 326 : 6 : bytea *str = PG_GETARG_BYTEA_PP(0);
327 : 6 : bytea *pat = PG_GETARG_BYTEA_PP(1);
328 : : bool result;
329 : : char *s,
330 : : *p;
331 : : int slen,
332 : : plen;
333 : :
334 [ - + ]: 6 : s = VARDATA_ANY(str);
335 [ - + - - : 6 : slen = VARSIZE_ANY_EXHDR(str);
- - - - -
+ ]
336 [ - + ]: 6 : p = VARDATA_ANY(pat);
337 [ - + - - : 6 : plen = VARSIZE_ANY_EXHDR(pat);
- - - - -
+ ]
338 : :
4754 339 : 6 : result = (SB_MatchText(s, slen, p, plen, 0, true) == LIKE_TRUE);
340 : :
8248 bruce@momjian.us 341 : 6 : PG_RETURN_BOOL(result);
342 : : }
343 : :
344 : : Datum
345 : 6 : byteanlike(PG_FUNCTION_ARGS)
346 : : {
6050 tgl@sss.pgh.pa.us 347 : 6 : bytea *str = PG_GETARG_BYTEA_PP(0);
348 : 6 : bytea *pat = PG_GETARG_BYTEA_PP(1);
349 : : bool result;
350 : : char *s,
351 : : *p;
352 : : int slen,
353 : : plen;
354 : :
355 [ - + ]: 6 : s = VARDATA_ANY(str);
356 [ - + - - : 6 : slen = VARSIZE_ANY_EXHDR(str);
- - - - -
+ ]
357 [ - + ]: 6 : p = VARDATA_ANY(pat);
358 [ - + - - : 6 : plen = VARSIZE_ANY_EXHDR(pat);
- - - - -
+ ]
359 : :
4754 360 : 6 : result = (SB_MatchText(s, slen, p, plen, 0, true) != LIKE_TRUE);
361 : :
8248 bruce@momjian.us 362 : 6 : PG_RETURN_BOOL(result);
363 : : }
364 : :
365 : : /*
366 : : * Case-insensitive versions
367 : : */
368 : :
369 : : Datum
8612 tgl@sss.pgh.pa.us 370 : 7709 : nameiclike(PG_FUNCTION_ARGS)
371 : : {
8649 lockhart@fourpalms.o 372 : 7709 : Name str = PG_GETARG_NAME(0);
6050 tgl@sss.pgh.pa.us 373 : 7709 : text *pat = PG_GETARG_TEXT_PP(1);
374 : : bool result;
375 : : text *strtext;
376 : :
2590 noah@leadboat.com 377 : 7709 : strtext = DatumGetTextPP(DirectFunctionCall1(name_text,
378 : : NameGetDatum(str)));
4814 peter_e@gmx.net 379 : 7709 : result = (Generic_Text_IC_like(strtext, pat, PG_GET_COLLATION()) == LIKE_TRUE);
380 : :
8649 lockhart@fourpalms.o 381 : 7709 : PG_RETURN_BOOL(result);
382 : : }
383 : :
384 : : Datum
8612 tgl@sss.pgh.pa.us 385 : 3 : nameicnlike(PG_FUNCTION_ARGS)
386 : : {
8649 lockhart@fourpalms.o 387 : 3 : Name str = PG_GETARG_NAME(0);
6050 tgl@sss.pgh.pa.us 388 : 3 : text *pat = PG_GETARG_TEXT_PP(1);
389 : : bool result;
390 : : text *strtext;
391 : :
2590 noah@leadboat.com 392 : 3 : strtext = DatumGetTextPP(DirectFunctionCall1(name_text,
393 : : NameGetDatum(str)));
4814 peter_e@gmx.net 394 : 3 : result = (Generic_Text_IC_like(strtext, pat, PG_GET_COLLATION()) != LIKE_TRUE);
395 : :
8649 lockhart@fourpalms.o 396 : 3 : PG_RETURN_BOOL(result);
397 : : }
398 : :
399 : : Datum
8612 tgl@sss.pgh.pa.us 400 : 34229 : texticlike(PG_FUNCTION_ARGS)
401 : : {
6050 402 : 34229 : text *str = PG_GETARG_TEXT_PP(0);
403 : 34229 : text *pat = PG_GETARG_TEXT_PP(1);
404 : : bool result;
405 : :
4814 peter_e@gmx.net 406 : 34229 : result = (Generic_Text_IC_like(str, pat, PG_GET_COLLATION()) == LIKE_TRUE);
407 : :
8649 lockhart@fourpalms.o 408 : 34223 : PG_RETURN_BOOL(result);
409 : : }
410 : :
411 : : Datum
8612 tgl@sss.pgh.pa.us 412 : 28 : texticnlike(PG_FUNCTION_ARGS)
413 : : {
6050 414 : 28 : text *str = PG_GETARG_TEXT_PP(0);
415 : 28 : text *pat = PG_GETARG_TEXT_PP(1);
416 : : bool result;
417 : :
4814 peter_e@gmx.net 418 : 28 : result = (Generic_Text_IC_like(str, pat, PG_GET_COLLATION()) != LIKE_TRUE);
419 : :
8649 lockhart@fourpalms.o 420 : 28 : PG_RETURN_BOOL(result);
421 : : }
422 : :
423 : : /*
424 : : * like_escape() --- given a pattern and an ESCAPE string,
425 : : * convert the pattern to use Postgres' standard backslash escape convention.
426 : : */
427 : : Datum
8612 tgl@sss.pgh.pa.us 428 : 106 : like_escape(PG_FUNCTION_ARGS)
429 : : {
6050 430 : 106 : text *pat = PG_GETARG_TEXT_PP(0);
431 : 106 : text *esc = PG_GETARG_TEXT_PP(1);
432 : : text *result;
433 : :
8228 ishii@postgresql.org 434 [ - + ]: 106 : if (pg_database_encoding_max_length() == 1)
6161 andrew@dunslane.net 435 :UBC 0 : result = SB_do_like_escape(pat, esc);
436 : : else
8207 bruce@momjian.us 437 :CBC 106 : result = MB_do_like_escape(pat, esc);
438 : :
8612 tgl@sss.pgh.pa.us 439 : 106 : PG_RETURN_TEXT_P(result);
440 : : }
441 : :
442 : : /*
443 : : * like_escape_bytea() --- given a pattern and an ESCAPE string,
444 : : * convert the pattern to use Postgres' standard backslash escape convention.
445 : : */
446 : : Datum
8248 bruce@momjian.us 447 : 6 : like_escape_bytea(PG_FUNCTION_ARGS)
448 : : {
6050 tgl@sss.pgh.pa.us 449 : 6 : bytea *pat = PG_GETARG_BYTEA_PP(0);
450 : 6 : bytea *esc = PG_GETARG_BYTEA_PP(1);
5995 bruce@momjian.us 451 : 6 : bytea *result = SB_do_like_escape((text *) pat, (text *) esc);
452 : :
453 : 6 : PG_RETURN_BYTEA_P((bytea *) result);
454 : : }
|