~ubuntu-branches/ubuntu/oneiric/postgresql-9.1/oneiric-security

« back to all changes in this revision

Viewing changes to src/backend/utils/adt/like.c

  • Committer: Bazaar Package Importer
  • Author(s): Martin Pitt
  • Date: 2011-05-11 10:41:53 UTC
  • Revision ID: james.westby@ubuntu.com-20110511104153-psbh2o58553fv1m0
Tags: upstream-9.1~beta1
ImportĀ upstreamĀ versionĀ 9.1~beta1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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-2011, PostgreSQL Global Development Group
 
11
 * Portions Copyright (c) 1994, Regents of the University of California
 
12
 *
 
13
 * IDENTIFICATION
 
14
 *      src/backend/utils/adt/like.c
 
15
 *
 
16
 *-------------------------------------------------------------------------
 
17
 */
 
18
#include "postgres.h"
 
19
 
 
20
#include <ctype.h>
 
21
 
 
22
#include "catalog/pg_collation.h"
 
23
#include "mb/pg_wchar.h"
 
24
#include "utils/builtins.h"
 
25
#include "utils/pg_locale.h"
 
26
 
 
27
 
 
28
#define LIKE_TRUE                                               1
 
29
#define LIKE_FALSE                                              0
 
30
#define LIKE_ABORT                                              (-1)
 
31
 
 
32
 
 
33
static int SB_MatchText(char *t, int tlen, char *p, int plen,
 
34
                         pg_locale_t locale, bool locale_is_c);
 
35
static text *SB_do_like_escape(text *, text *);
 
36
 
 
37
static int MB_MatchText(char *t, int tlen, char *p, int plen,
 
38
                         pg_locale_t locale, bool locale_is_c);
 
39
static text *MB_do_like_escape(text *, text *);
 
40
 
 
41
static int UTF8_MatchText(char *t, int tlen, char *p, int plen,
 
42
                           pg_locale_t locale, bool locale_is_c);
 
43
 
 
44
static int SB_IMatchText(char *t, int tlen, char *p, int plen,
 
45
                          pg_locale_t locale, bool locale_is_c);
 
46
 
 
47
static int      GenericMatchText(char *s, int slen, char *p, int plen);
 
48
static int      Generic_Text_IC_like(text *str, text *pat, Oid collation);
 
49
 
 
50
/*--------------------
 
51
 * Support routine for MatchText. Compares given multibyte streams
 
52
 * as wide characters. If they match, returns 1 otherwise returns 0.
 
53
 *--------------------
 
54
 */
 
55
static inline int
 
56
wchareq(char *p1, char *p2)
 
57
{
 
58
        int                     p1_len;
 
59
 
 
60
        /* Optimization:  quickly compare the first byte. */
 
61
        if (*p1 != *p2)
 
62
                return 0;
 
63
 
 
64
        p1_len = pg_mblen(p1);
 
65
        if (pg_mblen(p2) != p1_len)
 
66
                return 0;
 
67
 
 
68
        /* They are the same length */
 
69
        while (p1_len--)
 
70
        {
 
71
                if (*p1++ != *p2++)
 
72
                        return 0;
 
73
        }
 
74
        return 1;
 
75
}
 
76
 
 
77
/*
 
78
 * Formerly we had a routine iwchareq() here that tried to do case-insensitive
 
79
 * comparison of multibyte characters.  It did not work at all, however,
 
80
 * because it relied on tolower() which has a single-byte API ... and
 
81
 * towlower() wouldn't be much better since we have no suitably cheap way
 
82
 * of getting a single character transformed to the system's wchar_t format.
 
83
 * So now, we just downcase the strings using lower() and apply regular LIKE
 
84
 * comparison.  This should be revisited when we install better locale support.
 
85
 */
 
86
 
 
87
/*
 
88
 * We do handle case-insensitive matching for single-byte encodings using
 
89
 * fold-on-the-fly processing, however.
 
90
 */
 
91
static char
 
92
SB_lower_char(unsigned char c, pg_locale_t locale, bool locale_is_c)
 
93
{
 
94
        if (locale_is_c)
 
95
                return pg_ascii_tolower(c);
 
96
#ifdef HAVE_LOCALE_T
 
97
        else if (locale)
 
98
                return tolower_l(c, locale);
 
99
#endif
 
100
        else
 
101
                return pg_tolower(c);
 
102
}
 
103
 
 
104
 
 
105
#define NextByte(p, plen)       ((p)++, (plen)--)
 
106
 
 
107
/* Set up to compile like_match.c for multibyte characters */
 
108
#define CHAREQ(p1, p2) wchareq((p1), (p2))
 
109
#define NextChar(p, plen) \
 
110
        do { int __l = pg_mblen(p); (p) +=__l; (plen) -=__l; } while (0)
 
