]> granicus.if.org Git - postgresql/blob - src/backend/utils/adt/like.c
Clean up code to remove the explicit backslash cruft.
[postgresql] / src / backend / utils / adt / like.c
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-2000, PostgreSQL, Inc
11  * Portions Copyright (c) 1994, Regents of the University of California
12  *
13  * IDENTIFICATION
14  *      $Header: /cvsroot/pgsql/src/backend/utils/adt/like.c,v 1.39 2000/08/07 01:45:00 thomas Exp $
15  *
16  *-------------------------------------------------------------------------
17  */
18 #include "postgres.h"
19 #include <ctype.h>
20 #include "mb/pg_wchar.h"
21 #include "utils/builtins.h"
22
23
24 #define LIKE_TRUE                                               1
25 #define LIKE_FALSE                                              0
26 #define LIKE_ABORT                                              (-1)
27
28
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);
31
32
33 /*
34  *      interface routines called by the function manager
35  */
36
37 Datum
38 namelike(PG_FUNCTION_ARGS)
39 {
40         Name            n = PG_GETARG_NAME(0);
41         text       *p = PG_GETARG_TEXT_P(1);
42
43         PG_RETURN_BOOL(MatchText(NameStr(*n), strlen(NameStr(*n)),
44                                                          VARDATA(p), (VARSIZE(p)-VARHDRSZ),
45                                                          NULL)
46                                    == LIKE_TRUE);
47 }
48
49 Datum
50 namenlike(PG_FUNCTION_ARGS)
51 {
52         Name            n = PG_GETARG_NAME(0);
53         text       *p = PG_GETARG_TEXT_P(1);
54
55         PG_RETURN_BOOL(MatchText(NameStr(*n), strlen(NameStr(*n)),
56                                                          VARDATA(p), (VARSIZE(p)-VARHDRSZ),
57                                                          NULL)
58                                    != LIKE_TRUE);
59 }
60
61 Datum
62 namelike_escape(PG_FUNCTION_ARGS)
63 {
64         Name            n = PG_GETARG_NAME(0);
65         text       *p = PG_GETARG_TEXT_P(1);
66         text       *e = PG_GETARG_TEXT_P(2);
67
68         PG_RETURN_BOOL(MatchText(NameStr(*n), strlen(NameStr(*n)),
69                                                          VARDATA(p), (VARSIZE(p)-VARHDRSZ),
70                                                          ((VARSIZE(e)-VARHDRSZ) > 0? VARDATA(e): NULL))
71                                    == LIKE_TRUE);
72 }
73
74 Datum
75 namenlike_escape(PG_FUNCTION_ARGS)
76 {
77         Name            n = PG_GETARG_NAME(0);
78         text       *p = PG_GETARG_TEXT_P(1);
79         text       *e = PG_GETARG_TEXT_P(2);
80
81         PG_RETURN_BOOL(MatchText(NameStr(*n), strlen(NameStr(*n)),
82                                                          VARDATA(p), (VARSIZE(p)-VARHDRSZ),
83                                                          ((VARSIZE(e)-VARHDRSZ) > 0? VARDATA(e): NULL))
84                                    != LIKE_TRUE);
85 }
86
87 Datum
88 textlike(PG_FUNCTION_ARGS)
89 {
90         text       *s = PG_GETARG_TEXT_P(0);
91         text       *p = PG_GETARG_TEXT_P(1);
92
93         PG_RETURN_BOOL(MatchText(VARDATA(s), (VARSIZE(s)-VARHDRSZ),
94                                                          VARDATA(p), (VARSIZE(p)-VARHDRSZ),
95                                                          NULL)
96                                    == LIKE_TRUE);
97 }
98
99 Datum
100 textnlike(PG_FUNCTION_ARGS)
101 {
102         text       *s = PG_GETARG_TEXT_P(0);
103         text       *p = PG_GETARG_TEXT_P(1);
104
105         PG_RETURN_BOOL(MatchText(VARDATA(s), (VARSIZE(s)-VARHDRSZ),
106                                                          VARDATA(p), (VARSIZE(p)-VARHDRSZ),
107                                                          NULL)
108                                    != LIKE_TRUE);
109 }
110
111 Datum
112 textlike_escape(PG_FUNCTION_ARGS)
113 {
114         text       *s = PG_GETARG_TEXT_P(0);
115         text       *p = PG_GETARG_TEXT_P(1);
116         text       *e = PG_GETARG_TEXT_P(2);
117
118         PG_RETURN_BOOL(MatchText(VARDATA(s), (VARSIZE(s)-VARHDRSZ),
119                                                          VARDATA(p), (VARSIZE(p)-VARHDRSZ),
120                                                          ((VARSIZE(e)-VARHDRSZ) > 0? VARDATA(e): NULL))
121                                    == LIKE_TRUE);
122 }
123
124 Datum
125 textnlike_escape(PG_FUNCTION_ARGS)
126 {
127         text       *s = PG_GETARG_TEXT_P(0);
128         text       *p = PG_GETARG_TEXT_P(1);
129         text       *e = PG_GETARG_TEXT_P(2);
130
131         PG_RETURN_BOOL(MatchText(VARDATA(s), (VARSIZE(s)-VARHDRSZ),
132                                                          VARDATA(p), (VARSIZE(p)-VARHDRSZ),
133                                                          ((VARSIZE(e)-VARHDRSZ) > 0? VARDATA(e): NULL))
134                                    != LIKE_TRUE);
135 }
136
137 /*
138  * Case-insensitive versions
139  */
140
141 Datum
142 inamelike(PG_FUNCTION_ARGS)
143 {
144         Name            n = PG_GETARG_NAME(0);
145         text       *p = PG_GETARG_TEXT_P(1);
146
147         PG_RETURN_BOOL(MatchTextLower(NameStr(*n), strlen(NameStr(*n)),
148                                                                   VARDATA(p), (VARSIZE(p)-VARHDRSZ),
149                                                                   NULL)
150                                    == LIKE_TRUE);
151 }
152
153 Datum
154 inamenlike(PG_FUNCTION_ARGS)
155 {
156         Name            n = PG_GETARG_NAME(0);
157         text       *p = PG_GETARG_TEXT_P(1);
158
159         PG_RETURN_BOOL(MatchTextLower(NameStr(*n), strlen(NameStr(*n)),
160                                                                   VARDATA(p), (VARSIZE(p)-VARHDRSZ),
161                                                                   NULL)
162                                    != LIKE_TRUE);
163 }
164
165 Datum
166 inamelike_escape(PG_FUNCTION_ARGS)
167 {
168         Name            n = PG_GETARG_NAME(0);
169         text       *p = PG_GETARG_TEXT_P(1);
170         text       *e = PG_GETARG_TEXT_P(2);
171
172         PG_RETURN_BOOL(MatchTextLower(NameStr(*n), strlen(NameStr(*n)),
173                                                                   VARDATA(p), (VARSIZE(p)-VARHDRSZ),
174                                                                   ((VARSIZE(e)-VARHDRSZ) > 0? VARDATA(e): NULL))
175                                    == LIKE_TRUE);
176 }
177
178 Datum
179 inamenlike_escape(PG_FUNCTION_ARGS)
180 {
181         Name            n = PG_GETARG_NAME(0);
182         text       *p = PG_GETARG_TEXT_P(1);
183         text       *e = PG_GETARG_TEXT_P(2);
184
185         PG_RETURN_BOOL(MatchTextLower(NameStr(*n), strlen(NameStr(*n)),
186                                                                   VARDATA(p), (VARSIZE(p)-VARHDRSZ),
187                                                                   ((VARSIZE(e)-VARHDRSZ) > 0? VARDATA(e): NULL))
188                                    != LIKE_TRUE);
189 }
190
191 Datum
192 itextlike(PG_FUNCTION_ARGS)
193 {
194         text       *s = PG_GETARG_TEXT_P(0);
195         text       *p = PG_GETARG_TEXT_P(1);
196
197         PG_RETURN_BOOL(MatchTextLower(VARDATA(s), (VARSIZE(s)-VARHDRSZ),
198                                                                   VARDATA(p), (VARSIZE(p)-VARHDRSZ),
199                                                                   NULL)
200                                    == LIKE_TRUE);
201 }
202
203 Datum
204 itextnlike(PG_FUNCTION_ARGS)
205 {
206         text       *s = PG_GETARG_TEXT_P(0);
207         text       *p = PG_GETARG_TEXT_P(1);
208
209         PG_RETURN_BOOL(MatchTextLower(VARDATA(s), (VARSIZE(s)-VARHDRSZ),
210                                                                   VARDATA(p), (VARSIZE(p)-VARHDRSZ),
211                                                                   NULL)
212                                    != LIKE_TRUE);
213 }
214
215 Datum
216 itextlike_escape(PG_FUNCTION_ARGS)
217 {
218         text       *s = PG_GETARG_TEXT_P(0);
219         text       *p = PG_GETARG_TEXT_P(1);
220         text       *e = PG_GETARG_TEXT_P(2);
221
222         PG_RETURN_BOOL(MatchTextLower(VARDATA(s), (VARSIZE(s)-VARHDRSZ),
223                                                                   VARDATA(p), (VARSIZE(p)-VARHDRSZ),
224                                                                   ((VARSIZE(e)-VARHDRSZ) > 0? VARDATA(e): NULL))
225                                    == LIKE_TRUE);
226 }
227
228 Datum
229 itextnlike_escape(PG_FUNCTION_ARGS)
230 {
231         text       *s = PG_GETARG_TEXT_P(0);
232         text       *p = PG_GETARG_TEXT_P(1);
233         text       *e = PG_GETARG_TEXT_P(2);
234
235         PG_RETURN_BOOL(MatchTextLower(VARDATA(s), (VARSIZE(s)-VARHDRSZ),
236                                                                   VARDATA(p), (VARSIZE(p)-VARHDRSZ),
237                                                                   ((VARSIZE(e)-VARHDRSZ) > 0? VARDATA(e): NULL))
238                                    != LIKE_TRUE);
239 }
240
241
242 /*
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.
246 **
247 **      This code was shamelessly stolen from the "pql" code by myself and
248 **      slightly modified :)
249 **
250 **      All references to the word "star" were replaced by "percent"
251 **      All references to the word "wild" were replaced by "like"
252 **
253 **      All the nice shell RE matching stuff was replaced by just "_" and "%"
254 **
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.
257 **
258 **      Keith Parks. <keith@mtcc.demon.co.uk>
259 **
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]
263 **
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
271 **
272 */
273
274 /*--------------------
275  *      Match text and p, return LIKE_TRUE, LIKE_FALSE, or LIKE_ABORT.
276  *
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.
280  *
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  *--------------------
284  */
285
286 #define NextChar(p, plen) (p)++, (plen)--
287
288 static int
289 MatchText(pg_wchar * t, int tlen, pg_wchar * p, int plen, char *e)
290 {
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.
294          */
295         if ((plen == 1) && (*p == '%')
296                 && ! ((e != NULL) && (*e == '%')))
297                 return LIKE_TRUE;
298
299         while ((tlen > 0) && (plen > 0))
300         {
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.
303                  */
304                 if ((e != NULL) && (*p == *e))
305                 {
306                         NextChar(p, plen);
307                         if ((plen <= 0) || (*t != *p))
308                                 return LIKE_FALSE;
309                 }
310                 else if (*p == '%')
311                 {
312                         /* %% is the same as % according to the SQL standard */
313                         /* Advance past all %'s */
314                         while ((plen > 0) && (*p == '%'))
315                                 NextChar(p, plen);
316                         /* Trailing percent matches everything. */
317                         if (plen <= 0)
318                                 return LIKE_TRUE;
319
320                         /*
321                          * Otherwise, scan for a text position at which we can
322                          * match the rest of the pattern.
323                          */
324                         while (tlen > 0)
325                         {
326                                 /*
327                                  * Optimization to prevent most recursion: don't
328                                  * recurse unless first pattern char might match this
329                                  * text char.
330                                  */
331                                 if ((*t == *p) || (*p == '_')
332                                         || ((e != NULL) && (*p == *e)))
333                                 {
334                                         int matched = MatchText(t, tlen, p, plen, e);
335
336                                         if (matched != LIKE_FALSE)
337                                                 return matched;         /* TRUE or ABORT */
338                                 }
339
340                                 NextChar(t, tlen);
341                         }
342
343                         /*
344                          * End of text with no match, so no point in trying later
345                          * places to start matching this pattern.
346                          */
347                         return LIKE_ABORT;
348                 }
349                 else if ((*p != '_') && (*t != *p))
350                 {
351                         /* Not the single-character wildcard and no explicit match?
352                          * Then time to quit...
353                          */
354                         return LIKE_FALSE;
355                 }
356
357                 NextChar(t, tlen);
358                 NextChar(p, plen);
359         }
360
361         if (tlen > 0)
362                 return LIKE_FALSE;              /* end of pattern, but not of text */
363
364         /* End of input string.  Do we have matching pattern remaining? */
365         while ((plen > 0) && (*p == '%'))       /* allow multiple %'s at end of pattern */
366                 NextChar(p, plen);
367         if (plen <= 0)
368                 return LIKE_TRUE;
369
370         /*
371          * End of text with no match, so no point in trying later places to
372          * start matching this pattern.
373          */
374         return LIKE_ABORT;
375 } /* MatchText() */
376
377 static int
378 MatchTextLower(pg_wchar * t, int tlen, pg_wchar * p, int plen, char *e)
379 {
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.
383          */
384         if ((plen == 1) && (*p == '%')
385                 && ! ((e != NULL) && (*e == '%')))
386                 return LIKE_TRUE;
387
388         while ((tlen > 0) && (plen > 0))
389         {
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.
392                  */
393                 if ((e != NULL) && (tolower(*p) == tolower(*e)))
394                 {
395                         NextChar(p, plen);
396                         if ((plen <= 0) || (tolower(*t) != tolower(*p)))
397                                 return LIKE_FALSE;
398                 }
399                 else if (*p == '%')
400                 {
401                         /* %% is the same as % according to the SQL standard */
402                         /* Advance past all %'s */
403                         while ((plen > 0) && (*p == '%'))
404                                 NextChar(p, plen);
405                         /* Trailing percent matches everything. */
406                         if (plen <= 0)
407                                 return LIKE_TRUE;
408
409                         /*
410                          * Otherwise, scan for a text position at which we can
411                          * match the rest of the pattern.
412                          */
413                         while (tlen > 0)
414                         {
415                                 /*
416                                  * Optimization to prevent most recursion: don't
417                                  * recurse unless first pattern char might match this
418                                  * text char.
419                                  */
420                                 if ((tolower(*t) == tolower(*p)) || (*p == '_')
421                                         || ((e != NULL) && (tolower(*p) == tolower(*e))))
422                                 {
423                                         int matched = MatchText(t, tlen, p, plen, e);
424
425                                         if (matched != LIKE_FALSE)
426                                                 return matched;         /* TRUE or ABORT */
427                                 }
428
429                                 NextChar(t, tlen);
430                         }
431
432                         /*
433                          * End of text with no match, so no point in trying later
434                          * places to start matching this pattern.
435                          */
436                         return LIKE_ABORT;
437                 }
438                 else if ((*p != '_') && (tolower(*t) != tolower(*p)))
439                 {
440                         return LIKE_FALSE;
441                 }
442
443                 NextChar(t, tlen);
444                 NextChar(p, plen);
445         }
446
447         if (tlen > 0)
448                 return LIKE_FALSE;              /* end of pattern, but not of text */
449
450         /* End of input string.  Do we have matching pattern remaining? */
451         while ((plen > 0) && (*p == '%'))       /* allow multiple %'s at end of pattern */
452                 NextChar(p, plen);
453         if (plen <= 0)
454                 return LIKE_TRUE;
455
456         /*
457          * End of text with no match, so no point in trying later places to
458          * start matching this pattern.
459          */
460         return LIKE_ABORT;
461 } /* MatchTextLower() */