]> granicus.if.org Git - postgresql/blob - src/backend/utils/adt/like.c
Ye-old pgindent run. Same 4-space tabs.
[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.34 2000/04/12 17:15:50 momjian Exp $
15  *
16  *-------------------------------------------------------------------------
17  */
18 #include "postgres.h"
19 #include "mb/pg_wchar.h"
20 #include "utils/builtins.h"
21
22 static int      like(pg_wchar * text, pg_wchar * p);
23
24 /*
25  *      interface routines called by the function manager
26  */
27
28 /*
29    fixedlen_like:
30
31    a generic fixed length like routine
32                  s              - the string to match against  (not necessarily null-terminated)
33                  p                 - the pattern
34                  charlen   - the length of the string
35 */
36 static bool
37 fixedlen_like(char *s, struct varlena * p, int charlen)
38 {
39         pg_wchar   *sterm,
40                            *pterm;
41         int                     result;
42         int                     len;
43
44         if (!s || !p)
45                 return FALSE;
46
47         /* be sure sterm is null-terminated */
48 #ifdef MULTIBYTE
49         sterm = (pg_wchar *) palloc((charlen + 1) * sizeof(pg_wchar));
50         (void) pg_mb2wchar_with_len((unsigned char *) s, sterm, charlen);
51 #else
52         sterm = (char *) palloc(charlen + 1);
53         StrNCpy(sterm, s, charlen + 1);
54 #endif
55
56         /*
57          * p is a text = varlena, not a string so we have to make a string
58          * from the vl_data field of the struct.
59          */
60
61         /* palloc the length of the text + the null character */
62         len = VARSIZE(p) - VARHDRSZ;
63 #ifdef MULTIBYTE
64         pterm = (pg_wchar *) palloc((len + 1) * sizeof(pg_wchar));
65         (void) pg_mb2wchar_with_len((unsigned char *) VARDATA(p), pterm, len);
66 #else
67         pterm = (char *) palloc(len + 1);
68         memmove(pterm, VARDATA(p), len);
69         *(pterm + len) = (char) NULL;
70 #endif
71
72         /* do the regexp matching */
73         result = like(sterm, pterm);
74
75         pfree(sterm);
76         pfree(pterm);
77
78         return (bool) result;
79 }
80
81 bool
82 namelike(NameData *n, struct varlena * p)
83 {
84         if (!n)
85                 return FALSE;
86         return fixedlen_like(NameStr(*n), p, NAMEDATALEN);
87 }
88
89 bool
90 namenlike(NameData *s, struct varlena * p)
91 {
92         return !namelike(s, p);
93 }
94
95 bool
96 textlike(struct varlena * s, struct varlena * p)
97 {
98         if (!s)
99                 return FALSE;
100         return fixedlen_like(VARDATA(s), p, VARSIZE(s) - VARHDRSZ);
101 }
102
103 bool
104 textnlike(struct varlena * s, struct varlena * p)
105 {
106         return !textlike(s, p);
107 }
108
109
110 /*
111 **      Originally written by Rich $alz, mirror!rs, Wed Nov 26 19:03:17 EST 1986.
112 **      Rich $alz is now <rsalz@bbn.com>.
113 **      Special thanks to Lars Mathiesen <thorinn@diku.dk> for the LABORT code.
114 **
115 **      This code was shamelessly stolen from the "pql" code by myself and
116 **      slightly modified :)
117 **
118 **      All references to the word "star" were replaced by "percent"
119 **      All references to the word "wild" were replaced by "like"
120 **
121 **      All the nice shell RE matching stuff was replaced by just "_" and "%"
122 **
123 **      As I don't have a copy of the SQL standard handy I wasn't sure whether
124 **      to leave in the '\' escape character handling.
125 **
126 **      Keith Parks. <keith@mtcc.demon.co.uk>
127 **
128 **      [SQL92 lets you specify the escape character by saying
129 **       LIKE <pattern> ESCAPE <escape character>. We are a small operation
130 **       so we force you to use '\'. - ay 7/95]
131 **
132 */
133
134 #define LIKE_TRUE                                               1
135 #define LIKE_FALSE                                              0
136 #define LIKE_ABORT                                              -1
137
138 /*--------------------
139  *      Match text and p, return LIKE_TRUE, LIKE_FALSE, or LIKE_ABORT.
140  *
141  *      LIKE_TRUE: they match
142  *      LIKE_FALSE: they don't match
143  *      LIKE_ABORT: not only don't they match, but the text is too short.
144  *
145  * If LIKE_ABORT is returned, then no suffix of the text can match the
146  * pattern either, so an upper-level % scan can stop scanning now.
147  *--------------------
148  */
149 static int
150 DoMatch(pg_wchar * text, pg_wchar * p)
151 {
152         for (; *p && *text; text ++, p++)
153         {
154                 switch (*p)
155                 {
156                         case '\\':
157                                 /* Literal match with following character. */
158                                 p++;
159                                 /* FALLTHROUGH */
160                         default:
161                                 if (*text !=*p)
162                                         return LIKE_FALSE;
163                                 break;
164                         case '_':
165                                 /* Match any single character. */
166                                 break;
167                         case '%':
168                                 /* %% is the same as % according to the SQL standard */
169                                 /* Advance past all %'s */
170                                 while (*p == '%')
171                                         p++;
172                                 /* Trailing percent matches everything. */
173                                 if (*p == '\0')
174                                         return LIKE_TRUE;
175
176                                 /*
177                                  * Otherwise, scan for a text position at which we can
178                                  * match the rest of the pattern.
179                                  */
180                                 for (; *text; text ++)
181                                 {
182
183                                         /*
184                                          * Optimization to prevent most recursion: don't
185                                          * recurse unless first pattern char might match this
186                                          * text char.
187                                          */
188                                         if (*text == *p || *p == '\\' || *p == '_')
189                                         {
190                                                 int                     matched = DoMatch(text, p);
191
192                                                 if (matched != LIKE_FALSE)
193                                                         return matched;         /* TRUE or ABORT */
194                                         }
195                                 }
196
197                                 /*
198                                  * End of text with no match, so no point in trying later
199                                  * places to start matching this pattern.
200                                  */
201                                 return LIKE_ABORT;
202                 }
203         }
204
205         if (*text !='\0')
206                 return LIKE_FALSE;              /* end of pattern, but not of text */
207
208         /* End of input string.  Do we have matching pattern remaining? */
209         while (*p == '%')                       /* allow multiple %'s at end of pattern */
210                 p++;
211         if (*p == '\0')
212                 return LIKE_TRUE;
213
214         /*
215          * End of text with no match, so no point in trying later places to
216          * start matching this pattern.
217          */
218         return LIKE_ABORT;
219 }
220
221 /*
222 **      User-level routine.  Returns TRUE or FALSE.
223 */
224 static int
225 like(pg_wchar * text, pg_wchar * p)
226 {
227         /* Fast path for match-everything pattern */
228         if (p[0] == '%' && p[1] == '\0')
229                 return TRUE;
230         return DoMatch(text, p) == LIKE_TRUE;
231 }