111
#define CopyAdvChar(dst, src, srclen) \
 
112
        do { int __l = pg_mblen(src); \
 
113
                 (srclen) -= __l; \
 
114
                 while (__l-- > 0) \
 
115
                         *(dst)++ = *(src)++; \
 
116
           } while (0)
 
117
 
 
118
#define MatchText       MB_MatchText
 
119
#define do_like_escape  MB_do_like_escape
 
120
 
 
121
#include "like_match.c"
 
122
 
 
123
/* Set up to compile like_match.c for single-byte characters */
 
124
#define CHAREQ(p1, p2) (*(p1) == *(p2))
 
125
#define NextChar(p, plen) NextByte((p), (plen))
 
126
#define CopyAdvChar(dst, src, srclen) (*(dst)++ = *(src)++, (srclen)--)
 
127
 
 
128
#define MatchText       SB_MatchText
 
129
#define do_like_escape  SB_do_like_escape
 
130
 
 
131
#include "like_match.c"
 
132
 
 
133
/* setup to compile like_match.c for single byte case insensitive matches */
 
134
#define MATCH_LOWER(t) SB_lower_char((unsigned char) (t), locale, locale_is_c)
 
135
#define NextChar(p, plen) NextByte((p), (plen))
 
136
#define MatchText SB_IMatchText
 
137
 
 
138
#include "like_match.c"
 
139
 
 
140
/* setup to compile like_match.c for UTF8 encoding, using fast NextChar */
 
141
 
 
142
#define NextChar(p, plen) \
 
143
        do { (p)++; (plen)--; } while ((plen) > 0 && (*(p) & 0xC0) == 0x80 )
 
144
#define MatchText       UTF8_MatchText
 
145
 
 
146
#include "like_match.c"
 
147
 
 
148
/* Generic for all cases not requiring inline case-folding */
 
149
static inline int
 
150
GenericMatchText(char *s, int slen, char *p, int plen)
 
151
{
 
152
        if (pg_database_encoding_max_length() == 1)
 
153
                return SB_MatchText(s, slen, p, plen, 0, true);
 
154
        else if (GetDatabaseEncoding() == PG_UTF8)
 
155
                return UTF8_MatchText(s, slen, p, plen, 0, true);
 
156
        else
 
157
                return MB_MatchText(s, slen, p, plen, 0, true);
 
158
}
 
159
 
 
160
static inline int
 
161
Generic_Text_IC_like(text *str, text *pat, Oid collation)
 
162
{
 
163
        char       *s,
 
164
                           *p;
 
165
        int                     slen,
 
166
                                plen;
 
167
 
 
168
        /*
 
169
         * For efficiency reasons, in the single byte case we don't call lower()
 
170
         * on the pattern and text, but instead call SB_lower_char on each
 
171
         * character.  In the multi-byte case we don't have much choice :-(
 
172
         */
 
173
 
 
174
        if (pg_database_encoding_max_length() > 1)
 
175
        {
 
176
                /* lower's result is never packed, so OK to use old macros here */
 
177
                pat = DatumGetTextP(DirectFunctionCall1Coll(lower, collation,
 
178
                                                                                                        PointerGetDatum(pat)));
 
179
                p = VARDATA(pat);
 
180
                plen = (VARSIZE(pat) - VARHDRSZ);
 
181
                str = DatumGetTextP(DirectFunctionCall1Coll(lower, collation,
 
182
                                                                                                        PointerGetDatum(str)));
 
183
                s = VARDATA(str);
 
184
                slen = (VARSIZE(str) - VARHDRSZ);
 
185
                if (GetDatabaseEncoding() == PG_UTF8)
 
186
                        return UTF8_MatchText(s, slen, p, plen, 0, true);
 
187
                else
 
188
                        return MB_MatchText(s, slen, p, plen, 0, true);
 
189
        }
 
190
        else
 
191
        {
 
192
                /*
 
193
                 * Here we need to prepare locale information for SB_lower_char. This
 
194
                 * should match the methods used in str_tolower().
 
195
                 */
 
196
                pg_locale_t locale = 0;
 
197
                bool            locale_is_c = false;
 
198
 
 
199
                if (lc_ctype_is_c(collation))
 
200
                        locale_is_c = true;
 
201
                else if (collation != DEFAULT_COLLATION_OID)
 
202
                {
 
203
                        if (!OidIsValid(collation))
 
204
                        {
 
205
                                /*
 
206
                                 * This typically means that the parser could not resolve a
 
207
                                 * conflict of implicit collations, so report it that way.
 
208
                                 */
 
209
                                ereport(ERROR,
 
210
                                                (errcode(ERRCODE_INDETERMINATE_COLLATION),
 
211
                                                 errmsg("could not determine which collation to use for ILIKE"),
 
212
                                                 errhint("Use the COLLATE clause to set the collation explicitly.")));
 
213
                        }
 
214
                        locale = pg_newlocale_from_collation(collation);
 
215
                }
 
216
 
 
217
                p = VARDATA_ANY(pat);
 
218
                plen = VARSIZE_ANY_EXHDR(pat);
 
219
                s = VARDATA_ANY(str);
 
220
                slen = VARSIZE_ANY_EXHDR(str);
 
221
                return SB_IMatchText(s, slen, p, plen, locale, locale_is_c);
 
222
        }
 
223
}
 
