Coverage Report

Created: 2026-11-12 07:20

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