LCOV - code coverage report
Current view: top level - src/backend/utils/adt - like.c (source / functions) Coverage Total Hit
Test: Code coverage Lines: 93.2 % 177 165
Test Date: 2026-01-26 10:56:24 Functions: 100.0 % 15 15
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 47.4 % 38 18

             Branch data     Line data    Source code
       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                 :             : #define LIKE_TRUE                                               1
      31                 :             : #define LIKE_FALSE                                              0
      32                 :             : #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                 :         154 : wchareq(const char *p1, const char *p2)
      59                 :             : {
      60                 :         154 :         int                     p1_len;
      61                 :             : 
      62                 :             :         /* Optimization:  quickly compare the first byte. */
      63         [ +  + ]:         154 :         if (*p1 != *p2)
      64                 :         116 :                 return 0;
      65                 :             : 
      66                 :          38 :         p1_len = pg_mblen(p1);
      67         [ -  + ]:          38 :         if (pg_mblen(p2) != p1_len)
      68                 :           0 :                 return 0;
      69                 :             : 
      70                 :             :         /* They are the same length */
      71         [ +  + ]:          76 :         while (p1_len--)
      72                 :             :         {
      73         [ -  + ]:          38 :                 if (*p1++ != *p2++)
      74                 :           0 :                         return 0;
      75                 :             :         }
      76                 :          38 :         return 1;
      77                 :         154 : }
      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                 :             : #define NextByte(p, plen)       ((p)++, (plen)--)
      94                 :             : 
      95                 :             : /* Set up to compile like_match.c for multibyte characters */
      96                 :             : #define CHAREQ(p1, p2) wchareq((p1), (p2))
      97                 :             : #define NextChar(p, plen) \
      98                 :             :         do { int __l = pg_mblen(p); (p) +=__l; (plen) -=__l; } while (0)
      99                 :             : #define CopyAdvChar(dst, src, srclen) \
     100                 :             :         do { int __l = pg_mblen(src); \
     101                 :             :                  (srclen) -= __l; \
     102                 :             :                  while (__l-- > 0) \
     103                 :             :                          *(dst)++ = *(src)++; \
     104                 :             :            } while (0)
     105                 :             : 
     106                 :             : #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                 :             : #define CHAREQ(p1, p2) (*(p1) == *(p2))
     113                 :             : #define NextChar(p, plen) NextByte((p), (plen))
     114                 :             : #define CopyAdvChar(dst, src, srclen) (*(dst)++ = *(src)++, (srclen)--)
     115                 :             : 
     116                 :             : #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                 :             : #define NextChar(p, plen) NextByte((p), (plen))
     124                 :             : #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                 :             :         do { (p)++; (plen)--; } while ((plen) > 0 && (*(p) & 0xC0) == 0x80 )
     132                 :             : #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                 :      540513 : GenericMatchText(const char *s, int slen, const char *p, int plen, Oid collation)
     139                 :             : {
     140                 :      540513 :         pg_locale_t locale;
     141                 :             : 
     142         [ +  - ]:      540513 :         if (!OidIsValid(collation))
     143                 :             :         {
     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                 :             :                                 (errcode(ERRCODE_INDETERMINATE_COLLATION),
     150                 :             :                                  errmsg("could not determine which collation to use for LIKE"),
     151                 :             :                                  errhint("Use the COLLATE clause to set the collation explicitly.")));
     152                 :           0 :         }
     153                 :             : 
     154                 :      540513 :         locale = pg_newlocale_from_collation(collation);
     155                 :             : 
     156         [ -  + ]:      540513 :         if (pg_database_encoding_max_length() == 1)
     157                 :           0 :                 return SB_MatchText(s, slen, p, plen, locale);
     158         [ +  - ]:      540513 :         else if (GetDatabaseEncoding() == PG_UTF8)
     159                 :      540513 :                 return UTF8_MatchText(s, slen, p, plen, locale);
     160                 :             :         else
     161                 :           0 :                 return MB_MatchText(s, slen, p, plen, locale);
     162                 :      540513 : }
     163                 :             : 
     164                 :             : static inline int
     165                 :        2488 : Generic_Text_IC_like(text *str, text *pat, Oid collation)
     166                 :             : {
     167                 :        2488 :         char       *s,
     168                 :             :                            *p;
     169                 :        2488 :         int                     slen,
     170                 :             :                                 plen;
     171                 :        2488 :         pg_locale_t locale;
     172                 :             : 
     173         [ +  - ]:        2488 :         if (!OidIsValid(collation))
     174                 :             :         {
     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                 :             :                                 (errcode(ERRCODE_INDETERMINATE_COLLATION),
     181                 :             :                                  errmsg("could not determine which collation to use for ILIKE"),
     182                 :             :                                  errhint("Use the COLLATE clause to set the collation explicitly.")));
     183                 :           0 :         }
     184                 :             : 
     185                 :        2488 :         locale = pg_newlocale_from_collation(collation);
     186                 :             : 
     187         [ +  + ]:        2488 :         if (!locale->deterministic)
     188   [ +  -  +  - ]:           2 :                 ereport(ERROR,
     189                 :             :                                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     190                 :             :                                  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.
     195                 :             :          *
     196                 :             :          * XXX: use casefolding instead?
     197                 :             :          */
     198                 :             : 
     199         [ +  + ]:        2486 :         if (locale->ctype_is_c)
     200                 :             :         {
     201                 :        2446 :                 p = VARDATA_ANY(pat);
     202                 :        2446 :                 plen = VARSIZE_ANY_EXHDR(pat);
     203                 :        2446 :                 s = VARDATA_ANY(str);
     204                 :        2446 :                 slen = VARSIZE_ANY_EXHDR(str);
     205                 :        2446 :                 return C_IMatchText(s, slen, p, plen, locale);
     206                 :             :         }
     207                 :             :         else
     208                 :             :         {
     209                 :          40 :                 pat = DatumGetTextPP(DirectFunctionCall1Coll(lower, collation,
     210                 :             :                                                                                                          PointerGetDatum(pat)));
     211                 :          40 :                 p = VARDATA_ANY(pat);
     212                 :          40 :                 plen = VARSIZE_ANY_EXHDR(pat);
     213                 :          40 :                 str = DatumGetTextPP(DirectFunctionCall1Coll(lower, collation,
     214                 :             :                                                                                                          PointerGetDatum(str)));
     215                 :          40 :                 s = VARDATA_ANY(str);
     216                 :          40 :                 slen = VARSIZE_ANY_EXHDR(str);
     217                 :             : 
     218         [ +  - ]:          40 :                 if (GetDatabaseEncoding() == PG_UTF8)
     219                 :          40 :                         return UTF8_MatchText(s, slen, p, plen, 0);
     220         [ #  # ]:           0 :                 else if (pg_database_encoding_max_length() > 1)
     221                 :           0 :                         return MB_MatchText(s, slen, p, plen, 0);
     222                 :             :                 else
     223                 :           0 :                         return SB_MatchText(s, slen, p, plen, 0);
     224                 :             :         }
     225                 :        2486 : }
     226                 :             : 
     227                 :             : /*
     228                 :             :  *      interface routines called by the function manager
     229                 :             :  */
     230                 :             : 
     231                 :             : Datum
     232                 :       24687 : namelike(PG_FUNCTION_ARGS)
     233                 :             : {
     234                 :       24687 :         Name            str = PG_GETARG_NAME(0);
     235                 :       24687 :         text       *pat = PG_GETARG_TEXT_PP(1);
     236                 :       24687 :         bool            result;
     237                 :       24687 :         char       *s,
     238                 :             :                            *p;
     239                 :       24687 :         int                     slen,
     240                 :             :                                 plen;
     241                 :             : 
     242                 :       24687 :         s = NameStr(*str);
     243                 :       24687 :         slen = strlen(s);
     244                 :       24687 :         p = VARDATA_ANY(pat);
     245                 :       24687 :         plen = VARSIZE_ANY_EXHDR(pat);
     246                 :             : 
     247                 :       24687 :         result = (GenericMatchText(s, slen, p, plen, PG_GET_COLLATION()) == LIKE_TRUE);
     248                 :             : 
     249                 :       49374 :         PG_RETURN_BOOL(result);
     250                 :       24687 : }
     251                 :             : 
     252                 :             : Datum
     253                 :         902 : namenlike(PG_FUNCTION_ARGS)
     254                 :             : {
     255                 :         902 :         Name            str = PG_GETARG_NAME(0);
     256                 :         902 :         text       *pat = PG_GETARG_TEXT_PP(1);
     257                 :         902 :         bool            result;
     258                 :         902 :         char       *s,
     259                 :             :                            *p;
     260                 :         902 :         int                     slen,
     261                 :             :                                 plen;
     262                 :             : 
     263                 :         902 :         s = NameStr(*str);
     264                 :         902 :         slen = strlen(s);
     265                 :         902 :         p = VARDATA_ANY(pat);
     266                 :         902 :         plen = VARSIZE_ANY_EXHDR(pat);
     267                 :             : 
     268                 :         902 :         result = (GenericMatchText(s, slen, p, plen, PG_GET_COLLATION()) != LIKE_TRUE);
     269                 :             : 
     270                 :        1804 :         PG_RETURN_BOOL(result);
     271                 :         902 : }
     272                 :             : 
     273                 :             : Datum
     274                 :      464198 : textlike(PG_FUNCTION_ARGS)
     275                 :             : {
     276                 :      464198 :         text       *str = PG_GETARG_TEXT_PP(0);
     277                 :      464198 :         text       *pat = PG_GETARG_TEXT_PP(1);
     278                 :      464198 :         bool            result;
     279                 :      464198 :         char       *s,
     280                 :             :                            *p;
     281                 :      464198 :         int                     slen,
     282                 :             :                                 plen;
     283                 :             : 
     284                 :      464198 :         s = VARDATA_ANY(str);
     285                 :      464198 :         slen = VARSIZE_ANY_EXHDR(str);
     286                 :      464198 :         p = VARDATA_ANY(pat);
     287                 :      464198 :         plen = VARSIZE_ANY_EXHDR(pat);
     288                 :             : 
     289                 :      464198 :         result = (GenericMatchText(s, slen, p, plen, PG_GET_COLLATION()) == LIKE_TRUE);
     290                 :             : 
     291                 :      928396 :         PG_RETURN_BOOL(result);
     292                 :      464198 : }
     293                 :             : 
     294                 :             : Datum
     295                 :       50726 : textnlike(PG_FUNCTION_ARGS)
     296                 :             : {
     297                 :       50726 :         text       *str = PG_GETARG_TEXT_PP(0);
     298                 :       50726 :         text       *pat = PG_GETARG_TEXT_PP(1);
     299                 :       50726 :         bool            result;
     300                 :       50726 :         char       *s,
     301                 :             :                            *p;
     302                 :       50726 :         int                     slen,
     303                 :             :                                 plen;
     304                 :             : 
     305                 :       50726 :         s = VARDATA_ANY(str);
     306                 :       50726 :         slen = VARSIZE_ANY_EXHDR(str);
     307                 :       50726 :         p = VARDATA_ANY(pat);
     308                 :       50726 :         plen = VARSIZE_ANY_EXHDR(pat);
     309                 :             : 
     310                 :       50726 :         result = (GenericMatchText(s, slen, p, plen, PG_GET_COLLATION()) != LIKE_TRUE);
     311                 :             : 
     312                 :      101452 :         PG_RETURN_BOOL(result);
     313                 :       50726 : }
     314                 :             : 
     315                 :             : Datum
     316                 :           2 : bytealike(PG_FUNCTION_ARGS)
     317                 :             : {
     318                 :           2 :         bytea      *str = PG_GETARG_BYTEA_PP(0);
     319                 :           2 :         bytea      *pat = PG_GETARG_BYTEA_PP(1);
     320                 :           2 :         bool            result;
     321                 :           2 :         char       *s,
     322                 :             :                            *p;
     323                 :           2 :         int                     slen,
     324                 :             :                                 plen;
     325                 :             : 
     326                 :           2 :         s = VARDATA_ANY(str);
     327                 :           2 :         slen = VARSIZE_ANY_EXHDR(str);
     328                 :           2 :         p = VARDATA_ANY(pat);
     329                 :           2 :         plen = VARSIZE_ANY_EXHDR(pat);
     330                 :             : 
     331                 :           2 :         result = (SB_MatchText(s, slen, p, plen, 0) == LIKE_TRUE);
     332                 :             : 
     333                 :           4 :         PG_RETURN_BOOL(result);
     334                 :           2 : }
     335                 :             : 
     336                 :             : Datum
     337                 :           2 : byteanlike(PG_FUNCTION_ARGS)
     338                 :             : {
     339                 :           2 :         bytea      *str = PG_GETARG_BYTEA_PP(0);
     340                 :           2 :         bytea      *pat = PG_GETARG_BYTEA_PP(1);
     341                 :           2 :         bool            result;
     342                 :           2 :         char       *s,
     343                 :             :                            *p;
     344                 :           2 :         int                     slen,
     345                 :             :                                 plen;
     346                 :             : 
     347                 :           2 :         s = VARDATA_ANY(str);
     348                 :           2 :         slen = VARSIZE_ANY_EXHDR(str);
     349                 :           2 :         p = VARDATA_ANY(pat);
     350                 :           2 :         plen = VARSIZE_ANY_EXHDR(pat);
     351                 :             : 
     352                 :           2 :         result = (SB_MatchText(s, slen, p, plen, 0) != LIKE_TRUE);
     353                 :             : 
     354                 :           4 :         PG_RETURN_BOOL(result);
     355                 :           2 : }
     356                 :             : 
     357                 :             : /*
     358                 :             :  * Case-insensitive versions
     359                 :             :  */
     360                 :             : 
     361                 :             : Datum
     362                 :        2445 : nameiclike(PG_FUNCTION_ARGS)
     363                 :             : {
     364                 :        2445 :         Name            str = PG_GETARG_NAME(0);
     365                 :        2445 :         text       *pat = PG_GETARG_TEXT_PP(1);
     366                 :        2445 :         bool            result;
     367                 :        2445 :         text       *strtext;
     368                 :             : 
     369                 :        2445 :         strtext = DatumGetTextPP(DirectFunctionCall1(name_text,
     370                 :             :                                                                                                  NameGetDatum(str)));
     371                 :        2445 :         result = (Generic_Text_IC_like(strtext, pat, PG_GET_COLLATION()) == LIKE_TRUE);
     372                 :             : 
     373                 :        4890 :         PG_RETURN_BOOL(result);
     374                 :        2445 : }
     375                 :             : 
     376                 :             : Datum
     377                 :           1 : nameicnlike(PG_FUNCTION_ARGS)
     378                 :             : {
     379                 :           1 :         Name            str = PG_GETARG_NAME(0);
     380                 :           1 :         text       *pat = PG_GETARG_TEXT_PP(1);
     381                 :           1 :         bool            result;
     382                 :           1 :         text       *strtext;
     383                 :             : 
     384                 :           1 :         strtext = DatumGetTextPP(DirectFunctionCall1(name_text,
     385                 :             :                                                                                                  NameGetDatum(str)));
     386                 :           1 :         result = (Generic_Text_IC_like(strtext, pat, PG_GET_COLLATION()) != LIKE_TRUE);
     387                 :             : 
     388                 :           2 :         PG_RETURN_BOOL(result);
     389                 :           1 : }
     390                 :             : 
     391                 :             : Datum
     392                 :          38 : texticlike(PG_FUNCTION_ARGS)
     393                 :             : {
     394                 :          38 :         text       *str = PG_GETARG_TEXT_PP(0);
     395                 :          38 :         text       *pat = PG_GETARG_TEXT_PP(1);
     396                 :          38 :         bool            result;
     397                 :             : 
     398                 :          38 :         result = (Generic_Text_IC_like(str, pat, PG_GET_COLLATION()) == LIKE_TRUE);
     399                 :             : 
     400                 :          76 :         PG_RETURN_BOOL(result);
     401                 :          38 : }
     402                 :             : 
     403                 :             : Datum
     404                 :           4 : texticnlike(PG_FUNCTION_ARGS)
     405                 :             : {
     406                 :           4 :         text       *str = PG_GETARG_TEXT_PP(0);
     407                 :           4 :         text       *pat = PG_GETARG_TEXT_PP(1);
     408                 :           4 :         bool            result;
     409                 :             : 
     410                 :           4 :         result = (Generic_Text_IC_like(str, pat, PG_GET_COLLATION()) != LIKE_TRUE);
     411                 :             : 
     412                 :           8 :         PG_RETURN_BOOL(result);
     413                 :           4 : }
     414                 :             : 
     415                 :             : /*
     416                 :             :  * like_escape() --- given a pattern and an ESCAPE string,
     417                 :             :  * convert the pattern to use Postgres' standard backslash escape convention.
     418                 :             :  */
     419                 :             : Datum
     420                 :          30 : like_escape(PG_FUNCTION_ARGS)
     421                 :             : {
     422                 :          30 :         text       *pat = PG_GETARG_TEXT_PP(0);
     423                 :          30 :         text       *esc = PG_GETARG_TEXT_PP(1);
     424                 :          30 :         text       *result;
     425                 :             : 
     426         [ -  + ]:          30 :         if (pg_database_encoding_max_length() == 1)
     427                 :           0 :                 result = SB_do_like_escape(pat, esc);
     428                 :             :         else
     429                 :          30 :                 result = MB_do_like_escape(pat, esc);
     430                 :             : 
     431                 :          60 :         PG_RETURN_TEXT_P(result);
     432                 :          30 : }
     433                 :             : 
     434                 :             : /*
     435                 :             :  * like_escape_bytea() --- given a pattern and an ESCAPE string,
     436                 :             :  * convert the pattern to use Postgres' standard backslash escape convention.
     437                 :             :  */
     438                 :             : Datum
     439                 :           2 : like_escape_bytea(PG_FUNCTION_ARGS)
     440                 :             : {
     441                 :           2 :         bytea      *pat = PG_GETARG_BYTEA_PP(0);
     442                 :           2 :         bytea      *esc = PG_GETARG_BYTEA_PP(1);
     443                 :           2 :         bytea      *result = SB_do_like_escape((text *) pat, (text *) esc);
     444                 :             : 
     445                 :           4 :         PG_RETURN_BYTEA_P((bytea *) result);
     446                 :           2 : }
        

Generated by: LCOV version 2.3.2-1