224
 
 
225
/*
 
226
 *      interface routines called by the function manager
 
227
 */
 
228
 
 
229
Datum
 
230
namelike(PG_FUNCTION_ARGS)
 
231
{
 
232
        Name            str = PG_GETARG_NAME(0);
 
233
        text       *pat = PG_GETARG_TEXT_PP(1);
 
234
        bool            result;
 
235
        char       *s,
 
236
                           *p;
 
237
        int                     slen,
 
238
                                plen;
 
239
 
 
240
        s = NameStr(*str);
 
241
        slen = strlen(s);
 
242
        p = VARDATA_ANY(pat);
 
243
        plen = VARSIZE_ANY_EXHDR(pat);
 
244
 
 
245
        result = (GenericMatchText(s, slen, p, plen) == LIKE_TRUE);
 
246
 
 
247
        PG_RETURN_BOOL(result);
 
248
}
 
249
 
 
250
Datum
 
251
namenlike(PG_FUNCTION_ARGS)
 
252
{
 
253
        Name            str = PG_GETARG_NAME(0);
 
254
        text       *pat = PG_GETARG_TEXT_PP(1);
 
255
        bool            result;
 
256
        char       *s,
 
257
                           *p;
 
258
        int                     slen,
 
259
                                plen;
 
260
 
 
261
        s = NameStr(*str);
 
262
        slen = strlen(s);
 
263
        p = VARDATA_ANY(pat);
 
264
        plen = VARSIZE_ANY_EXHDR(pat);
 
265
 
 
266
        result = (GenericMatchText(s, slen, p, plen) != LIKE_TRUE);
 
267
 
 
268
        PG_RETURN_BOOL(result);
 
269
}
 
270
 
 
271
Datum
 
272
textlike(PG_FUNCTION_ARGS)
 
273
{
 
274
        text       *str = PG_GETARG_TEXT_PP(0);
 
275
        text       *pat = PG_GETARG_TEXT_PP(1);
 
276
        bool            result;
 
277
        char       *s,
 
278
                           *p;
 
279
        int                     slen,
 
280
                                plen;
 
281
 
 
282
        s = VARDATA_ANY(str);
 
283
        slen = VARSIZE_ANY_EXHDR(str);
 
284
        p = VARDATA_ANY(pat);
 
285
        plen = VARSIZE_ANY_EXHDR(pat);
 
286
 
 
287
        result = (GenericMatchText(s, slen, p, plen) == LIKE_TRUE);
 
288
 
 
289
        PG_RETURN_BOOL(result);
 
290
}
 
291
 
 
292
Datum
 
293
textnlike(PG_FUNCTION_ARGS)
 
294
{
 
295
        text       *str = PG_GETARG_TEXT_PP(0);
 
296
        text       *pat = PG_GETARG_TEXT_PP(1);
 
297
        bool            result;
 
298
        char       *s,
 
299
                           *p;
 
300
        int                     slen,
 
301
                                plen;
 
302
 
 
303
        s = VARDATA_ANY(str);
 
304
        slen = VARSIZE_ANY_EXHDR(str);
 
305
        p = VARDATA_ANY(pat);
 
306
        plen = VARSIZE_ANY_EXHDR(pat);
 
307
 
 
308
        result = (GenericMatchText(s, slen, p, plen) != LIKE_TRUE);
 
309
 
 
310
        PG_RETURN_BOOL(result);
 
311
}
 
312
 
 
313
Datum
 
314
bytealike(PG_FUNCTION_ARGS)
 
315
{
 
316
        bytea      *str = PG_GETARG_BYTEA_PP(0);
 
317
        bytea      *pat = PG_GETARG_BYTEA_PP(1);
 
318
        bool            result;
 
319
        char       *s,
 
320
                           *p;
 
321
        int                     slen,
 
322
                                plen;
 
323
 
 
324
        s = VARDATA_ANY(str);
 
325
        slen = VARSIZE_ANY_EXHDR(str);
 
326
        p = VARDATA_ANY(pat);
 
327
        plen = VARSIZE_ANY_EXHDR(pat);
 
328
 
 
329
        result = (SB_MatchText(s, slen, p, plen, 0, true) == LIKE_TRUE);
 
330
 
 
331
        PG_RETURN_BOOL(result);
 
332
}
 
