1 /*-------------------------------------------------------------------------
4 * like expression handling code.
7 * A big hack of the regexp.c code!! Contributed by
8 * Keith Parks <emkxp01@mtcc.demon.co.uk> (7/95).
10 * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
11 * Portions Copyright (c) 1994, Regents of the University of California
14 * $Header: /cvsroot/pgsql/src/backend/utils/adt/like.c,v 1.39 2000/08/07 01:45:00 thomas Exp $
16 *-------------------------------------------------------------------------
20 #include "mb/pg_wchar.h"
21 #include "utils/builtins.h"
26 #define LIKE_ABORT (-1)
29 static int MatchText(pg_wchar * t, int tlen, pg_wchar * p, int plen, char *e);
30 static int MatchTextLower(pg_wchar * t, int tlen, pg_wchar * p, int plen, char *e);
34 * interface routines called by the function manager
38 namelike(PG_FUNCTION_ARGS)
40 Name n = PG_GETARG_NAME(0);
41 text *p = PG_GETARG_TEXT_P(1);
43 PG_RETURN_BOOL(MatchText(NameStr(*n), strlen(NameStr(*n)),
44 VARDATA(p), (VARSIZE(p)-VARHDRSZ),
50 namenlike(PG_FUNCTION_ARGS)
52 Name n = PG_GETARG_NAME(0);
53 text *p = PG_GETARG_TEXT_P(1);
55 PG_RETURN_BOOL(MatchText(NameStr(*n), strlen(NameStr(*n)),
56 VARDATA(p), (VARSIZE(p)-VARHDRSZ),
62 namelike_escape(PG_FUNCTION_ARGS)
64 Name n = PG_GETARG_NAME(0);
65 text *p = PG_GETARG_TEXT_P(1);
66 text *e = PG_GETARG_TEXT_P(2);
68 PG_RETURN_BOOL(MatchText(NameStr(*n), strlen(NameStr(*n)),
69 VARDATA(p), (VARSIZE(p)-VARHDRSZ),
70 ((VARSIZE(e)-VARHDRSZ) > 0? VARDATA(e): NULL))
75 namenlike_escape(PG_FUNCTION_ARGS)
77 Name n = PG_GETARG_NAME(0);
78 text *p = PG_GETARG_TEXT_P(1);
79 text *e = PG_GETARG_TEXT_P(2);
81 PG_RETURN_BOOL(MatchText(NameStr(*n), strlen(NameStr(*n)),
82 VARDATA(p), (VARSIZE(p)-VARHDRSZ),
83 ((VARSIZE(e)-VARHDRSZ) > 0? VARDATA(e): NULL))
88 textlike(PG_FUNCTION_ARGS)
90 text *s = PG_GETARG_TEXT_P(0);
91 text *p = PG_GETARG_TEXT_P(1);
93 PG_RETURN_BOOL(MatchText(VARDATA(s), (VARSIZE(s)-VARHDRSZ),
94 VARDATA(p), (VARSIZE(p)-VARHDRSZ),
100 textnlike(PG_FUNCTION_ARGS)
102 text *s = PG_GETARG_TEXT_P(0);
103 text *p = PG_GETARG_TEXT_P(1);
105 PG_RETURN_BOOL(MatchText(VARDATA(s), (VARSIZE(s)-VARHDRSZ),
106 VARDATA(p), (VARSIZE(p)-VARHDRSZ),
112 textlike_escape(PG_FUNCTION_ARGS)
114 text *s = PG_GETARG_TEXT_P(0);
115 text *p = PG_GETARG_TEXT_P(1);
116 text *e = PG_GETARG_TEXT_P(2);
118 PG_RETURN_BOOL(MatchText(VARDATA(s), (VARSIZE(s)-VARHDRSZ),
119 VARDATA(p), (VARSIZE(p)-VARHDRSZ),
120 ((VARSIZE(e)-VARHDRSZ) > 0? VARDATA(e): NULL))
125 textnlike_escape(PG_FUNCTION_ARGS)
127 text *s = PG_GETARG_TEXT_P(0);
128 text *p = PG_GETARG_TEXT_P(1);
129 text *e = PG_GETARG_TEXT_P(2);
131 PG_RETURN_BOOL(MatchText(VARDATA(s), (VARSIZE(s)-VARHDRSZ),
132 VARDATA(p), (VARSIZE(p)-VARHDRSZ),
133 ((VARSIZE(e)-VARHDRSZ) > 0? VARDATA(e): NULL))
138 * Case-insensitive versions
142 inamelike(PG_FUNCTION_ARGS)
144 Name n = PG_GETARG_NAME(0);
145 text *p = PG_GETARG_TEXT_P(1);
147 PG_RETURN_BOOL(MatchTextLower(NameStr(*n), strlen(NameStr(*n)),
148 VARDATA(p), (VARSIZE(p)-VARHDRSZ),
154 inamenlike(PG_FUNCTION_ARGS)
156 Name n = PG_GETARG_NAME(0);
157 text *p = PG_GETARG_TEXT_P(1);
159 PG_RETURN_BOOL(MatchTextLower(NameStr(*n), strlen(NameStr(*n)),
160 VARDATA(p), (VARSIZE(p)-VARHDRSZ),
166 inamelike_escape(PG_FUNCTION_ARGS)
168 Name n = PG_GETARG_NAME(0);
169 text *p = PG_GETARG_TEXT_P(1);
170 text *e = PG_GETARG_TEXT_P(2);
172 PG_RETURN_BOOL(MatchTextLower(NameStr(*n), strlen(NameStr(*n)),
173 VARDATA(p), (VARSIZE(p)-VARHDRSZ),
174 ((VARSIZE(e)-VARHDRSZ) > 0? VARDATA(e): NULL))
179 inamenlike_escape(PG_FUNCTION_ARGS)
181 Name n = PG_GETARG_NAME(0);
182 text *p = PG_GETARG_TEXT_P(1);
183 text *e = PG_GETARG_TEXT_P(2);
185 PG_RETURN_BOOL(MatchTextLower(NameStr(*n), strlen(NameStr(*n)),
186 VARDATA(p), (VARSIZE(p)-VARHDRSZ),
187 ((VARSIZE(e)-VARHDRSZ) > 0? VARDATA(e): NULL))
192 itextlike(PG_FUNCTION_ARGS)
194 text *s = PG_GETARG_TEXT_P(0);
195 text *p = PG_GETARG_TEXT_P(1);
197 PG_RETURN_BOOL(MatchTextLower(VARDATA(s), (VARSIZE(s)-VARHDRSZ),
198 VARDATA(p), (VARSIZE(p)-VARHDRSZ),
204 itextnlike(PG_FUNCTION_ARGS)
206 text *s = PG_GETARG_TEXT_P(0);
207 text *p = PG_GETARG_TEXT_P(1);
209 PG_RETURN_BOOL(MatchTextLower(VARDATA(s), (VARSIZE(s)-VARHDRSZ),
210 VARDATA(p), (VARSIZE(p)-VARHDRSZ),
216 itextlike_escape(PG_FUNCTION_ARGS)
218 text *s = PG_GETARG_TEXT_P(0);
219 text *p = PG_GETARG_TEXT_P(1);
220 text *e = PG_GETARG_TEXT_P(2);
222 PG_RETURN_BOOL(MatchTextLower(VARDATA(s), (VARSIZE(s)-VARHDRSZ),
223 VARDATA(p), (VARSIZE(p)-VARHDRSZ),
224 ((VARSIZE(e)-VARHDRSZ) > 0? VARDATA(e): NULL))
229 itextnlike_escape(PG_FUNCTION_ARGS)
231 text *s = PG_GETARG_TEXT_P(0);
232 text *p = PG_GETARG_TEXT_P(1);
233 text *e = PG_GETARG_TEXT_P(2);
235 PG_RETURN_BOOL(MatchTextLower(VARDATA(s), (VARSIZE(s)-VARHDRSZ),
236 VARDATA(p), (VARSIZE(p)-VARHDRSZ),
237 ((VARSIZE(e)-VARHDRSZ) > 0? VARDATA(e): NULL))
243 ** Originally written by Rich $alz, mirror!rs, Wed Nov 26 19:03:17 EST 1986.
244 ** Rich $alz is now <rsalz@bbn.com>.
245 ** Special thanks to Lars Mathiesen <thorinn@diku.dk> for the LABORT code.
247 ** This code was shamelessly stolen from the "pql" code by myself and
248 ** slightly modified :)
250 ** All references to the word "star" were replaced by "percent"
251 ** All references to the word "wild" were replaced by "like"
253 ** All the nice shell RE matching stuff was replaced by just "_" and "%"
255 ** As I don't have a copy of the SQL standard handy I wasn't sure whether
256 ** to leave in the '\' escape character handling.
258 ** Keith Parks. <keith@mtcc.demon.co.uk>
260 ** [SQL92 lets you specify the escape character by saying
261 ** LIKE <pattern> ESCAPE <escape character>. We are a small operation
262 ** so we force you to use '\'. - ay 7/95]
264 ** OK, we now support the SQL9x LIKE <pattern> ESCAPE <char> syntax.
265 ** We should kill the backslash escaping mechanism since it is non-standard
266 ** and undocumented afaik.
267 ** The code is rewritten to avoid requiring null-terminated strings,
268 ** which in turn allows us to leave out some memcpy() operations.
269 ** This code should be faster and take less memory, but no promises...
270 ** - thomas 2000-08-06
274 /*--------------------
275 * Match text and p, return LIKE_TRUE, LIKE_FALSE, or LIKE_ABORT.
277 * LIKE_TRUE: they match
278 * LIKE_FALSE: they don't match
279 * LIKE_ABORT: not only don't they match, but the text is too short.
281 * If LIKE_ABORT is returned, then no suffix of the text can match the
282 * pattern either, so an upper-level % scan can stop scanning now.
283 *--------------------
286 #define NextChar(p, plen) (p)++, (plen)--
289 MatchText(pg_wchar * t, int tlen, pg_wchar * p, int plen, char *e)
291 /* Fast path for match-everything pattern
292 * Include weird case of escape character as a percent sign or underscore,
293 * when presumably that wildcard character becomes a literal.
295 if ((plen == 1) && (*p == '%')
296 && ! ((e != NULL) && (*e == '%')))
299 while ((tlen > 0) && (plen > 0))
301 /* If an escape character was specified and we find it here in the pattern,
302 * then we'd better have an exact match for the next character.
304 if ((e != NULL) && (*p == *e))
307 if ((plen <= 0) || (*t != *p))
312 /* %% is the same as % according to the SQL standard */
313 /* Advance past all %'s */
314 while ((plen > 0) && (*p == '%'))
316 /* Trailing percent matches everything. */
321 * Otherwise, scan for a text position at which we can
322 * match the rest of the pattern.
327 * Optimization to prevent most recursion: don't
328 * recurse unless first pattern char might match this
331 if ((*t == *p) || (*p == '_')
332 || ((e != NULL) && (*p == *e)))
334 int matched = MatchText(t, tlen, p, plen, e);
336 if (matched != LIKE_FALSE)
337 return matched; /* TRUE or ABORT */
344 * End of text with no match, so no point in trying later
345 * places to start matching this pattern.
349 else if ((*p != '_') && (*t != *p))
351 /* Not the single-character wildcard and no explicit match?
352 * Then time to quit...
362 return LIKE_FALSE; /* end of pattern, but not of text */
364 /* End of input string. Do we have matching pattern remaining? */
365 while ((plen > 0) && (*p == '%')) /* allow multiple %'s at end of pattern */
371 * End of text with no match, so no point in trying later places to
372 * start matching this pattern.
378 MatchTextLower(pg_wchar * t, int tlen, pg_wchar * p, int plen, char *e)
380 /* Fast path for match-everything pattern
381 * Include weird case of escape character as a percent sign or underscore,
382 * when presumably that wildcard character becomes a literal.
384 if ((plen == 1) && (*p == '%')
385 && ! ((e != NULL) && (*e == '%')))
388 while ((tlen > 0) && (plen > 0))
390 /* If an escape character was specified and we find it here in the pattern,
391 * then we'd better have an exact match for the next character.
393 if ((e != NULL) && (tolower(*p) == tolower(*e)))
396 if ((plen <= 0) || (tolower(*t) != tolower(*p)))
401 /* %% is the same as % according to the SQL standard */
402 /* Advance past all %'s */
403 while ((plen > 0) && (*p == '%'))
405 /* Trailing percent matches everything. */
410 * Otherwise, scan for a text position at which we can
411 * match the rest of the pattern.
416 * Optimization to prevent most recursion: don't
417 * recurse unless first pattern char might match this
420 if ((tolower(*t) == tolower(*p)) || (*p == '_')
421 || ((e != NULL) && (tolower(*p) == tolower(*e))))
423 int matched = MatchText(t, tlen, p, plen, e);
425 if (matched != LIKE_FALSE)
426 return matched; /* TRUE or ABORT */
433 * End of text with no match, so no point in trying later
434 * places to start matching this pattern.
438 else if ((*p != '_') && (tolower(*t) != tolower(*p)))
448 return LIKE_FALSE; /* end of pattern, but not of text */
450 /* End of input string. Do we have matching pattern remaining? */
451 while ((plen > 0) && (*p == '%')) /* allow multiple %'s at end of pattern */
457 * End of text with no match, so no point in trying later places to
458 * start matching this pattern.
461 } /* MatchTextLower() */