]> granicus.if.org Git - postgresql/blob - src/backend/utils/adt/like.c
4223bffb1883af2531f18fcc5fe667be3cc8c1ac
[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-2006, PostgreSQL Global Development Group
11  * Portions Copyright (c) 1994, Regents of the University of California
12  *
13  * IDENTIFICATION
14  *      $PostgreSQL: pgsql/src/backend/utils/adt/like.c,v 1.66 2006/10/04 00:29:59 momjian Exp $
15  *
16  *-------------------------------------------------------------------------
17  */
18 #include "postgres.h"
19
20 #include <ctype.h>
21
22 #include "mb/pg_wchar.h"
23 #include "utils/builtins.h"
24
25
26 #define LIKE_TRUE                                               1
27 #define LIKE_FALSE                                              0
28 #define LIKE_ABORT                                              (-1)
29
30
31 static int      MatchText(char *t, int tlen, char *p, int plen);
32 static int      MatchTextIC(char *t, int tlen, char *p, int plen);
33 static int      MatchBytea(char *t, int tlen, char *p, int plen);
34 static text *do_like_escape(text *, text *);
35
36 static int      MBMatchText(char *t, int tlen, char *p, int plen);
37 static int      MBMatchTextIC(char *t, int tlen, char *p, int plen);
38 static text *MB_do_like_escape(text *, text *);
39
40 /*--------------------
41  * Support routine for MatchText. Compares given multibyte streams
42  * as wide characters. If they match, returns 1 otherwise returns 0.
43  *--------------------
44  */
45 static int
46 wchareq(char *p1, char *p2)
47 {
48         int                     p1_len;
49
50         /* Optimization:  quickly compare the first byte. */
51         if (*p1 != *p2)
52                 return 0;
53
54         p1_len = pg_mblen(p1);
55         if (pg_mblen(p2) != p1_len)
56                 return 0;
57
58         /* They are the same length */
59         while (p1_len--)
60         {
61                 if (*p1++ != *p2++)
62                         return 0;
63         }
64         return 1;
65 }
66
67 /*
68  * Formerly we had a routine iwchareq() here that tried to do case-insensitive
69  * comparison of multibyte characters.  It did not work at all, however,
70  * because it relied on tolower() which has a single-byte API ... and
71  * towlower() wouldn't be much better since we have no suitably cheap way
72  * of getting a single character transformed to the system's wchar_t format.
73  * So now, we just downcase the strings using lower() and apply regular LIKE
74  * comparison.  This should be revisited when we install better locale support.
75  *
76  * Note that MBMatchText and MBMatchTextIC do exactly the same thing now.
77  * Is it worth refactoring to avoid duplicated code?  They might become
78  * different again in the future.
79  */
80
81 /* Set up to compile like_match.c for multibyte characters */
82 #define CHAREQ(p1, p2) wchareq(p1, p2)
83 #define ICHAREQ(p1, p2) wchareq(p1, p2)
84 #define NextChar(p, plen) \
85         do { int __l = pg_mblen(p); (p) +=__l; (plen) -=__l; } while (0)
86 #define CopyAdvChar(dst, src, srclen) \
87         do { int __l = pg_mblen(src); \
88                  (srclen) -= __l; \
89                  while (__l-- > 0) \
90                          *(dst)++ = *(src)++; \
91            } while (0)
92
93 #define MatchText       MBMatchText
94 #define MatchTextIC MBMatchTextIC
95 #define do_like_escape  MB_do_like_escape
96
97 #include "like_match.c"
98
99 #undef CHAREQ
100 #undef ICHAREQ
101 #undef NextChar
102 #undef CopyAdvChar
103 #undef MatchText
104 #undef MatchTextIC
105 #undef do_like_escape
106
107 /* Set up to compile like_match.c for single-byte characters */
108 #define CHAREQ(p1, p2) (*(p1) == *(p2))
109 #define ICHAREQ(p1, p2) (tolower((unsigned char) *(p1)) == tolower((unsigned char) *(p2)))
110 #define NextChar(p, plen) ((p)++, (plen)--)
111 #define CopyAdvChar(dst, src, srclen) (*(dst)++ = *(src)++, (srclen)--)
112
113 #include "like_match.c"
114
115 /* And some support for BYTEA */
116 #define BYTEA_CHAREQ(p1, p2) (*(p1) == *(p2))
117 #define BYTEA_NextChar(p, plen) ((p)++, (plen)--)
118 #define BYTEA_CopyAdvChar(dst, src, srclen) (*(dst)++ = *(src)++, (srclen)--)
119
120
121 /*
122  *      interface routines called by the function manager
123  */
124
125 Datum
126 namelike(PG_FUNCTION_ARGS)
127 {
128         Name            str = PG_GETARG_NAME(0);
129         text       *pat = PG_GETARG_TEXT_P(1);
130         bool            result;
131         char       *s,
132                            *p;
133         int                     slen,
134                                 plen;
135
136         s = NameStr(*str);
137         slen = strlen(s);
138         p = VARDATA(pat);
139         plen = (VARSIZE(pat) - VARHDRSZ);
140
141         if (pg_database_encoding_max_length() == 1)
142                 result = (MatchText(s, slen, p, plen) == LIKE_TRUE);
143         else
144                 result = (MBMatchText(s, slen, p, plen) == LIKE_TRUE);
145
146         PG_RETURN_BOOL(result);
147 }
148
149 Datum
150 namenlike(PG_FUNCTION_ARGS)
151 {
152         Name            str = PG_GETARG_NAME(0);
153         text       *pat = PG_GETARG_TEXT_P(1);
154         bool            result;
155         char       *s,
156                            *p;
157         int                     slen,
158                                 plen;
159
160         s = NameStr(*str);
161         slen = strlen(s);
162         p = VARDATA(pat);
163         plen = (VARSIZE(pat) - VARHDRSZ);
164
165         if (pg_database_encoding_max_length() == 1)
166                 result = (MatchText(s, slen, p, plen) != LIKE_TRUE);
167         else
168                 result = (MBMatchText(s, slen, p, plen) != LIKE_TRUE);
169
170         PG_RETURN_BOOL(result);
171 }
172
173 Datum
174 textlike(PG_FUNCTION_ARGS)
175 {
176         text       *str = PG_GETARG_TEXT_P(0);
177         text       *pat = PG_GETARG_TEXT_P(1);
178         bool            result;
179         char       *s,
180                            *p;
181         int                     slen,
182                                 plen;
183
184         s = VARDATA(str);
185         slen = (VARSIZE(str) - VARHDRSZ);
186         p = VARDATA(pat);
187         plen = (VARSIZE(pat) - VARHDRSZ);
188
189         if (pg_database_encoding_max_length() == 1)
190                 result = (MatchText(s, slen, p, plen) == LIKE_TRUE);
191         else
192                 result = (MBMatchText(s, slen, p, plen) == LIKE_TRUE);
193
194         PG_RETURN_BOOL(result);
195 }
196
197 Datum
198 textnlike(PG_FUNCTION_ARGS)
199 {
200         text       *str = PG_GETARG_TEXT_P(0);
201         text       *pat = PG_GETARG_TEXT_P(1);
202         bool            result;
203         char       *s,
204                            *p;
205         int                     slen,
206                                 plen;
207
208         s = VARDATA(str);
209         slen = (VARSIZE(str) - VARHDRSZ);
210         p = VARDATA(pat);
211         plen = (VARSIZE(pat) - VARHDRSZ);
212
213         if (pg_database_encoding_max_length() == 1)
214                 result = (MatchText(s, slen, p, plen) != LIKE_TRUE);
215         else
216                 result = (MBMatchText(s, slen, p, plen) != LIKE_TRUE);
217
218         PG_RETURN_BOOL(result);
219 }
220
221 Datum
222 bytealike(PG_FUNCTION_ARGS)
223 {
224         bytea      *str = PG_GETARG_BYTEA_P(0);
225         bytea      *pat = PG_GETARG_BYTEA_P(1);
226         bool            result;
227         char       *s,
228                            *p;
229         int                     slen,
230                                 plen;
231
232         s = VARDATA(str);
233         slen = (VARSIZE(str) - VARHDRSZ);
234         p = VARDATA(pat);
235         plen = (VARSIZE(pat) - VARHDRSZ);
236
237         result = (MatchBytea(s, slen, p, plen) == LIKE_TRUE);
238
239         PG_RETURN_BOOL(result);
240 }
241
242 Datum
243 byteanlike(PG_FUNCTION_ARGS)
244 {
245         bytea      *str = PG_GETARG_BYTEA_P(0);
246         bytea      *pat = PG_GETARG_BYTEA_P(1);
247         bool            result;
248         char       *s,
249                            *p;
250         int                     slen,
251                                 plen;
252
253         s = VARDATA(str);
254         slen = (VARSIZE(str) - VARHDRSZ);
255         p = VARDATA(pat);
256         plen = (VARSIZE(pat) - VARHDRSZ);
257
258         result = (MatchBytea(s, slen, p, plen) != LIKE_TRUE);
259
260         PG_RETURN_BOOL(result);
261 }
262
263 /*
264  * Case-insensitive versions
265  */
266
267 Datum
268 nameiclike(PG_FUNCTION_ARGS)
269 {
270         Name            str = PG_GETARG_NAME(0);
271         text       *pat = PG_GETARG_TEXT_P(1);
272         bool            result;
273         char       *s,
274                            *p;
275         int                     slen,
276                                 plen;
277
278         if (pg_database_encoding_max_length() == 1)
279         {
280                 s = NameStr(*str);
281                 slen = strlen(s);
282                 p = VARDATA(pat);
283                 plen = (VARSIZE(pat) - VARHDRSZ);
284                 result = (MatchTextIC(s, slen, p, plen) == LIKE_TRUE);
285         }
286         else
287         {
288                 /* Force inputs to lower case to achieve case insensitivity */
289                 text       *strtext;
290
291                 strtext = DatumGetTextP(DirectFunctionCall1(name_text,
292                                                                                                         NameGetDatum(str)));
293                 strtext = DatumGetTextP(DirectFunctionCall1(lower,
294                                                                                                   PointerGetDatum(strtext)));
295                 pat = DatumGetTextP(DirectFunctionCall1(lower,
296                                                                                                 PointerGetDatum(pat)));
297
298                 s = VARDATA(strtext);
299                 slen = (VARSIZE(strtext) - VARHDRSZ);
300                 p = VARDATA(pat);
301                 plen = (VARSIZE(pat) - VARHDRSZ);
302                 result = (MBMatchTextIC(s, slen, p, plen) == LIKE_TRUE);
303         }
304
305         PG_RETURN_BOOL(result);
306 }
307
308 Datum
309 nameicnlike(PG_FUNCTION_ARGS)
310 {
311         Name            str = PG_GETARG_NAME(0);
312         text       *pat = PG_GETARG_TEXT_P(1);
313         bool            result;
314         char       *s,
315                            *p;
316         int                     slen,
317                                 plen;
318
319         if (pg_database_encoding_max_length() == 1)
320         {
321                 s = NameStr(*str);
322                 slen = strlen(s);
323                 p = VARDATA(pat);
324                 plen = (VARSIZE(pat) - VARHDRSZ);
325                 result = (MatchTextIC(s, slen, p, plen) != LIKE_TRUE);
326         }
327         else
328         {
329                 /* Force inputs to lower case to achieve case insensitivity */
330                 text       *strtext;
331
332                 strtext = DatumGetTextP(DirectFunctionCall1(name_text,
333                                                                                                         NameGetDatum(str)));
334                 strtext = DatumGetTextP(DirectFunctionCall1(lower,
335                                                                                                   PointerGetDatum(strtext)));
336                 pat = DatumGetTextP(DirectFunctionCall1(lower,
337                                                                                                 PointerGetDatum(pat)));
338
339                 s = VARDATA(strtext);
340                 slen = (VARSIZE(strtext) - VARHDRSZ);
341                 p = VARDATA(pat);
342                 plen = (VARSIZE(pat) - VARHDRSZ);
343                 result = (MBMatchTextIC(s, slen, p, plen) != LIKE_TRUE);
344         }
345
346         PG_RETURN_BOOL(result);
347 }
348
349 Datum
350 texticlike(PG_FUNCTION_ARGS)
351 {
352         text       *str = PG_GETARG_TEXT_P(0);
353         text       *pat = PG_GETARG_TEXT_P(1);
354         bool            result;
355         char       *s,
356                            *p;
357         int                     slen,
358                                 plen;
359
360         if (pg_database_encoding_max_length() == 1)
361         {
362                 s = VARDATA(str);
363                 slen = (VARSIZE(str) - VARHDRSZ);
364                 p = VARDATA(pat);
365                 plen = (VARSIZE(pat) - VARHDRSZ);
366                 result = (MatchTextIC(s, slen, p, plen) == LIKE_TRUE);
367         }
368         else
369         {
370                 /* Force inputs to lower case to achieve case insensitivity */
371                 str = DatumGetTextP(DirectFunctionCall1(lower,
372                                                                                                 PointerGetDatum(str)));
373                 pat = DatumGetTextP(DirectFunctionCall1(lower,
374                                                                                                 PointerGetDatum(pat)));
375                 s = VARDATA(str);
376                 slen = (VARSIZE(str) - VARHDRSZ);
377                 p = VARDATA(pat);
378                 plen = (VARSIZE(pat) - VARHDRSZ);
379                 result = (MBMatchTextIC(s, slen, p, plen) == LIKE_TRUE);
380         }
381
382         PG_RETURN_BOOL(result);
383 }
384
385 Datum
386 texticnlike(PG_FUNCTION_ARGS)
387 {
388         text       *str = PG_GETARG_TEXT_P(0);
389         text       *pat = PG_GETARG_TEXT_P(1);
390         bool            result;
391         char       *s,
392                            *p;
393         int                     slen,
394                                 plen;
395
396         if (pg_database_encoding_max_length() == 1)
397         {
398                 s = VARDATA(str);
399                 slen = (VARSIZE(str) - VARHDRSZ);
400                 p = VARDATA(pat);
401                 plen = (VARSIZE(pat) - VARHDRSZ);
402                 result = (MatchTextIC(s, slen, p, plen) != LIKE_TRUE);
403         }
404         else
405         {
406                 /* Force inputs to lower case to achieve case insensitivity */
407                 str = DatumGetTextP(DirectFunctionCall1(lower,
408                                                                                                 PointerGetDatum(str)));
409                 pat = DatumGetTextP(DirectFunctionCall1(lower,
410                                                                                                 PointerGetDatum(pat)));
411                 s = VARDATA(str);
412                 slen = (VARSIZE(str) - VARHDRSZ);
413                 p = VARDATA(pat);
414                 plen = (VARSIZE(pat) - VARHDRSZ);
415                 result = (MBMatchTextIC(s, slen, p, plen) != LIKE_TRUE);
416         }
417
418         PG_RETURN_BOOL(result);
419 }
420
421 /*
422  * like_escape() --- given a pattern and an ESCAPE string,
423  * convert the pattern to use Postgres' standard backslash escape convention.
424  */
425 Datum
426 like_escape(PG_FUNCTION_ARGS)
427 {
428         text       *pat = PG_GETARG_TEXT_P(0);
429         text       *esc = PG_GETARG_TEXT_P(1);
430         text       *result;
431
432         if (pg_database_encoding_max_length() == 1)
433                 result = do_like_escape(pat, esc);
434         else
435                 result = MB_do_like_escape(pat, esc);
436
437         PG_RETURN_TEXT_P(result);
438 }
439
440 /*
441  * like_escape_bytea() --- given a pattern and an ESCAPE string,
442  * convert the pattern to use Postgres' standard backslash escape convention.
443  */
444 Datum
445 like_escape_bytea(PG_FUNCTION_ARGS)
446 {
447         bytea      *pat = PG_GETARG_BYTEA_P(0);
448         bytea      *esc = PG_GETARG_BYTEA_P(1);
449         bytea      *result;
450         char       *p,
451                            *e,
452                            *r;
453         int                     plen,
454                                 elen;
455         bool            afterescape;
456
457         p = VARDATA(pat);
458         plen = (VARSIZE(pat) - VARHDRSZ);
459         e = VARDATA(esc);
460         elen = (VARSIZE(esc) - VARHDRSZ);
461
462         /*
463          * Worst-case pattern growth is 2x --- unlikely, but it's hardly worth
464          * trying to calculate the size more accurately than that.
465          */
466         result = (text *) palloc(plen * 2 + VARHDRSZ);
467         r = VARDATA(result);
468
469         if (elen == 0)
470         {
471                 /*
472                  * No escape character is wanted.  Double any backslashes in the
473                  * pattern to make them act like ordinary characters.
474                  */
475                 while (plen > 0)
476                 {
477                         if (*p == '\\')
478                                 *r++ = '\\';
479                         BYTEA_CopyAdvChar(r, p, plen);
480                 }
481         }
482         else
483         {
484                 /*
485                  * The specified escape must be only a single character.
486                  */
487                 BYTEA_NextChar(e, elen);
488                 if (elen != 0)
489                         ereport(ERROR,
490                                         (errcode(ERRCODE_INVALID_ESCAPE_SEQUENCE),
491                                          errmsg("invalid escape string"),
492                                   errhint("Escape string must be empty or one character.")));
493
494                 e = VARDATA(esc);
495
496                 /*
497                  * If specified escape is '\', just copy the pattern as-is.
498                  */
499                 if (*e == '\\')
500                 {
501                         memcpy(result, pat, VARSIZE(pat));
502                         PG_RETURN_BYTEA_P(result);
503                 }
504
505                 /*
506                  * Otherwise, convert occurrences of the specified escape character to
507                  * '\', and double occurrences of '\' --- unless they immediately
508                  * follow an escape character!
509                  */
510                 afterescape = false;
511                 while (plen > 0)
512                 {
513                         if (BYTEA_CHAREQ(p, e) && !afterescape)
514                         {
515                                 *r++ = '\\';
516                                 BYTEA_NextChar(p, plen);
517                                 afterescape = true;
518                         }
519                         else if (*p == '\\')
520                         {
521                                 *r++ = '\\';
522                                 if (!afterescape)
523                                         *r++ = '\\';
524                                 BYTEA_NextChar(p, plen);
525                                 afterescape = false;
526                         }
527                         else
528                         {
529                                 BYTEA_CopyAdvChar(r, p, plen);
530                                 afterescape = false;
531                         }
532                 }
533         }
534
535         VARATT_SIZEP(result) = r - ((char *) result);
536
537         PG_RETURN_BYTEA_P(result);
538 }
539
540 /*
541  * Same as above, but specifically for bytea (binary) datatype
542  */
543 static int
544 MatchBytea(char *t, int tlen, char *p, int plen)
545 {
546         /* Fast path for match-everything pattern */
547         if ((plen == 1) && (*p == '%'))
548                 return LIKE_TRUE;
549
550         while ((tlen > 0) && (plen > 0))
551         {
552                 if (*p == '\\')
553                 {
554                         /* Next pattern char must match literally, whatever it is */
555                         BYTEA_NextChar(p, plen);
556                         if ((plen <= 0) || !BYTEA_CHAREQ(t, p))
557                                 return LIKE_FALSE;
558                 }
559                 else if (*p == '%')
560                 {
561                         /* %% is the same as % according to the SQL standard */
562                         /* Advance past all %'s */
563                         while ((plen > 0) && (*p == '%'))
564                                 BYTEA_NextChar(p, plen);
565                         /* Trailing percent matches everything. */
566                         if (plen <= 0)
567                                 return LIKE_TRUE;
568
569                         /*
570                          * Otherwise, scan for a text position at which we can match the
571                          * rest of the pattern.
572                          */
573                         while (tlen > 0)
574                         {
575                                 /*
576                                  * Optimization to prevent most recursion: don't recurse
577                                  * unless first pattern char might match this text char.
578                                  */
579                                 if (BYTEA_CHAREQ(t, p) || (*p == '\\') || (*p == '_'))
580                                 {
581                                         int                     matched = MatchBytea(t, tlen, p, plen);
582
583                                         if (matched != LIKE_FALSE)
584                                                 return matched; /* TRUE or ABORT */
585                                 }
586
587                                 BYTEA_NextChar(t, tlen);
588                         }
589
590                         /*
591                          * End of text with no match, so no point in trying later places
592                          * to start matching this pattern.
593                          */
594                         return LIKE_ABORT;
595                 }
596                 else if ((*p != '_') && !BYTEA_CHAREQ(t, p))
597                 {
598                         /*
599                          * Not the single-character wildcard and no explicit match? Then
600                          * time to quit...
601                          */
602                         return LIKE_FALSE;
603                 }
604
605                 BYTEA_NextChar(t, tlen);
606                 BYTEA_NextChar(p, plen);
607         }
608
609         if (tlen > 0)
610                 return LIKE_FALSE;              /* end of pattern, but not of text */
611
612         /* End of input string.  Do we have matching pattern remaining? */
613         while ((plen > 0) && (*p == '%'))       /* allow multiple %'s at end of
614                                                                                  * pattern */
615                 BYTEA_NextChar(p, plen);
616         if (plen <= 0)
617                 return LIKE_TRUE;
618
619         /*
620          * End of text with no match, so no point in trying later places to start
621          * matching this pattern.
622          */
623         return LIKE_ABORT;
624 }       /* MatchBytea() */