333
 
 
334
Datum
 
335
byteanlike(PG_FUNCTION_ARGS)
 
336
{
 
337
        bytea      *str = PG_GETARG_BYTEA_PP(0);
 
338
        bytea      *pat = PG_GETARG_BYTEA_PP(1);
 
339
        bool            result;
 
340
        char       *s,
 
341
                           *p;
 
342
        int                     slen,
 
343
                                plen;
 
344
 
 
345
        s = VARDATA_ANY(str);
 
346
        slen = VARSIZE_ANY_EXHDR(str);
 
347
        p = VARDATA_ANY(pat);
 
348
        plen = VARSIZE_ANY_EXHDR(pat);
 
349
 
 
350
        result = (SB_MatchText(s, slen, p, plen, 0, true) != LIKE_TRUE);
 
351
 
 
352
        PG_RETURN_BOOL(result);
 
353
}
 
354
 
 
355
/*
 
356
 * Case-insensitive versions
 
357
 */
 
358
 
 
359
Datum
 
360
nameiclike(PG_FUNCTION_ARGS)
 
361
{
 
362
        Name            str = PG_GETARG_NAME(0);
 
363
        text       *pat = PG_GETARG_TEXT_PP(1);
 
364
        bool            result;
 
365
        text       *strtext;
 
366
 
 
367
        strtext = DatumGetTextP(DirectFunctionCall1(name_text,
 
368
                                                                                                NameGetDatum(str)));
 
369
        result = (Generic_Text_IC_like(strtext, pat, PG_GET_COLLATION()) == LIKE_TRUE);
 
370
 
 
371
        PG_RETURN_BOOL(result);
 
372
}
 
373
 
 
374
Datum
 
375
nameicnlike(PG_FUNCTION_ARGS)
 
376
{
 
377
        Name            str = PG_GETARG_NAME(0);
 
378
        text       *pat = PG_GETARG_TEXT_PP(1);
 
379
        bool            result;
 
380
        text       *strtext;
 
381
 
 
382
        strtext = DatumGetTextP(DirectFunctionCall1(name_text,
 
383
                                                                                                NameGetDatum(str)));
 
384
        result = (Generic_Text_IC_like(strtext, pat, PG_GET_COLLATION()) != LIKE_TRUE);
 
385
 
 
386
        PG_RETURN_BOOL(result);
 
387
}
 
388
 
 
389
Datum
 
390
texticlike(PG_FUNCTION_ARGS)
 
391
{
 
392
        text       *str = PG_GETARG_TEXT_PP(0);
 
393
        text       *pat = PG_GETARG_TEXT_PP(1);
 
394
        bool            result;
 
395
 
 
396
        result = (Generic_Text_IC_like(str, pat, PG_GET_COLLATION()) == LIKE_TRUE);
 
397
 
 
398
        PG_RETURN_BOOL(result);
 
399
}
 
400
 
 
401
Datum
 
402
texticnlike(PG_FUNCTION_ARGS)
 
403
{
 
404
        text       *str = PG_GETARG_TEXT_PP(0);
 
405
        text       *pat = PG_GETARG_TEXT_PP(1);
 
406
        bool            result;
 
407
 
 
408
        result = (Generic_Text_IC_like(str, pat, PG_GET_COLLATION()) != LIKE_TRUE);
 
409
 
 
410
        PG_RETURN_BOOL(result);
 
411
}
 
412
 
 
413
/*
 
414
 * like_escape() --- given a pattern and an ESCAPE string,
 
415
 * convert the pattern to use Postgres' standard backslash escape convention.
 
416
 */
 
417
Datum
 
418
like_escape(PG_FUNCTION_ARGS)
 
419
{
 
420
        text       *pat = PG_GETARG_TEXT_PP(0);
 
421
        text       *esc = PG_GETARG_TEXT_PP(1);
 
422
        text       *result;
 
423
 
 
424
        if (pg_database_encoding_max_length() == 1)
 
425
                result = SB_do_like_escape(pat, esc);
 
426
        else
 
427
                result = MB_do_like_escape(pat, esc);
 
428
 
 
429
        PG_RETURN_TEXT_P(result);
 
430
}
 
431
 
 
432
/*
 
433
 * like_escape_bytea() --- given a pattern and an ESCAPE string,
 
434
 * convert the pattern to use Postgres' standard backslash escape convention.
 
435
 */
 
436
Datum
 
437
like_escape_bytea(PG_FUNCTION_ARGS)
 
438
{
 
439
        bytea      *pat = PG_GETARG_BYTEA_PP(0);
 
440
        bytea      *esc = PG_GETARG_BYTEA_PP(1);
 
441
        bytea      *result = SB_do_like_escape((text *) pat, (text *) esc);
 
442
 
 
443
        PG_RETURN_BYTEA_P((bytea *) result);
 
444
}