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