LCOV - code coverage report
Current view: top level - src/backend/utils/adt - regexp.c (source / functions) Hit Total Coverage
Test: PostgreSQL 17devel Lines: 583 644 90.5 %
Date: 2023-12-11 15:11:28 Functions: 50 51 98.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*-------------------------------------------------------------------------
       2             :  *
       3             :  * regexp.c
       4             :  *    Postgres' interface to the regular expression package.
       5             :  *
       6             :  * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group
       7             :  * Portions Copyright (c) 1994, Regents of the University of California
       8             :  *
       9             :  *
      10             :  * IDENTIFICATION
      11             :  *    src/backend/utils/adt/regexp.c
      12             :  *
      13             :  *      Alistair Crooks added the code for the regex caching
      14             :  *      agc - cached the regular expressions used - there's a good chance
      15             :  *      that we'll get a hit, so this saves a compile step for every
      16             :  *      attempted match. I haven't actually measured the speed improvement,
      17             :  *      but it `looks' a lot quicker visually when watching regression
      18             :  *      test output.
      19             :  *
      20             :  *      agc - incorporated Keith Bostic's Berkeley regex code into
      21             :  *      the tree for all ports. To distinguish this regex code from any that
      22             :  *      is existent on a platform, I've prepended the string "pg_" to
      23             :  *      the functions regcomp, regerror, regexec and regfree.
      24             :  *      Fixed a bug that was originally a typo by me, where `i' was used
      25             :  *      instead of `oldest' when compiling regular expressions - benign
      26             :  *      results mostly, although occasionally it bit you...
      27             :  *
      28             :  *-------------------------------------------------------------------------
      29             :  */
      30             : #include "postgres.h"
      31             : 
      32             : #include "catalog/pg_type.h"
      33             : #include "funcapi.h"
      34             : #include "miscadmin.h"
      35             : #include "regex/regex.h"
      36             : #include "utils/array.h"
      37             : #include "utils/builtins.h"
      38             : #include "utils/memutils.h"
      39             : #include "utils/varlena.h"
      40             : 
      41             : #define PG_GETARG_TEXT_PP_IF_EXISTS(_n) \
      42             :     (PG_NARGS() > (_n) ? PG_GETARG_TEXT_PP(_n) : NULL)
      43             : 
      44             : 
      45             : /* all the options of interest for regex functions */
      46             : typedef struct pg_re_flags
      47             : {
      48             :     int         cflags;         /* compile flags for Spencer's regex code */
      49             :     bool        glob;           /* do it globally (for each occurrence) */
      50             : } pg_re_flags;
      51             : 
      52             : /* cross-call state for regexp_match and regexp_split functions */
      53             : typedef struct regexp_matches_ctx
      54             : {
      55             :     text       *orig_str;       /* data string in original TEXT form */
      56             :     int         nmatches;       /* number of places where pattern matched */
      57             :     int         npatterns;      /* number of capturing subpatterns */
      58             :     /* We store start char index and end+1 char index for each match */
      59             :     /* so the number of entries in match_locs is nmatches * npatterns * 2 */
      60             :     int        *match_locs;     /* 0-based character indexes */
      61             :     int         next_match;     /* 0-based index of next match to process */
      62             :     /* workspace for build_regexp_match_result() */
      63             :     Datum      *elems;          /* has npatterns elements */
      64             :     bool       *nulls;          /* has npatterns elements */
      65             :     pg_wchar   *wide_str;       /* wide-char version of original string */
      66             :     char       *conv_buf;       /* conversion buffer, if needed */
      67             :     int         conv_bufsiz;    /* size thereof */
      68             : } regexp_matches_ctx;
      69             : 
      70             : /*
      71             :  * We cache precompiled regular expressions using a "self organizing list"
      72             :  * structure, in which recently-used items tend to be near the front.
      73             :  * Whenever we use an entry, it's moved up to the front of the list.
      74             :  * Over time, an item's average position corresponds to its frequency of use.
      75             :  *
      76             :  * When we first create an entry, it's inserted at the front of
      77             :  * the array, dropping the entry at the end of the array if necessary to
      78             :  * make room.  (This might seem to be weighting the new entry too heavily,
      79             :  * but if we insert new entries further back, we'll be unable to adjust to
      80             :  * a sudden shift in the query mix where we are presented with MAX_CACHED_RES
      81             :  * never-before-seen items used circularly.  We ought to be able to handle
      82             :  * that case, so we have to insert at the front.)
      83             :  *
      84             :  * Knuth mentions a variant strategy in which a used item is moved up just
      85             :  * one place in the list.  Although he says this uses fewer comparisons on
      86             :  * average, it seems not to adapt very well to the situation where you have
      87             :  * both some reusable patterns and a steady stream of non-reusable patterns.
      88             :  * A reusable pattern that isn't used at least as often as non-reusable
      89             :  * patterns are seen will "fail to keep up" and will drop off the end of the
      90             :  * cache.  With move-to-front, a reusable pattern is guaranteed to stay in
      91             :  * the cache as long as it's used at least once in every MAX_CACHED_RES uses.
      92             :  */
      93             : 
      94             : /* this is the maximum number of cached regular expressions */
      95             : #ifndef MAX_CACHED_RES
      96             : #define MAX_CACHED_RES  32
      97             : #endif
      98             : 
      99             : /* A parent memory context for regular expressions. */
     100             : static MemoryContext RegexpCacheMemoryContext;
     101             : 
     102             : /* this structure describes one cached regular expression */
     103             : typedef struct cached_re_str
     104             : {
     105             :     MemoryContext cre_context;  /* memory context for this regexp */
     106             :     char       *cre_pat;        /* original RE (not null terminated!) */
     107             :     int         cre_pat_len;    /* length of original RE, in bytes */
     108             :     int         cre_flags;      /* compile flags: extended,icase etc */
     109             :     Oid         cre_collation;  /* collation to use */
     110             :     regex_t     cre_re;         /* the compiled regular expression */
     111             : } cached_re_str;
     112             : 
     113             : static int  num_res = 0;        /* # of cached re's */
     114             : static cached_re_str re_array[MAX_CACHED_RES];  /* cached re's */
     115             : 
     116             : 
     117             : /* Local functions */
     118             : static regexp_matches_ctx *setup_regexp_matches(text *orig_str, text *pattern,
     119             :                                                 pg_re_flags *re_flags,
     120             :                                                 int start_search,
     121             :                                                 Oid collation,
     122             :                                                 bool use_subpatterns,
     123             :                                                 bool ignore_degenerate,
     124             :                                                 bool fetching_unmatched);
     125             : static ArrayType *build_regexp_match_result(regexp_matches_ctx *matchctx);
     126             : static Datum build_regexp_split_result(regexp_matches_ctx *splitctx);
     127             : 
     128             : 
     129             : /*
     130             :  * RE_compile_and_cache - compile a RE, caching if possible
     131             :  *
     132             :  * Returns regex_t *
     133             :  *
     134             :  *  text_re --- the pattern, expressed as a TEXT object
     135             :  *  cflags --- compile options for the pattern
     136             :  *  collation --- collation to use for LC_CTYPE-dependent behavior
     137             :  *
     138             :  * Pattern is given in the database encoding.  We internally convert to
     139             :  * an array of pg_wchar, which is what Spencer's regex package wants.
     140             :  */
     141             : regex_t *
     142      944522 : RE_compile_and_cache(text *text_re, int cflags, Oid collation)
     143             : {
     144      944522 :     int         text_re_len = VARSIZE_ANY_EXHDR(text_re);
     145      944522 :     char       *text_re_val = VARDATA_ANY(text_re);
     146             :     pg_wchar   *pattern;
     147             :     int         pattern_len;
     148             :     int         i;
     149             :     int         regcomp_result;
     150             :     cached_re_str re_temp;
     151             :     char        errMsg[100];
     152             :     MemoryContext oldcontext;
     153             : 
     154             :     /*
     155             :      * Look for a match among previously compiled REs.  Since the data
     156             :      * structure is self-organizing with most-used entries at the front, our
     157             :      * search strategy can just be to scan from the front.
     158             :      */
     159     1476698 :     for (i = 0; i < num_res; i++)
     160             :     {
     161     1471706 :         if (re_array[i].cre_pat_len == text_re_len &&
     162      949430 :             re_array[i].cre_flags == cflags &&
     163      948250 :             re_array[i].cre_collation == collation &&
     164      948118 :             memcmp(re_array[i].cre_pat, text_re_val, text_re_len) == 0)
     165             :         {
     166             :             /*
     167             :              * Found a match; move it to front if not there already.
     168             :              */
     169      939530 :             if (i > 0)
     170             :             {
     171      450320 :                 re_temp = re_array[i];
     172      450320 :                 memmove(&re_array[1], &re_array[0], i * sizeof(cached_re_str));
     173      450320 :                 re_array[0] = re_temp;
     174             :             }
     175             : 
     176      939530 :             return &re_array[0].cre_re;
     177             :         }
     178             :     }
     179             : 
     180             :     /* Set up the cache memory on first go through. */
     181        4992 :     if (unlikely(RegexpCacheMemoryContext == NULL))
     182        1178 :         RegexpCacheMemoryContext =
     183        1178 :             AllocSetContextCreate(TopMemoryContext,
     184             :                                   "RegexpCacheMemoryContext",
     185             :                                   ALLOCSET_SMALL_SIZES);
     186             : 
     187             :     /*
     188             :      * Couldn't find it, so try to compile the new RE.  To avoid leaking
     189             :      * resources on failure, we build into the re_temp local.
     190             :      */
     191             : 
     192             :     /* Convert pattern string to wide characters */
     193        4992 :     pattern = (pg_wchar *) palloc((text_re_len + 1) * sizeof(pg_wchar));
     194        4992 :     pattern_len = pg_mb2wchar_with_len(text_re_val,
     195             :                                        pattern,
     196             :                                        text_re_len);
     197             : 
     198             :     /*
     199             :      * Make a memory context for this compiled regexp.  This is initially a
     200             :      * child of the current memory context, so it will be cleaned up
     201             :      * automatically if compilation is interrupted and throws an ERROR. We'll
     202             :      * re-parent it under the longer lived cache context if we make it to the
     203             :      * bottom of this function.
     204             :      */
     205        4992 :     re_temp.cre_context = AllocSetContextCreate(CurrentMemoryContext,
     206             :                                                 "RegexpMemoryContext",
     207             :                                                 ALLOCSET_SMALL_SIZES);
     208        4992 :     oldcontext = MemoryContextSwitchTo(re_temp.cre_context);
     209             : 
     210        4992 :     regcomp_result = pg_regcomp(&re_temp.cre_re,
     211             :                                 pattern,
     212             :                                 pattern_len,
     213             :                                 cflags,
     214             :                                 collation);
     215             : 
     216        4984 :     pfree(pattern);
     217             : 
     218        4984 :     if (regcomp_result != REG_OKAY)
     219             :     {
     220             :         /* re didn't compile (no need for pg_regfree, if so) */
     221          36 :         pg_regerror(regcomp_result, &re_temp.cre_re, errMsg, sizeof(errMsg));
     222          36 :         ereport(ERROR,
     223             :                 (errcode(ERRCODE_INVALID_REGULAR_EXPRESSION),
     224             :                  errmsg("invalid regular expression: %s", errMsg)));
     225             :     }
     226             : 
     227             :     /* Copy the pattern into the per-regexp memory context. */
     228        4948 :     re_temp.cre_pat = palloc(text_re_len + 1);
     229        4948 :     memcpy(re_temp.cre_pat, text_re_val, text_re_len);
     230             : 
     231             :     /*
     232             :      * NUL-terminate it only for the benefit of the identifier used for the
     233             :      * memory context, visible in the pg_backend_memory_contexts view.
     234             :      */
     235        4948 :     re_temp.cre_pat[text_re_len] = 0;
     236        4948 :     MemoryContextSetIdentifier(re_temp.cre_context, re_temp.cre_pat);
     237             : 
     238        4948 :     re_temp.cre_pat_len = text_re_len;
     239        4948 :     re_temp.cre_flags = cflags;
     240        4948 :     re_temp.cre_collation = collation;
     241             : 
     242             :     /*
     243             :      * Okay, we have a valid new item in re_temp; insert it into the storage
     244             :      * array.  Discard last entry if needed.
     245             :      */
     246        4948 :     if (num_res >= MAX_CACHED_RES)
     247             :     {
     248         774 :         --num_res;
     249             :         Assert(num_res < MAX_CACHED_RES);
     250             :         /* Delete the memory context holding the regexp and pattern. */
     251         774 :         MemoryContextDelete(re_array[num_res].cre_context);
     252             :     }
     253             : 
     254             :     /* Re-parent the memory context to our long-lived cache context. */
     255        4948 :     MemoryContextSetParent(re_temp.cre_context, RegexpCacheMemoryContext);
     256             : 
     257        4948 :     if (num_res > 0)
     258        3770 :         memmove(&re_array[1], &re_array[0], num_res * sizeof(cached_re_str));
     259             : 
     260        4948 :     re_array[0] = re_temp;
     261        4948 :     num_res++;
     262             : 
     263        4948 :     MemoryContextSwitchTo(oldcontext);
     264             : 
     265        4948 :     return &re_array[0].cre_re;
     266             : }
     267             : 
     268             : /*
     269             :  * RE_wchar_execute - execute a RE on pg_wchar data
     270             :  *
     271             :  * Returns true on match, false on no match
     272             :  *
     273             :  *  re --- the compiled pattern as returned by RE_compile_and_cache
     274             :  *  data --- the data to match against (need not be null-terminated)
     275             :  *  data_len --- the length of the data string
     276             :  *  start_search -- the offset in the data to start searching
     277             :  *  nmatch, pmatch  --- optional return area for match details
     278             :  *
     279             :  * Data is given as array of pg_wchar which is what Spencer's regex package
     280             :  * wants.
     281             :  */
     282             : static bool
     283     1812710 : RE_wchar_execute(regex_t *re, pg_wchar *data, int data_len,
     284             :                  int start_search, int nmatch, regmatch_t *pmatch)
     285             : {
     286             :     int         regexec_result;
     287             :     char        errMsg[100];
     288             : 
     289             :     /* Perform RE match and return result */
     290     1812710 :     regexec_result = pg_regexec(re,
     291             :                                 data,
     292             :                                 data_len,
     293             :                                 start_search,
     294             :                                 NULL,   /* no details */
     295             :                                 nmatch,
     296             :                                 pmatch,
     297             :                                 0);
     298             : 
     299     1812710 :     if (regexec_result != REG_OKAY && regexec_result != REG_NOMATCH)
     300             :     {
     301             :         /* re failed??? */
     302           0 :         pg_regerror(regexec_result, re, errMsg, sizeof(errMsg));
     303           0 :         ereport(ERROR,
     304             :                 (errcode(ERRCODE_INVALID_REGULAR_EXPRESSION),
     305             :                  errmsg("regular expression failed: %s", errMsg)));
     306             :     }
     307             : 
     308     1812710 :     return (regexec_result == REG_OKAY);
     309             : }
     310             : 
     311             : /*
     312             :  * RE_execute - execute a RE
     313             :  *
     314             :  * Returns true on match, false on no match
     315             :  *
     316             :  *  re --- the compiled pattern as returned by RE_compile_and_cache
     317             :  *  dat --- the data to match against (need not be null-terminated)
     318             :  *  dat_len --- the length of the data string
     319             :  *  nmatch, pmatch  --- optional return area for match details
     320             :  *
     321             :  * Data is given in the database encoding.  We internally
     322             :  * convert to array of pg_wchar which is what Spencer's regex package wants.
     323             :  */
     324             : static bool
     325      717536 : RE_execute(regex_t *re, char *dat, int dat_len,
     326             :            int nmatch, regmatch_t *pmatch)
     327             : {
     328             :     pg_wchar   *data;
     329             :     int         data_len;
     330             :     bool        match;
     331             : 
     332             :     /* Convert data string to wide characters */
     333      717536 :     data = (pg_wchar *) palloc((dat_len + 1) * sizeof(pg_wchar));
     334      717536 :     data_len = pg_mb2wchar_with_len(dat, data, dat_len);
     335             : 
     336             :     /* Perform RE match and return result */
     337      717536 :     match = RE_wchar_execute(re, data, data_len, 0, nmatch, pmatch);
     338             : 
     339      717536 :     pfree(data);
     340      717536 :     return match;
     341             : }
     342             : 
     343             : /*
     344             :  * RE_compile_and_execute - compile and execute a RE
     345             :  *
     346             :  * Returns true on match, false on no match
     347             :  *
     348             :  *  text_re --- the pattern, expressed as a TEXT object
     349             :  *  dat --- the data to match against (need not be null-terminated)
     350             :  *  dat_len --- the length of the data string
     351             :  *  cflags --- compile options for the pattern
     352             :  *  collation --- collation to use for LC_CTYPE-dependent behavior
     353             :  *  nmatch, pmatch  --- optional return area for match details
     354             :  *
     355             :  * Both pattern and data are given in the database encoding.  We internally
     356             :  * convert to array of pg_wchar which is what Spencer's regex package wants.
     357             :  */
     358             : bool
     359      715958 : RE_compile_and_execute(text *text_re, char *dat, int dat_len,
     360             :                        int cflags, Oid collation,
     361             :                        int nmatch, regmatch_t *pmatch)
     362             : {
     363             :     regex_t    *re;
     364             : 
     365             :     /* Use REG_NOSUB if caller does not want sub-match details */
     366      715958 :     if (nmatch < 2)
     367      715958 :         cflags |= REG_NOSUB;
     368             : 
     369             :     /* Compile RE */
     370      715958 :     re = RE_compile_and_cache(text_re, cflags, collation);
     371             : 
     372      715934 :     return RE_execute(re, dat, dat_len, nmatch, pmatch);
     373             : }
     374             : 
     375             : 
     376             : /*
     377             :  * parse_re_flags - parse the options argument of regexp_match and friends
     378             :  *
     379             :  *  flags --- output argument, filled with desired options
     380             :  *  opts --- TEXT object, or NULL for defaults
     381             :  *
     382             :  * This accepts all the options allowed by any of the callers; callers that
     383             :  * don't want some have to reject them after the fact.
     384             :  */
     385             : static void
     386      205458 : parse_re_flags(pg_re_flags *flags, text *opts)
     387             : {
     388             :     /* regex flavor is always folded into the compile flags */
     389      205458 :     flags->cflags = REG_ADVANCED;
     390      205458 :     flags->glob = false;
     391             : 
     392      205458 :     if (opts)
     393             :     {
     394        2270 :         char       *opt_p = VARDATA_ANY(opts);
     395        2270 :         int         opt_len = VARSIZE_ANY_EXHDR(opts);
     396             :         int         i;
     397             : 
     398        5368 :         for (i = 0; i < opt_len; i++)
     399             :         {
     400        3122 :             switch (opt_p[i])
     401             :             {
     402        1948 :                 case 'g':
     403        1948 :                     flags->glob = true;
     404        1948 :                     break;
     405           0 :                 case 'b':       /* BREs (but why???) */
     406           0 :                     flags->cflags &= ~(REG_ADVANCED | REG_EXTENDED | REG_QUOTE);
     407           0 :                     break;
     408          10 :                 case 'c':       /* case sensitive */
     409          10 :                     flags->cflags &= ~REG_ICASE;
     410          10 :                     break;
     411           0 :                 case 'e':       /* plain EREs */
     412           0 :                     flags->cflags |= REG_EXTENDED;
     413           0 :                     flags->cflags &= ~(REG_ADVANCED | REG_QUOTE);
     414           0 :                     break;
     415         292 :                 case 'i':       /* case insensitive */
     416         292 :                     flags->cflags |= REG_ICASE;
     417         292 :                     break;
     418         830 :                 case 'm':       /* Perloid synonym for n */
     419             :                 case 'n':       /* \n affects ^ $ . [^ */
     420         830 :                     flags->cflags |= REG_NEWLINE;
     421         830 :                     break;
     422           0 :                 case 'p':       /* ~Perl, \n affects . [^ */
     423           0 :                     flags->cflags |= REG_NLSTOP;
     424           0 :                     flags->cflags &= ~REG_NLANCH;
     425           0 :                     break;
     426           0 :                 case 'q':       /* literal string */
     427           0 :                     flags->cflags |= REG_QUOTE;
     428           0 :                     flags->cflags &= ~(REG_ADVANCED | REG_EXTENDED);
     429           0 :                     break;
     430          12 :                 case 's':       /* single line, \n ordinary */
     431          12 :                     flags->cflags &= ~REG_NEWLINE;
     432          12 :                     break;
     433           0 :                 case 't':       /* tight syntax */
     434           0 :                     flags->cflags &= ~REG_EXPANDED;
     435           0 :                     break;
     436           0 :                 case 'w':       /* weird, \n affects ^ $ only */
     437           0 :                     flags->cflags &= ~REG_NLSTOP;
     438           0 :                     flags->cflags |= REG_NLANCH;
     439           0 :                     break;
     440           6 :                 case 'x':       /* expanded syntax */
     441           6 :                     flags->cflags |= REG_EXPANDED;
     442           6 :                     break;
     443          24 :                 default:
     444          24 :                     ereport(ERROR,
     445             :                             (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     446             :                              errmsg("invalid regular expression option: \"%.*s\"",
     447             :                                     pg_mblen(opt_p + i), opt_p + i)));
     448             :                     break;
     449             :             }
     450             :         }
     451             :     }
     452      205434 : }
     453             : 
     454             : 
     455             : /*
     456             :  *  interface routines called by the function manager
     457             :  */
     458             : 
     459             : Datum
     460      272068 : nameregexeq(PG_FUNCTION_ARGS)
     461             : {
     462      272068 :     Name        n = PG_GETARG_NAME(0);
     463      272068 :     text       *p = PG_GETARG_TEXT_PP(1);
     464             : 
     465      272068 :     PG_RETURN_BOOL(RE_compile_and_execute(p,
     466             :                                           NameStr(*n),
     467             :                                           strlen(NameStr(*n)),
     468             :                                           REG_ADVANCED,
     469             :                                           PG_GET_COLLATION(),
     470             :                                           0, NULL));
     471             : }
     472             : 
     473             : Datum
     474       16896 : nameregexne(PG_FUNCTION_ARGS)
     475             : {
     476       16896 :     Name        n = PG_GETARG_NAME(0);
     477       16896 :     text       *p = PG_GETARG_TEXT_PP(1);
     478             : 
     479       16896 :     PG_RETURN_BOOL(!RE_compile_and_execute(p,
     480             :                                            NameStr(*n),
     481             :                                            strlen(NameStr(*n)),
     482             :                                            REG_ADVANCED,
     483             :                                            PG_GET_COLLATION(),
     484             :                                            0, NULL));
     485             : }
     486             : 
     487             : Datum
     488      390140 : textregexeq(PG_FUNCTION_ARGS)
     489             : {
     490      390140 :     text       *s = PG_GETARG_TEXT_PP(0);
     491      390140 :     text       *p = PG_GETARG_TEXT_PP(1);
     492             : 
     493      390140 :     PG_RETURN_BOOL(RE_compile_and_execute(p,
     494             :                                           VARDATA_ANY(s),
     495             :                                           VARSIZE_ANY_EXHDR(s),
     496             :                                           REG_ADVANCED,
     497             :                                           PG_GET_COLLATION(),
     498             :                                           0, NULL));
     499             : }
     500             : 
     501             : Datum
     502       34110 : textregexne(PG_FUNCTION_ARGS)
     503             : {
     504       34110 :     text       *s = PG_GETARG_TEXT_PP(0);
     505       34110 :     text       *p = PG_GETARG_TEXT_PP(1);
     506             : 
     507       34110 :     PG_RETURN_BOOL(!RE_compile_and_execute(p,
     508             :                                            VARDATA_ANY(s),
     509             :                                            VARSIZE_ANY_EXHDR(s),
     510             :                                            REG_ADVANCED,
     511             :                                            PG_GET_COLLATION(),
     512             :                                            0, NULL));
     513             : }
     514             : 
     515             : 
     516             : /*
     517             :  *  routines that use the regexp stuff, but ignore the case.
     518             :  *  for this, we use the REG_ICASE flag to pg_regcomp
     519             :  */
     520             : 
     521             : 
     522             : Datum
     523        2314 : nameicregexeq(PG_FUNCTION_ARGS)
     524             : {
     525        2314 :     Name        n = PG_GETARG_NAME(0);
     526        2314 :     text       *p = PG_GETARG_TEXT_PP(1);
     527             : 
     528        2314 :     PG_RETURN_BOOL(RE_compile_and_execute(p,
     529             :                                           NameStr(*n),
     530             :                                           strlen(NameStr(*n)),
     531             :                                           REG_ADVANCED | REG_ICASE,
     532             :                                           PG_GET_COLLATION(),
     533             :                                           0, NULL));
     534             : }
     535             : 
     536             : Datum
     537           6 : nameicregexne(PG_FUNCTION_ARGS)
     538             : {
     539           6 :     Name        n = PG_GETARG_NAME(0);
     540           6 :     text       *p = PG_GETARG_TEXT_PP(1);
     541             : 
     542           6 :     PG_RETURN_BOOL(!RE_compile_and_execute(p,
     543             :                                            NameStr(*n),
     544             :                                            strlen(NameStr(*n)),
     545             :                                            REG_ADVANCED | REG_ICASE,
     546             :                                            PG_GET_COLLATION(),
     547             :                                            0, NULL));
     548             : }
     549             : 
     550             : Datum
     551         108 : texticregexeq(PG_FUNCTION_ARGS)
     552             : {
     553         108 :     text       *s = PG_GETARG_TEXT_PP(0);
     554         108 :     text       *p = PG_GETARG_TEXT_PP(1);
     555             : 
     556         108 :     PG_RETURN_BOOL(RE_compile_and_execute(p,
     557             :                                           VARDATA_ANY(s),
     558             :                                           VARSIZE_ANY_EXHDR(s),
     559             :                                           REG_ADVANCED | REG_ICASE,
     560             :                                           PG_GET_COLLATION(),
     561             :                                           0, NULL));
     562             : }
     563             : 
     564             : Datum
     565          16 : texticregexne(PG_FUNCTION_ARGS)
     566             : {
     567          16 :     text       *s = PG_GETARG_TEXT_PP(0);
     568          16 :     text       *p = PG_GETARG_TEXT_PP(1);
     569             : 
     570          16 :     PG_RETURN_BOOL(!RE_compile_and_execute(p,
     571             :                                            VARDATA_ANY(s),
     572             :                                            VARSIZE_ANY_EXHDR(s),
     573             :                                            REG_ADVANCED | REG_ICASE,
     574             :                                            PG_GET_COLLATION(),
     575             :                                            0, NULL));
     576             : }
     577             : 
     578             : 
     579             : /*
     580             :  * textregexsubstr()
     581             :  *      Return a substring matched by a regular expression.
     582             :  */
     583             : Datum
     584        1602 : textregexsubstr(PG_FUNCTION_ARGS)
     585             : {
     586        1602 :     text       *s = PG_GETARG_TEXT_PP(0);
     587        1602 :     text       *p = PG_GETARG_TEXT_PP(1);
     588             :     regex_t    *re;
     589             :     regmatch_t  pmatch[2];
     590             :     int         so,
     591             :                 eo;
     592             : 
     593             :     /* Compile RE */
     594        1602 :     re = RE_compile_and_cache(p, REG_ADVANCED, PG_GET_COLLATION());
     595             : 
     596             :     /*
     597             :      * We pass two regmatch_t structs to get info about the overall match and
     598             :      * the match for the first parenthesized subexpression (if any). If there
     599             :      * is a parenthesized subexpression, we return what it matched; else
     600             :      * return what the whole regexp matched.
     601             :      */
     602        3204 :     if (!RE_execute(re,
     603        3204 :                     VARDATA_ANY(s), VARSIZE_ANY_EXHDR(s),
     604             :                     2, pmatch))
     605           6 :         PG_RETURN_NULL();       /* definitely no match */
     606             : 
     607        1596 :     if (re->re_nsub > 0)
     608             :     {
     609             :         /* has parenthesized subexpressions, use the first one */
     610        1522 :         so = pmatch[1].rm_so;
     611        1522 :         eo = pmatch[1].rm_eo;
     612             :     }
     613             :     else
     614             :     {
     615             :         /* no parenthesized subexpression, use whole match */
     616          74 :         so = pmatch[0].rm_so;
     617          74 :         eo = pmatch[0].rm_eo;
     618             :     }
     619             : 
     620             :     /*
     621             :      * It is possible to have a match to the whole pattern but no match for a
     622             :      * subexpression; for example 'foo(bar)?' is considered to match 'foo' but
     623             :      * there is no subexpression match.  So this extra test for match failure
     624             :      * is not redundant.
     625             :      */
     626        1596 :     if (so < 0 || eo < 0)
     627           6 :         PG_RETURN_NULL();
     628             : 
     629        1590 :     return DirectFunctionCall3(text_substr,
     630             :                                PointerGetDatum(s),
     631             :                                Int32GetDatum(so + 1),
     632             :                                Int32GetDatum(eo - so));
     633             : }
     634             : 
     635             : /*
     636             :  * textregexreplace_noopt()
     637             :  *      Return a string matched by a regular expression, with replacement.
     638             :  *
     639             :  * This version doesn't have an option argument: we default to case
     640             :  * sensitive match, replace the first instance only.
     641             :  */
     642             : Datum
     643        8050 : textregexreplace_noopt(PG_FUNCTION_ARGS)
     644             : {
     645        8050 :     text       *s = PG_GETARG_TEXT_PP(0);
     646        8050 :     text       *p = PG_GETARG_TEXT_PP(1);
     647        8050 :     text       *r = PG_GETARG_TEXT_PP(2);
     648             : 
     649        8050 :     PG_RETURN_TEXT_P(replace_text_regexp(s, p, r,
     650             :                                          REG_ADVANCED, PG_GET_COLLATION(),
     651             :                                          0, 1));
     652             : }
     653             : 
     654             : /*
     655             :  * textregexreplace()
     656             :  *      Return a string matched by a regular expression, with replacement.
     657             :  */
     658             : Datum
     659        1876 : textregexreplace(PG_FUNCTION_ARGS)
     660             : {
     661        1876 :     text       *s = PG_GETARG_TEXT_PP(0);
     662        1876 :     text       *p = PG_GETARG_TEXT_PP(1);
     663        1876 :     text       *r = PG_GETARG_TEXT_PP(2);
     664        1876 :     text       *opt = PG_GETARG_TEXT_PP(3);
     665             :     pg_re_flags flags;
     666             : 
     667             :     /*
     668             :      * regexp_replace() with four arguments will be preferentially resolved as
     669             :      * this form when the fourth argument is of type UNKNOWN.  However, the
     670             :      * user might have intended to call textregexreplace_extended_no_n.  If we
     671             :      * see flags that look like an integer, emit the same error that
     672             :      * parse_re_flags would, but add a HINT about how to fix it.
     673             :      */
     674        1876 :     if (VARSIZE_ANY_EXHDR(opt) > 0)
     675             :     {
     676        1876 :         char       *opt_p = VARDATA_ANY(opt);
     677             : 
     678        1876 :         if (*opt_p >= '0' && *opt_p <= '9')
     679           6 :             ereport(ERROR,
     680             :                     (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     681             :                      errmsg("invalid regular expression option: \"%.*s\"",
     682             :                             pg_mblen(opt_p), opt_p),
     683             :                      errhint("If you meant to use regexp_replace() with a start parameter, cast the fourth argument to integer explicitly.")));
     684             :     }
     685             : 
     686        1870 :     parse_re_flags(&flags, opt);
     687             : 
     688        1864 :     PG_RETURN_TEXT_P(replace_text_regexp(s, p, r,
     689             :                                          flags.cflags, PG_GET_COLLATION(),
     690             :                                          0, flags.glob ? 0 : 1));
     691             : }
     692             : 
     693             : /*
     694             :  * textregexreplace_extended()
     695             :  *      Return a string matched by a regular expression, with replacement.
     696             :  *      Extends textregexreplace by allowing a start position and the
     697             :  *      choice of the occurrence to replace (0 means all occurrences).
     698             :  */
     699             : Datum
     700          66 : textregexreplace_extended(PG_FUNCTION_ARGS)
     701             : {
     702          66 :     text       *s = PG_GETARG_TEXT_PP(0);
     703          66 :     text       *p = PG_GETARG_TEXT_PP(1);
     704          66 :     text       *r = PG_GETARG_TEXT_PP(2);
     705          66 :     int         start = 1;
     706          66 :     int         n = 1;
     707          66 :     text       *flags = PG_GETARG_TEXT_PP_IF_EXISTS(5);
     708             :     pg_re_flags re_flags;
     709             : 
     710             :     /* Collect optional parameters */
     711          66 :     if (PG_NARGS() > 3)
     712             :     {
     713          66 :         start = PG_GETARG_INT32(3);
     714          66 :         if (start <= 0)
     715           6 :             ereport(ERROR,
     716             :                     (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     717             :                      errmsg("invalid value for parameter \"%s\": %d",
     718             :                             "start", start)));
     719             :     }
     720          60 :     if (PG_NARGS() > 4)
     721             :     {
     722          54 :         n = PG_GETARG_INT32(4);
     723          54 :         if (n < 0)
     724           6 :             ereport(ERROR,
     725             :                     (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     726             :                      errmsg("invalid value for parameter \"%s\": %d",
     727             :                             "n", n)));
     728             :     }
     729             : 
     730             :     /* Determine options */
     731          54 :     parse_re_flags(&re_flags, flags);
     732             : 
     733             :     /* If N was not specified, deduce it from the 'g' flag */
     734          54 :     if (PG_NARGS() <= 4)
     735           6 :         n = re_flags.glob ? 0 : 1;
     736             : 
     737             :     /* Do the replacement(s) */
     738          54 :     PG_RETURN_TEXT_P(replace_text_regexp(s, p, r,
     739             :                                          re_flags.cflags, PG_GET_COLLATION(),
     740             :                                          start - 1, n));
     741             : }
     742             : 
     743             : /* This is separate to keep the opr_sanity regression test from complaining */
     744             : Datum
     745           6 : textregexreplace_extended_no_n(PG_FUNCTION_ARGS)
     746             : {
     747           6 :     return textregexreplace_extended(fcinfo);
     748             : }
     749             : 
     750             : /* This is separate to keep the opr_sanity regression test from complaining */
     751             : Datum
     752           6 : textregexreplace_extended_no_flags(PG_FUNCTION_ARGS)
     753             : {
     754           6 :     return textregexreplace_extended(fcinfo);
     755             : }
     756             : 
     757             : /*
     758             :  * similar_to_escape(), similar_escape()
     759             :  *
     760             :  * Convert a SQL "SIMILAR TO" regexp pattern to POSIX style, so it can be
     761             :  * used by our regexp engine.
     762             :  *
     763             :  * similar_escape_internal() is the common workhorse for three SQL-exposed
     764             :  * functions.  esc_text can be passed as NULL to select the default escape
     765             :  * (which is '\'), or as an empty string to select no escape character.
     766             :  */
     767             : static text *
     768         120 : similar_escape_internal(text *pat_text, text *esc_text)
     769             : {
     770             :     text       *result;
     771             :     char       *p,
     772             :                *e,
     773             :                *r;
     774             :     int         plen,
     775             :                 elen;
     776         120 :     bool        afterescape = false;
     777         120 :     bool        incharclass = false;
     778         120 :     int         nquotes = 0;
     779             : 
     780         120 :     p = VARDATA_ANY(pat_text);
     781         120 :     plen = VARSIZE_ANY_EXHDR(pat_text);
     782         120 :     if (esc_text == NULL)
     783             :     {
     784             :         /* No ESCAPE clause provided; default to backslash as escape */
     785          28 :         e = "\\";
     786          28 :         elen = 1;
     787             :     }
     788             :     else
     789             :     {
     790          92 :         e = VARDATA_ANY(esc_text);
     791          92 :         elen = VARSIZE_ANY_EXHDR(esc_text);
     792          92 :         if (elen == 0)
     793           6 :             e = NULL;           /* no escape character */
     794          86 :         else if (elen > 1)
     795             :         {
     796           6 :             int         escape_mblen = pg_mbstrlen_with_len(e, elen);
     797             : 
     798           6 :             if (escape_mblen > 1)
     799           6 :                 ereport(ERROR,
     800             :                         (errcode(ERRCODE_INVALID_ESCAPE_SEQUENCE),
     801             :                          errmsg("invalid escape string"),
     802             :                          errhint("Escape string must be empty or one character.")));
     803             :         }
     804             :     }
     805             : 
     806             :     /*----------
     807             :      * We surround the transformed input string with
     808             :      *          ^(?: ... )$
     809             :      * which requires some explanation.  We need "^" and "$" to force
     810             :      * the pattern to match the entire input string as per the SQL spec.
     811             :      * The "(?:" and ")" are a non-capturing set of parens; we have to have
     812             :      * parens in case the string contains "|", else the "^" and "$" will
     813             :      * be bound into the first and last alternatives which is not what we
     814             :      * want, and the parens must be non capturing because we don't want them
     815             :      * to count when selecting output for SUBSTRING.
     816             :      *
     817             :      * When the pattern is divided into three parts by escape-double-quotes,
     818             :      * what we emit is
     819             :      *          ^(?:part1){1,1}?(part2){1,1}(?:part3)$
     820             :      * which requires even more explanation.  The "{1,1}?" on part1 makes it
     821             :      * non-greedy so that it will match the smallest possible amount of text
     822             :      * not the largest, as required by SQL.  The plain parens around part2
     823             :      * are capturing parens so that that part is what controls the result of
     824             :      * SUBSTRING.  The "{1,1}" forces part2 to be greedy, so that it matches
     825             :      * the largest possible amount of text; hence part3 must match the
     826             :      * smallest amount of text, as required by SQL.  We don't need an explicit
     827             :      * greediness marker on part3.  Note that this also confines the effects
     828             :      * of any "|" characters to the respective part, which is what we want.
     829             :      *
     830             :      * The SQL spec says that SUBSTRING's pattern must contain exactly two
     831             :      * escape-double-quotes, but we only complain if there's more than two.
     832             :      * With none, we act as though part1 and part3 are empty; with one, we
     833             :      * act as though part3 is empty.  Both behaviors fall out of omitting
     834             :      * the relevant part separators in the above expansion.  If the result
     835             :      * of this function is used in a plain regexp match (SIMILAR TO), the
     836             :      * escape-double-quotes have no effect on the match behavior.
     837             :      *----------
     838             :      */
     839             : 
     840             :     /*
     841             :      * We need room for the prefix/postfix and part separators, plus as many
     842             :      * as 3 output bytes per input byte; since the input is at most 1GB this
     843             :      * can't overflow size_t.
     844             :      */
     845         114 :     result = (text *) palloc(VARHDRSZ + 23 + 3 * (size_t) plen);
     846         114 :     r = VARDATA(result);
     847             : 
     848         114 :     *r++ = '^';
     849         114 :     *r++ = '(';
     850         114 :     *r++ = '?';
     851         114 :     *r++ = ':';
     852             : 
     853         922 :     while (plen > 0)
     854             :     {
     855         814 :         char        pchar = *p;
     856             : 
     857             :         /*
     858             :          * If both the escape character and the current character from the
     859             :          * pattern are multi-byte, we need to take the slow path.
     860             :          *
     861             :          * But if one of them is single-byte, we can process the pattern one
     862             :          * byte at a time, ignoring multi-byte characters.  (This works
     863             :          * because all server-encodings have the property that a valid
     864             :          * multi-byte character representation cannot contain the
     865             :          * representation of a valid single-byte character.)
     866             :          */
     867             : 
     868         814 :         if (elen > 1)
     869             :         {
     870           0 :             int         mblen = pg_mblen(p);
     871             : 
     872           0 :             if (mblen > 1)
     873             :             {
     874             :                 /* slow, multi-byte path */
     875           0 :                 if (afterescape)
     876             :                 {
     877           0 :                     *r++ = '\\';
     878           0 :                     memcpy(r, p, mblen);
     879           0 :                     r += mblen;
     880           0 :                     afterescape = false;
     881             :                 }
     882           0 :                 else if (e && elen == mblen && memcmp(e, p, mblen) == 0)
     883             :                 {
     884             :                     /* SQL escape character; do not send to output */
     885           0 :                     afterescape = true;
     886             :                 }
     887             :                 else
     888             :                 {
     889             :                     /*
     890             :                      * We know it's a multi-byte character, so we don't need
     891             :                      * to do all the comparisons to single-byte characters
     892             :                      * that we do below.
     893             :                      */
     894           0 :                     memcpy(r, p, mblen);
     895           0 :                     r += mblen;
     896             :                 }
     897             : 
     898           0 :                 p += mblen;
     899           0 :                 plen -= mblen;
     900             : 
     901           0 :                 continue;
     902             :             }
     903             :         }
     904             : 
     905             :         /* fast path */
     906         814 :         if (afterescape)
     907             :         {
     908         142 :             if (pchar == '"' && !incharclass)  /* escape-double-quote? */
     909             :             {
     910             :                 /* emit appropriate part separator, per notes above */
     911         124 :                 if (nquotes == 0)
     912             :                 {
     913          62 :                     *r++ = ')';
     914          62 :                     *r++ = '{';
     915          62 :                     *r++ = '1';
     916          62 :                     *r++ = ',';
     917          62 :                     *r++ = '1';
     918          62 :                     *r++ = '}';
     919          62 :                     *r++ = '?';
     920          62 :                     *r++ = '(';
     921             :                 }
     922          62 :                 else if (nquotes == 1)
     923             :                 {
     924          56 :                     *r++ = ')';
     925          56 :                     *r++ = '{';
     926          56 :                     *r++ = '1';
     927          56 :                     *r++ = ',';
     928          56 :                     *r++ = '1';
     929          56 :                     *r++ = '}';
     930          56 :                     *r++ = '(';
     931          56 :                     *r++ = '?';
     932          56 :                     *r++ = ':';
     933             :                 }
     934             :                 else
     935           6 :                     ereport(ERROR,
     936             :                             (errcode(ERRCODE_INVALID_USE_OF_ESCAPE_CHARACTER),
     937             :                              errmsg("SQL regular expression may not contain more than two escape-double-quote separators")));
     938         118 :                 nquotes++;
     939             :             }
     940             :             else
     941             :             {
     942             :                 /*
     943             :                  * We allow any character at all to be escaped; notably, this
     944             :                  * allows access to POSIX character-class escapes such as
     945             :                  * "\d".  The SQL spec is considerably more restrictive.
     946             :                  */
     947          18 :                 *r++ = '\\';
     948          18 :                 *r++ = pchar;
     949             :             }
     950         136 :             afterescape = false;
     951             :         }
     952         672 :         else if (e && pchar == *e)
     953             :         {
     954             :             /* SQL escape character; do not send to output */
     955         142 :             afterescape = true;
     956             :         }
     957         530 :         else if (incharclass)
     958             :         {
     959           0 :             if (pchar == '\\')
     960           0 :                 *r++ = '\\';
     961           0 :             *r++ = pchar;
     962           0 :             if (pchar == ']')
     963           0 :                 incharclass = false;
     964             :         }
     965         530 :         else if (pchar == '[')
     966             :         {
     967           0 :             *r++ = pchar;
     968           0 :             incharclass = true;
     969             :         }
     970         530 :         else if (pchar == '%')
     971             :         {
     972          96 :             *r++ = '.';
     973          96 :             *r++ = '*';
     974             :         }
     975         434 :         else if (pchar == '_')
     976          52 :             *r++ = '.';
     977         382 :         else if (pchar == '(')
     978             :         {
     979             :             /* convert to non-capturing parenthesis */
     980          18 :             *r++ = '(';
     981          18 :             *r++ = '?';
     982          18 :             *r++ = ':';
     983             :         }
     984         364 :         else if (pchar == '\\' || pchar == '.' ||
     985         354 :                  pchar == '^' || pchar == '$')
     986             :         {
     987          10 :             *r++ = '\\';
     988          10 :             *r++ = pchar;
     989             :         }
     990             :         else
     991         354 :             *r++ = pchar;
     992         808 :         p++, plen--;
     993             :     }
     994             : 
     995         108 :     *r++ = ')';
     996         108 :     *r++ = '$';
     997             : 
     998         108 :     SET_VARSIZE(result, r - ((char *) result));
     999             : 
    1000         108 :     return result;
    1001             : }
    1002             : 
    1003             : /*
    1004             :  * similar_to_escape(pattern, escape)
    1005             :  */
    1006             : Datum
    1007          92 : similar_to_escape_2(PG_FUNCTION_ARGS)
    1008             : {
    1009          92 :     text       *pat_text = PG_GETARG_TEXT_PP(0);
    1010          92 :     text       *esc_text = PG_GETARG_TEXT_PP(1);
    1011             :     text       *result;
    1012             : 
    1013          92 :     result = similar_escape_internal(pat_text, esc_text);
    1014             : 
    1015          80 :     PG_RETURN_TEXT_P(result);
    1016             : }
    1017             : 
    1018             : /*
    1019             :  * similar_to_escape(pattern)
    1020             :  * Inserts a default escape character.
    1021             :  */
    1022             : Datum
    1023          28 : similar_to_escape_1(PG_FUNCTION_ARGS)
    1024             : {
    1025          28 :     text       *pat_text = PG_GETARG_TEXT_PP(0);
    1026             :     text       *result;
    1027             : 
    1028          28 :     result = similar_escape_internal(pat_text, NULL);
    1029             : 
    1030          28 :     PG_RETURN_TEXT_P(result);
    1031             : }
    1032             : 
    1033             : /*
    1034             :  * similar_escape(pattern, escape)
    1035             :  *
    1036             :  * Legacy function for compatibility with views stored using the
    1037             :  * pre-v13 expansion of SIMILAR TO.  Unlike the above functions, this
    1038             :  * is non-strict, which leads to not-per-spec handling of "ESCAPE NULL".
    1039             :  */
    1040             : Datum
    1041           0 : similar_escape(PG_FUNCTION_ARGS)
    1042             : {
    1043             :     text       *pat_text;
    1044             :     text       *esc_text;
    1045             :     text       *result;
    1046             : 
    1047             :     /* This function is not strict, so must test explicitly */
    1048           0 :     if (PG_ARGISNULL(0))
    1049           0 :         PG_RETURN_NULL();
    1050           0 :     pat_text = PG_GETARG_TEXT_PP(0);
    1051             : 
    1052           0 :     if (PG_ARGISNULL(1))
    1053           0 :         esc_text = NULL;        /* use default escape character */
    1054             :     else
    1055           0 :         esc_text = PG_GETARG_TEXT_PP(1);
    1056             : 
    1057           0 :     result = similar_escape_internal(pat_text, esc_text);
    1058             : 
    1059           0 :     PG_RETURN_TEXT_P(result);
    1060             : }
    1061             : 
    1062             : /*
    1063             :  * regexp_count()
    1064             :  *      Return the number of matches of a pattern within a string.
    1065             :  */
    1066             : Datum
    1067          48 : regexp_count(PG_FUNCTION_ARGS)
    1068             : {
    1069          48 :     text       *str = PG_GETARG_TEXT_PP(0);
    1070          48 :     text       *pattern = PG_GETARG_TEXT_PP(1);
    1071          48 :     int         start = 1;
    1072          48 :     text       *flags = PG_GETARG_TEXT_PP_IF_EXISTS(3);
    1073             :     pg_re_flags re_flags;
    1074             :     regexp_matches_ctx *matchctx;
    1075             : 
    1076             :     /* Collect optional parameters */
    1077          48 :     if (PG_NARGS() > 2)
    1078             :     {
    1079          42 :         start = PG_GETARG_INT32(2);
    1080          42 :         if (start <= 0)
    1081          12 :             ereport(ERROR,
    1082             :                     (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    1083             :                      errmsg("invalid value for parameter \"%s\": %d",
    1084             :                             "start", start)));
    1085             :     }
    1086             : 
    1087             :     /* Determine options */
    1088          36 :     parse_re_flags(&re_flags, flags);
    1089             :     /* User mustn't specify 'g' */
    1090          36 :     if (re_flags.glob)
    1091           0 :         ereport(ERROR,
    1092             :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    1093             :         /* translator: %s is a SQL function name */
    1094             :                  errmsg("%s does not support the \"global\" option",
    1095             :                         "regexp_count()")));
    1096             :     /* But we find all the matches anyway */
    1097          36 :     re_flags.glob = true;
    1098             : 
    1099             :     /* Do the matching */
    1100          36 :     matchctx = setup_regexp_matches(str, pattern, &re_flags, start - 1,
    1101             :                                     PG_GET_COLLATION(),
    1102             :                                     false,  /* can ignore subexprs */
    1103             :                                     false, false);
    1104             : 
    1105          36 :     PG_RETURN_INT32(matchctx->nmatches);
    1106             : }
    1107             : 
    1108             : /* This is separate to keep the opr_sanity regression test from complaining */
    1109             : Datum
    1110           6 : regexp_count_no_start(PG_FUNCTION_ARGS)
    1111             : {
    1112           6 :     return regexp_count(fcinfo);
    1113             : }
    1114             : 
    1115             : /* This is separate to keep the opr_sanity regression test from complaining */
    1116             : Datum
    1117          30 : regexp_count_no_flags(PG_FUNCTION_ARGS)
    1118             : {
    1119          30 :     return regexp_count(fcinfo);
    1120             : }
    1121             : 
    1122             : /*
    1123             :  * regexp_instr()
    1124             :  *      Return the match's position within the string
    1125             :  */
    1126             : Datum
    1127         156 : regexp_instr(PG_FUNCTION_ARGS)
    1128             : {
    1129         156 :     text       *str = PG_GETARG_TEXT_PP(0);
    1130         156 :     text       *pattern = PG_GETARG_TEXT_PP(1);
    1131         156 :     int         start = 1;
    1132         156 :     int         n = 1;
    1133         156 :     int         endoption = 0;
    1134         156 :     text       *flags = PG_GETARG_TEXT_PP_IF_EXISTS(5);
    1135         156 :     int         subexpr = 0;
    1136             :     int         pos;
    1137             :     pg_re_flags re_flags;
    1138             :     regexp_matches_ctx *matchctx;
    1139             : 
    1140             :     /* Collect optional parameters */
    1141         156 :     if (PG_NARGS() > 2)
    1142             :     {
    1143         138 :         start = PG_GETARG_INT32(2);
    1144         138 :         if (start <= 0)
    1145           6 :             ereport(ERROR,
    1146             :                     (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    1147             :                      errmsg("invalid value for parameter \"%s\": %d",
    1148             :                             "start", start)));
    1149             :     }
    1150         150 :     if (PG_NARGS() > 3)
    1151             :     {
    1152         126 :         n = PG_GETARG_INT32(3);
    1153         126 :         if (n <= 0)
    1154           6 :             ereport(ERROR,
    1155             :                     (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    1156             :                      errmsg("invalid value for parameter \"%s\": %d",
    1157             :                             "n", n)));
    1158             :     }
    1159         144 :     if (PG_NARGS() > 4)
    1160             :     {
    1161         108 :         endoption = PG_GETARG_INT32(4);
    1162         108 :         if (endoption != 0 && endoption != 1)
    1163          12 :             ereport(ERROR,
    1164             :                     (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    1165             :                      errmsg("invalid value for parameter \"%s\": %d",
    1166             :                             "endoption", endoption)));
    1167             :     }
    1168         132 :     if (PG_NARGS() > 6)
    1169             :     {
    1170          84 :         subexpr = PG_GETARG_INT32(6);
    1171          84 :         if (subexpr < 0)
    1172           6 :             ereport(ERROR,
    1173             :                     (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    1174             :                      errmsg("invalid value for parameter \"%s\": %d",
    1175             :                             "subexpr", subexpr)));
    1176             :     }
    1177             : 
    1178             :     /* Determine options */
    1179         126 :     parse_re_flags(&re_flags, flags);
    1180             :     /* User mustn't specify 'g' */
    1181         126 :     if (re_flags.glob)
    1182           6 :         ereport(ERROR,
    1183             :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    1184             :         /* translator: %s is a SQL function name */
    1185             :                  errmsg("%s does not support the \"global\" option",
    1186             :                         "regexp_instr()")));
    1187             :     /* But we find all the matches anyway */
    1188         120 :     re_flags.glob = true;
    1189             : 
    1190             :     /* Do the matching */
    1191         120 :     matchctx = setup_regexp_matches(str, pattern, &re_flags, start - 1,
    1192             :                                     PG_GET_COLLATION(),
    1193             :                                     (subexpr > 0),   /* need submatches? */
    1194             :                                     false, false);
    1195             : 
    1196             :     /* When n exceeds matches return 0 (includes case of no matches) */
    1197         120 :     if (n > matchctx->nmatches)
    1198          12 :         PG_RETURN_INT32(0);
    1199             : 
    1200             :     /* When subexpr exceeds number of subexpressions return 0 */
    1201         108 :     if (subexpr > matchctx->npatterns)
    1202          12 :         PG_RETURN_INT32(0);
    1203             : 
    1204             :     /* Select the appropriate match position to return */
    1205          96 :     pos = (n - 1) * matchctx->npatterns;
    1206          96 :     if (subexpr > 0)
    1207          54 :         pos += subexpr - 1;
    1208          96 :     pos *= 2;
    1209          96 :     if (endoption == 1)
    1210          30 :         pos += 1;
    1211             : 
    1212          96 :     if (matchctx->match_locs[pos] >= 0)
    1213          90 :         PG_RETURN_INT32(matchctx->match_locs[pos] + 1);
    1214             :     else
    1215           6 :         PG_RETURN_INT32(0);     /* position not identifiable */
    1216             : }
    1217             : 
    1218             : /* This is separate to keep the opr_sanity regression test from complaining */
    1219             : Datum
    1220          18 : regexp_instr_no_start(PG_FUNCTION_ARGS)
    1221             : {
    1222          18 :     return regexp_instr(fcinfo);
    1223             : }
    1224             : 
    1225             : /* This is separate to keep the opr_sanity regression test from complaining */
    1226             : Datum
    1227           6 : regexp_instr_no_n(PG_FUNCTION_ARGS)
    1228             : {
    1229           6 :     return regexp_instr(fcinfo);
    1230             : }
    1231             : 
    1232             : /* This is separate to keep the opr_sanity regression test from complaining */
    1233             : Datum
    1234          24 : regexp_instr_no_endoption(PG_FUNCTION_ARGS)
    1235             : {
    1236          24 :     return regexp_instr(fcinfo);
    1237             : }
    1238             : 
    1239             : /* This is separate to keep the opr_sanity regression test from complaining */
    1240             : Datum
    1241          12 : regexp_instr_no_flags(PG_FUNCTION_ARGS)
    1242             : {
    1243          12 :     return regexp_instr(fcinfo);
    1244             : }
    1245             : 
    1246             : /* This is separate to keep the opr_sanity regression test from complaining */
    1247             : Datum
    1248          12 : regexp_instr_no_subexpr(PG_FUNCTION_ARGS)
    1249             : {
    1250          12 :     return regexp_instr(fcinfo);
    1251             : }
    1252             : 
    1253             : /*
    1254             :  * regexp_like()
    1255             :  *      Test for a pattern match within a string.
    1256             :  */
    1257             : Datum
    1258          30 : regexp_like(PG_FUNCTION_ARGS)
    1259             : {
    1260          30 :     text       *str = PG_GETARG_TEXT_PP(0);
    1261          30 :     text       *pattern = PG_GETARG_TEXT_PP(1);
    1262          30 :     text       *flags = PG_GETARG_TEXT_PP_IF_EXISTS(2);
    1263             :     pg_re_flags re_flags;
    1264             : 
    1265             :     /* Determine options */
    1266          30 :     parse_re_flags(&re_flags, flags);
    1267             :     /* User mustn't specify 'g' */
    1268          30 :     if (re_flags.glob)
    1269           6 :         ereport(ERROR,
    1270             :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    1271             :         /* translator: %s is a SQL function name */
    1272             :                  errmsg("%s does not support the \"global\" option",
    1273             :                         "regexp_like()")));
    1274             : 
    1275             :     /* Otherwise it's like textregexeq/texticregexeq */
    1276          24 :     PG_RETURN_BOOL(RE_compile_and_execute(pattern,
    1277             :                                           VARDATA_ANY(str),
    1278             :                                           VARSIZE_ANY_EXHDR(str),
    1279             :                                           re_flags.cflags,
    1280             :                                           PG_GET_COLLATION(),
    1281             :                                           0, NULL));
    1282             : }
    1283             : 
    1284             : /* This is separate to keep the opr_sanity regression test from complaining */
    1285             : Datum
    1286           6 : regexp_like_no_flags(PG_FUNCTION_ARGS)
    1287             : {
    1288           6 :     return regexp_like(fcinfo);
    1289             : }
    1290             : 
    1291             : /*
    1292             :  * regexp_match()
    1293             :  *      Return the first substring(s) matching a pattern within a string.
    1294             :  */
    1295             : Datum
    1296        2556 : regexp_match(PG_FUNCTION_ARGS)
    1297             : {
    1298        2556 :     text       *orig_str = PG_GETARG_TEXT_PP(0);
    1299        2556 :     text       *pattern = PG_GETARG_TEXT_PP(1);
    1300        2556 :     text       *flags = PG_GETARG_TEXT_PP_IF_EXISTS(2);
    1301             :     pg_re_flags re_flags;
    1302             :     regexp_matches_ctx *matchctx;
    1303             : 
    1304             :     /* Determine options */
    1305        2556 :     parse_re_flags(&re_flags, flags);
    1306             :     /* User mustn't specify 'g' */
    1307        2556 :     if (re_flags.glob)
    1308           8 :         ereport(ERROR,
    1309             :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    1310             :         /* translator: %s is a SQL function name */
    1311             :                  errmsg("%s does not support the \"global\" option",
    1312             :                         "regexp_match()"),
    1313             :                  errhint("Use the regexp_matches function instead.")));
    1314             : 
    1315        2548 :     matchctx = setup_regexp_matches(orig_str, pattern, &re_flags, 0,
    1316             :                                     PG_GET_COLLATION(), true, false, false);
    1317             : 
    1318        2548 :     if (matchctx->nmatches == 0)
    1319         170 :         PG_RETURN_NULL();
    1320             : 
    1321             :     Assert(matchctx->nmatches == 1);
    1322             : 
    1323             :     /* Create workspace that build_regexp_match_result needs */
    1324        2378 :     matchctx->elems = (Datum *) palloc(sizeof(Datum) * matchctx->npatterns);
    1325        2378 :     matchctx->nulls = (bool *) palloc(sizeof(bool) * matchctx->npatterns);
    1326             : 
    1327        2378 :     PG_RETURN_DATUM(PointerGetDatum(build_regexp_match_result(matchctx)));
    1328             : }
    1329             : 
    1330             : /* This is separate to keep the opr_sanity regression test from complaining */
    1331             : Datum
    1332        2526 : regexp_match_no_flags(PG_FUNCTION_ARGS)
    1333             : {
    1334        2526 :     return regexp_match(fcinfo);
    1335             : }
    1336             : 
    1337             : /*
    1338             :  * regexp_matches()
    1339             :  *      Return a table of all matches of a pattern within a string.
    1340             :  */
    1341             : Datum
    1342         678 : regexp_matches(PG_FUNCTION_ARGS)
    1343             : {
    1344             :     FuncCallContext *funcctx;
    1345             :     regexp_matches_ctx *matchctx;
    1346             : 
    1347         678 :     if (SRF_IS_FIRSTCALL())
    1348             :     {
    1349         288 :         text       *pattern = PG_GETARG_TEXT_PP(1);
    1350         288 :         text       *flags = PG_GETARG_TEXT_PP_IF_EXISTS(2);
    1351             :         pg_re_flags re_flags;
    1352             :         MemoryContext oldcontext;
    1353             : 
    1354         288 :         funcctx = SRF_FIRSTCALL_INIT();
    1355         288 :         oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
    1356             : 
    1357             :         /* Determine options */
    1358         288 :         parse_re_flags(&re_flags, flags);
    1359             : 
    1360             :         /* be sure to copy the input string into the multi-call ctx */
    1361         282 :         matchctx = setup_regexp_matches(PG_GETARG_TEXT_P_COPY(0), pattern,
    1362             :                                         &re_flags, 0,
    1363             :                                         PG_GET_COLLATION(),
    1364             :                                         true, false, false);
    1365             : 
    1366             :         /* Pre-create workspace that build_regexp_match_result needs */
    1367         270 :         matchctx->elems = (Datum *) palloc(sizeof(Datum) * matchctx->npatterns);
    1368         270 :         matchctx->nulls = (bool *) palloc(sizeof(bool) * matchctx->npatterns);
    1369             : 
    1370         270 :         MemoryContextSwitchTo(oldcontext);
    1371         270 :         funcctx->user_fctx = (void *) matchctx;
    1372             :     }
    1373             : 
    1374         660 :     funcctx = SRF_PERCALL_SETUP();
    1375         660 :     matchctx = (regexp_matches_ctx *) funcctx->user_fctx;
    1376             : 
    1377         660 :     if (matchctx->next_match < matchctx->nmatches)
    1378             :     {
    1379             :         ArrayType  *result_ary;
    1380             : 
    1381         390 :         result_ary = build_regexp_match_result(matchctx);
    1382         390 :         matchctx->next_match++;
    1383         390 :         SRF_RETURN_NEXT(funcctx, PointerGetDatum(result_ary));
    1384             :     }
    1385             : 
    1386         270 :     SRF_RETURN_DONE(funcctx);
    1387             : }
    1388             : 
    1389             : /* This is separate to keep the opr_sanity regression test from complaining */
    1390             : Datum
    1391         354 : regexp_matches_no_flags(PG_FUNCTION_ARGS)
    1392             : {
    1393         354 :     return regexp_matches(fcinfo);
    1394             : }
    1395             : 
    1396             : /*
    1397             :  * setup_regexp_matches --- do the initial matching for regexp_match,
    1398             :  *      regexp_split, and related functions
    1399             :  *
    1400             :  * To avoid having to re-find the compiled pattern on each call, we do
    1401             :  * all the matching in one swoop.  The returned regexp_matches_ctx contains
    1402             :  * the locations of all the substrings matching the pattern.
    1403             :  *
    1404             :  * start_search: the character (not byte) offset in orig_str at which to
    1405             :  * begin the search.  Returned positions are relative to orig_str anyway.
    1406             :  * use_subpatterns: collect data about matches to parenthesized subexpressions.
    1407             :  * ignore_degenerate: ignore zero-length matches.
    1408             :  * fetching_unmatched: caller wants to fetch unmatched substrings.
    1409             :  *
    1410             :  * We don't currently assume that fetching_unmatched is exclusive of fetching
    1411             :  * the matched text too; if it's set, the conversion buffer is large enough to
    1412             :  * fetch any single matched or unmatched string, but not any larger
    1413             :  * substring.  (In practice, when splitting the matches are usually small
    1414             :  * anyway, and it didn't seem worth complicating the code further.)
    1415             :  */
    1416             : static regexp_matches_ctx *
    1417      203454 : setup_regexp_matches(text *orig_str, text *pattern, pg_re_flags *re_flags,
    1418             :                      int start_search,
    1419             :                      Oid collation,
    1420             :                      bool use_subpatterns,
    1421             :                      bool ignore_degenerate,
    1422             :                      bool fetching_unmatched)
    1423             : {
    1424      203454 :     regexp_matches_ctx *matchctx = palloc0(sizeof(regexp_matches_ctx));
    1425      203454 :     int         eml = pg_database_encoding_max_length();
    1426             :     int         orig_len;
    1427             :     pg_wchar   *wide_str;
    1428             :     int         wide_len;
    1429             :     int         cflags;
    1430             :     regex_t    *cpattern;
    1431             :     regmatch_t *pmatch;
    1432             :     int         pmatch_len;
    1433             :     int         array_len;
    1434             :     int         array_idx;
    1435             :     int         prev_match_end;
    1436             :     int         prev_valid_match_end;
    1437      203454 :     int         maxlen = 0;     /* largest fetch length in characters */
    1438             : 
    1439             :     /* save original string --- we'll extract result substrings from it */
    1440      203454 :     matchctx->orig_str = orig_str;
    1441             : 
    1442             :     /* convert string to pg_wchar form for matching */
    1443      203454 :     orig_len = VARSIZE_ANY_EXHDR(orig_str);
    1444      203454 :     wide_str = (pg_wchar *) palloc(sizeof(pg_wchar) * (orig_len + 1));
    1445      203454 :     wide_len = pg_mb2wchar_with_len(VARDATA_ANY(orig_str), wide_str, orig_len);
    1446             : 
    1447             :     /* set up the compiled pattern */
    1448      203454 :     cflags = re_flags->cflags;
    1449      203454 :     if (!use_subpatterns)
    1450      200522 :         cflags |= REG_NOSUB;
    1451      203454 :     cpattern = RE_compile_and_cache(pattern, cflags, collation);
    1452             : 
    1453             :     /* do we want to remember subpatterns? */
    1454      203442 :     if (use_subpatterns && cpattern->re_nsub > 0)
    1455             :     {
    1456        2710 :         matchctx->npatterns = cpattern->re_nsub;
    1457        2710 :         pmatch_len = cpattern->re_nsub + 1;
    1458             :     }
    1459             :     else
    1460             :     {
    1461      200732 :         use_subpatterns = false;
    1462      200732 :         matchctx->npatterns = 1;
    1463      200732 :         pmatch_len = 1;
    1464             :     }
    1465             : 
    1466             :     /* temporary output space for RE package */
    1467      203442 :     pmatch = palloc(sizeof(regmatch_t) * pmatch_len);
    1468             : 
    1469             :     /*
    1470             :      * the real output space (grown dynamically if needed)
    1471             :      *
    1472             :      * use values 2^n-1, not 2^n, so that we hit the limit at 2^28-1 rather
    1473             :      * than at 2^27
    1474             :      */
    1475      203442 :     array_len = re_flags->glob ? 255 : 31;
    1476      203442 :     matchctx->match_locs = (int *) palloc(sizeof(int) * array_len);
    1477      203442 :     array_idx = 0;
    1478             : 
    1479             :     /* search for the pattern, perhaps repeatedly */
    1480      203442 :     prev_match_end = 0;
    1481      203442 :     prev_valid_match_end = 0;
    1482     1095174 :     while (RE_wchar_execute(cpattern, wide_str, wide_len, start_search,
    1483             :                             pmatch_len, pmatch))
    1484             :     {
    1485             :         /*
    1486             :          * If requested, ignore degenerate matches, which are zero-length
    1487             :          * matches occurring at the start or end of a string or just after a
    1488             :          * previous match.
    1489             :          */
    1490      894346 :         if (!ignore_degenerate ||
    1491      891182 :             (pmatch[0].rm_so < wide_len &&
    1492      891140 :              pmatch[0].rm_eo > prev_match_end))
    1493             :         {
    1494             :             /* enlarge output space if needed */
    1495      894526 :             while (array_idx + matchctx->npatterns * 2 + 1 > array_len)
    1496             :             {
    1497         360 :                 array_len += array_len + 1; /* 2^n-1 => 2^(n+1)-1 */
    1498         360 :                 if (array_len > MaxAllocSize / sizeof(int))
    1499           0 :                     ereport(ERROR,
    1500             :                             (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
    1501             :                              errmsg("too many regular expression matches")));
    1502         360 :                 matchctx->match_locs = (int *) repalloc(matchctx->match_locs,
    1503             :                                                         sizeof(int) * array_len);
    1504             :             }
    1505             : 
    1506             :             /* save this match's locations */
    1507      894166 :             if (use_subpatterns)
    1508             :             {
    1509             :                 int         i;
    1510             : 
    1511        7872 :                 for (i = 1; i <= matchctx->npatterns; i++)
    1512             :                 {
    1513        5314 :                     int         so = pmatch[i].rm_so;
    1514        5314 :                     int         eo = pmatch[i].rm_eo;
    1515             : 
    1516        5314 :                     matchctx->match_locs[array_idx++] = so;
    1517        5314 :                     matchctx->match_locs[array_idx++] = eo;
    1518        5314 :                     if (so >= 0 && eo >= 0 && (eo - so) > maxlen)
    1519        3420 :                         maxlen = (eo - so);
    1520             :                 }
    1521             :             }
    1522             :             else
    1523             :             {
    1524      891608 :                 int         so = pmatch[0].rm_so;
    1525      891608 :                 int         eo = pmatch[0].rm_eo;
    1526             : 
    1527      891608 :                 matchctx->match_locs[array_idx++] = so;
    1528      891608 :                 matchctx->match_locs[array_idx++] = eo;
    1529      891608 :                 if (so >= 0 && eo >= 0 && (eo - so) > maxlen)
    1530      200566 :                     maxlen = (eo - so);
    1531             :             }
    1532      894166 :             matchctx->nmatches++;
    1533             : 
    1534             :             /*
    1535             :              * check length of unmatched portion between end of previous valid
    1536             :              * (nondegenerate, or degenerate but not ignored) match and start
    1537             :              * of current one
    1538             :              */
    1539      894166 :             if (fetching_unmatched &&
    1540      891002 :                 pmatch[0].rm_so >= 0 &&
    1541      891002 :                 (pmatch[0].rm_so - prev_valid_match_end) > maxlen)
    1542      380710 :                 maxlen = (pmatch[0].rm_so - prev_valid_match_end);
    1543      894166 :             prev_valid_match_end = pmatch[0].rm_eo;
    1544             :         }
    1545      894346 :         prev_match_end = pmatch[0].rm_eo;
    1546             : 
    1547             :         /* if not glob, stop after one match */
    1548      894346 :         if (!re_flags->glob)
    1549        2548 :             break;
    1550             : 
    1551             :         /*
    1552             :          * Advance search position.  Normally we start the next search at the
    1553             :          * end of the previous match; but if the match was of zero length, we
    1554             :          * have to advance by one character, or we'd just find the same match
    1555             :          * again.
    1556             :          */
    1557      891798 :         start_search = prev_match_end;
    1558      891798 :         if (pmatch[0].rm_so == pmatch[0].rm_eo)
    1559        1176 :             start_search++;
    1560      891798 :         if (start_search > wide_len)
    1561          66 :             break;
    1562             :     }
    1563             : 
    1564             :     /*
    1565             :      * check length of unmatched portion between end of last match and end of
    1566             :      * input string
    1567             :      */
    1568      203442 :     if (fetching_unmatched &&
    1569      200384 :         (wide_len - prev_valid_match_end) > maxlen)
    1570          28 :         maxlen = (wide_len - prev_valid_match_end);
    1571             : 
    1572             :     /*
    1573             :      * Keep a note of the end position of the string for the benefit of
    1574             :      * splitting code.
    1575             :      */
    1576      203442 :     matchctx->match_locs[array_idx] = wide_len;
    1577             : 
    1578      203442 :     if (eml > 1)
    1579             :     {
    1580        1120 :         int64       maxsiz = eml * (int64) maxlen;
    1581             :         int         conv_bufsiz;
    1582             : 
    1583             :         /*
    1584             :          * Make the conversion buffer large enough for any substring of
    1585             :          * interest.
    1586             :          *
    1587             :          * Worst case: assume we need the maximum size (maxlen*eml), but take
    1588             :          * advantage of the fact that the original string length in bytes is
    1589             :          * an upper bound on the byte length of any fetched substring (and we
    1590             :          * know that len+1 is safe to allocate because the varlena header is
    1591             :          * longer than 1 byte).
    1592             :          */
    1593        1120 :         if (maxsiz > orig_len)
    1594         136 :             conv_bufsiz = orig_len + 1;
    1595             :         else
    1596         984 :             conv_bufsiz = maxsiz + 1;   /* safe since maxsiz < 2^30 */
    1597             : 
    1598        1120 :         matchctx->conv_buf = palloc(conv_bufsiz);
    1599        1120 :         matchctx->conv_bufsiz = conv_bufsiz;
    1600        1120 :         matchctx->wide_str = wide_str;
    1601             :     }
    1602             :     else
    1603             :     {
    1604             :         /* No need to keep the wide string if we're in a single-byte charset. */
    1605      202322 :         pfree(wide_str);
    1606      202322 :         matchctx->wide_str = NULL;
    1607      202322 :         matchctx->conv_buf = NULL;
    1608      202322 :         matchctx->conv_bufsiz = 0;
    1609             :     }
    1610             : 
    1611             :     /* Clean up temp storage */
    1612      203442 :     pfree(pmatch);
    1613             : 
    1614      203442 :     return matchctx;
    1615             : }
    1616             : 
    1617             : /*
    1618             :  * build_regexp_match_result - build output array for current match
    1619             :  */
    1620             : static ArrayType *
    1621        2768 : build_regexp_match_result(regexp_matches_ctx *matchctx)
    1622             : {
    1623        2768 :     char       *buf = matchctx->conv_buf;
    1624        2768 :     Datum      *elems = matchctx->elems;
    1625        2768 :     bool       *nulls = matchctx->nulls;
    1626             :     int         dims[1];
    1627             :     int         lbs[1];
    1628             :     int         loc;
    1629             :     int         i;
    1630             : 
    1631             :     /* Extract matching substrings from the original string */
    1632        2768 :     loc = matchctx->next_match * matchctx->npatterns * 2;
    1633        8022 :     for (i = 0; i < matchctx->npatterns; i++)
    1634             :     {
    1635        5254 :         int         so = matchctx->match_locs[loc++];
    1636        5254 :         int         eo = matchctx->match_locs[loc++];
    1637             : 
    1638        5254 :         if (so < 0 || eo < 0)
    1639             :         {
    1640           6 :             elems[i] = (Datum) 0;
    1641           6 :             nulls[i] = true;
    1642             :         }
    1643        5248 :         else if (buf)
    1644             :         {
    1645        1728 :             int         len = pg_wchar2mb_with_len(matchctx->wide_str + so,
    1646             :                                                    buf,
    1647             :                                                    eo - so);
    1648             : 
    1649             :             Assert(len < matchctx->conv_bufsiz);
    1650        1728 :             elems[i] = PointerGetDatum(cstring_to_text_with_len(buf, len));
    1651        1728 :             nulls[i] = false;
    1652             :         }
    1653             :         else
    1654             :         {
    1655        3520 :             elems[i] = DirectFunctionCall3(text_substr,
    1656             :                                            PointerGetDatum(matchctx->orig_str),
    1657             :                                            Int32GetDatum(so + 1),
    1658             :                                            Int32GetDatum(eo - so));
    1659        3520 :             nulls[i] = false;
    1660             :         }
    1661             :     }
    1662             : 
    1663             :     /* And form an array */
    1664        2768 :     dims[0] = matchctx->npatterns;
    1665        2768 :     lbs[0] = 1;
    1666             :     /* XXX: this hardcodes assumptions about the text type */
    1667        2768 :     return construct_md_array(elems, nulls, 1, dims, lbs,
    1668             :                               TEXTOID, -1, false, TYPALIGN_INT);
    1669             : }
    1670             : 
    1671             : /*
    1672             :  * regexp_split_to_table()
    1673             :  *      Split the string at matches of the pattern, returning the
    1674             :  *      split-out substrings as a table.
    1675             :  */
    1676             : Datum
    1677         622 : regexp_split_to_table(PG_FUNCTION_ARGS)
    1678             : {
    1679             :     FuncCallContext *funcctx;
    1680             :     regexp_matches_ctx *splitctx;
    1681             : 
    1682         622 :     if (SRF_IS_FIRSTCALL())
    1683             :     {
    1684          52 :         text       *pattern = PG_GETARG_TEXT_PP(1);
    1685          52 :         text       *flags = PG_GETARG_TEXT_PP_IF_EXISTS(2);
    1686             :         pg_re_flags re_flags;
    1687             :         MemoryContext oldcontext;
    1688             : 
    1689          52 :         funcctx = SRF_FIRSTCALL_INIT();
    1690          52 :         oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
    1691             : 
    1692             :         /* Determine options */
    1693          52 :         parse_re_flags(&re_flags, flags);
    1694             :         /* User mustn't specify 'g' */
    1695          46 :         if (re_flags.glob)
    1696           6 :             ereport(ERROR,
    1697             :                     (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    1698             :             /* translator: %s is a SQL function name */
    1699             :                      errmsg("%s does not support the \"global\" option",
    1700             :                             "regexp_split_to_table()")));
    1701             :         /* But we find all the matches anyway */
    1702          40 :         re_flags.glob = true;
    1703             : 
    1704             :         /* be sure to copy the input string into the multi-call ctx */
    1705          40 :         splitctx = setup_regexp_matches(PG_GETARG_TEXT_P_COPY(0), pattern,
    1706             :                                         &re_flags, 0,
    1707             :                                         PG_GET_COLLATION(),
    1708             :                                         false, true, true);
    1709             : 
    1710          40 :         MemoryContextSwitchTo(oldcontext);
    1711          40 :         funcctx->user_fctx = (void *) splitctx;
    1712             :     }
    1713             : 
    1714         610 :     funcctx = SRF_PERCALL_SETUP();
    1715         610 :     splitctx = (regexp_matches_ctx *) funcctx->user_fctx;
    1716             : 
    1717         610 :     if (splitctx->next_match <= splitctx->nmatches)
    1718             :     {
    1719         570 :         Datum       result = build_regexp_split_result(splitctx);
    1720             : 
    1721         570 :         splitctx->next_match++;
    1722         570 :         SRF_RETURN_NEXT(funcctx, result);
    1723             :     }
    1724             : 
    1725          40 :     SRF_RETURN_DONE(funcctx);
    1726             : }
    1727             : 
    1728             : /* This is separate to keep the opr_sanity regression test from complaining */
    1729             : Datum
    1730         552 : regexp_split_to_table_no_flags(PG_FUNCTION_ARGS)
    1731             : {
    1732         552 :     return regexp_split_to_table(fcinfo);
    1733             : }
    1734             : 
    1735             : /*
    1736             :  * regexp_split_to_array()
    1737             :  *      Split the string at matches of the pattern, returning the
    1738             :  *      split-out substrings as an array.
    1739             :  */
    1740             : Datum
    1741      200356 : regexp_split_to_array(PG_FUNCTION_ARGS)
    1742             : {
    1743      200356 :     ArrayBuildState *astate = NULL;
    1744             :     pg_re_flags re_flags;
    1745             :     regexp_matches_ctx *splitctx;
    1746             : 
    1747             :     /* Determine options */
    1748      200356 :     parse_re_flags(&re_flags, PG_GETARG_TEXT_PP_IF_EXISTS(2));
    1749             :     /* User mustn't specify 'g' */
    1750      200350 :     if (re_flags.glob)
    1751           6 :         ereport(ERROR,
    1752             :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    1753             :         /* translator: %s is a SQL function name */
    1754             :                  errmsg("%s does not support the \"global\" option",
    1755             :                         "regexp_split_to_array()")));
    1756             :     /* But we find all the matches anyway */
    1757      200344 :     re_flags.glob = true;
    1758             : 
    1759      200344 :     splitctx = setup_regexp_matches(PG_GETARG_TEXT_PP(0),
    1760      200344 :                                     PG_GETARG_TEXT_PP(1),
    1761             :                                     &re_flags, 0,
    1762             :                                     PG_GET_COLLATION(),
    1763             :                                     false, true, true);
    1764             : 
    1765     1291160 :     while (splitctx->next_match <= splitctx->nmatches)
    1766             :     {
    1767     1090816 :         astate = accumArrayResult(astate,
    1768             :                                   build_regexp_split_result(splitctx),
    1769             :                                   false,
    1770             :                                   TEXTOID,
    1771             :                                   CurrentMemoryContext);
    1772     1090816 :         splitctx->next_match++;
    1773             :     }
    1774             : 
    1775      200344 :     PG_RETURN_DATUM(makeArrayResult(astate, CurrentMemoryContext));
    1776             : }
    1777             : 
    1778             : /* This is separate to keep the opr_sanity regression test from complaining */
    1779             : Datum
    1780      200314 : regexp_split_to_array_no_flags(PG_FUNCTION_ARGS)
    1781             : {
    1782      200314 :     return regexp_split_to_array(fcinfo);
    1783             : }
    1784             : 
    1785             : /*
    1786             :  * build_regexp_split_result - build output string for current match
    1787             :  *
    1788             :  * We return the string between the current match and the previous one,
    1789             :  * or the string after the last match when next_match == nmatches.
    1790             :  */
    1791             : static Datum
    1792     1091386 : build_regexp_split_result(regexp_matches_ctx *splitctx)
    1793             : {
    1794     1091386 :     char       *buf = splitctx->conv_buf;
    1795             :     int         startpos;
    1796             :     int         endpos;
    1797             : 
    1798     1091386 :     if (splitctx->next_match > 0)
    1799      891002 :         startpos = splitctx->match_locs[splitctx->next_match * 2 - 1];
    1800             :     else
    1801      200384 :         startpos = 0;
    1802     1091386 :     if (startpos < 0)
    1803           0 :         elog(ERROR, "invalid match ending position");
    1804             : 
    1805     1091386 :     endpos = splitctx->match_locs[splitctx->next_match * 2];
    1806     1091386 :     if (endpos < startpos)
    1807           0 :         elog(ERROR, "invalid match starting position");
    1808             : 
    1809     1091386 :     if (buf)
    1810             :     {
    1811             :         int         len;
    1812             : 
    1813       30416 :         len = pg_wchar2mb_with_len(splitctx->wide_str + startpos,
    1814             :                                    buf,
    1815             :                                    endpos - startpos);
    1816             :         Assert(len < splitctx->conv_bufsiz);
    1817       30416 :         return PointerGetDatum(cstring_to_text_with_len(buf, len));
    1818             :     }
    1819             :     else
    1820             :     {
    1821     1060970 :         return DirectFunctionCall3(text_substr,
    1822             :                                    PointerGetDatum(splitctx->orig_str),
    1823             :                                    Int32GetDatum(startpos + 1),
    1824             :                                    Int32GetDatum(endpos - startpos));
    1825             :     }
    1826             : }
    1827             : 
    1828             : /*
    1829             :  * regexp_substr()
    1830             :  *      Return the substring that matches a regular expression pattern
    1831             :  */
    1832             : Datum
    1833         108 : regexp_substr(PG_FUNCTION_ARGS)
    1834             : {
    1835         108 :     text       *str = PG_GETARG_TEXT_PP(0);
    1836         108 :     text       *pattern = PG_GETARG_TEXT_PP(1);
    1837         108 :     int         start = 1;
    1838         108 :     int         n = 1;
    1839         108 :     text       *flags = PG_GETARG_TEXT_PP_IF_EXISTS(4);
    1840         108 :     int         subexpr = 0;
    1841             :     int         so,
    1842             :                 eo,
    1843             :                 pos;
    1844             :     pg_re_flags re_flags;
    1845             :     regexp_matches_ctx *matchctx;
    1846             : 
    1847             :     /* Collect optional parameters */
    1848         108 :     if (PG_NARGS() > 2)
    1849             :     {
    1850          90 :         start = PG_GETARG_INT32(2);
    1851          90 :         if (start <= 0)
    1852           6 :             ereport(ERROR,
    1853             :                     (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    1854             :                      errmsg("invalid value for parameter \"%s\": %d",
    1855             :                             "start", start)));
    1856             :     }
    1857         102 :     if (PG_NARGS() > 3)
    1858             :     {
    1859          78 :         n = PG_GETARG_INT32(3);
    1860          78 :         if (n <= 0)
    1861           6 :             ereport(ERROR,
    1862             :                     (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    1863             :                      errmsg("invalid value for parameter \"%s\": %d",
    1864             :                             "n", n)));
    1865             :     }
    1866          96 :     if (PG_NARGS() > 5)
    1867             :     {
    1868          48 :         subexpr = PG_GETARG_INT32(5);
    1869          48 :         if (subexpr < 0)
    1870           6 :             ereport(ERROR,
    1871             :                     (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    1872             :                      errmsg("invalid value for parameter \"%s\": %d",
    1873             :                             "subexpr", subexpr)));
    1874             :     }
    1875             : 
    1876             :     /* Determine options */
    1877          90 :     parse_re_flags(&re_flags, flags);
    1878             :     /* User mustn't specify 'g' */
    1879          90 :     if (re_flags.glob)
    1880           6 :         ereport(ERROR,
    1881             :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    1882             :         /* translator: %s is a SQL function name */
    1883             :                  errmsg("%s does not support the \"global\" option",
    1884             :                         "regexp_substr()")));
    1885             :     /* But we find all the matches anyway */
    1886          84 :     re_flags.glob = true;
    1887             : 
    1888             :     /* Do the matching */
    1889          84 :     matchctx = setup_regexp_matches(str, pattern, &re_flags, start - 1,
    1890             :                                     PG_GET_COLLATION(),
    1891             :                                     (subexpr > 0),   /* need submatches? */
    1892             :                                     false, false);
    1893             : 
    1894             :     /* When n exceeds matches return NULL (includes case of no matches) */
    1895          84 :     if (n > matchctx->nmatches)
    1896          12 :         PG_RETURN_NULL();
    1897             : 
    1898             :     /* When subexpr exceeds number of subexpressions return NULL */
    1899          72 :     if (subexpr > matchctx->npatterns)
    1900           6 :         PG_RETURN_NULL();
    1901             : 
    1902             :     /* Select the appropriate match position to return */
    1903          66 :     pos = (n - 1) * matchctx->npatterns;
    1904          66 :     if (subexpr > 0)
    1905          30 :         pos += subexpr - 1;
    1906          66 :     pos *= 2;
    1907          66 :     so = matchctx->match_locs[pos];
    1908          66 :     eo = matchctx->match_locs[pos + 1];
    1909             : 
    1910          66 :     if (so < 0 || eo < 0)
    1911           6 :         PG_RETURN_NULL();       /* unidentifiable location */
    1912             : 
    1913          60 :     PG_RETURN_DATUM(DirectFunctionCall3(text_substr,
    1914             :                                         PointerGetDatum(matchctx->orig_str),
    1915             :                                         Int32GetDatum(so + 1),
    1916             :                                         Int32GetDatum(eo - so)));
    1917             : }
    1918             : 
    1919             : /* This is separate to keep the opr_sanity regression test from complaining */
    1920             : Datum
    1921          18 : regexp_substr_no_start(PG_FUNCTION_ARGS)
    1922             : {
    1923          18 :     return regexp_substr(fcinfo);
    1924             : }
    1925             : 
    1926             : /* This is separate to keep the opr_sanity regression test from complaining */
    1927             : Datum
    1928           6 : regexp_substr_no_n(PG_FUNCTION_ARGS)
    1929             : {
    1930           6 :     return regexp_substr(fcinfo);
    1931             : }
    1932             : 
    1933             : /* This is separate to keep the opr_sanity regression test from complaining */
    1934             : Datum
    1935          24 : regexp_substr_no_flags(PG_FUNCTION_ARGS)
    1936             : {
    1937          24 :     return regexp_substr(fcinfo);
    1938             : }
    1939             : 
    1940             : /* This is separate to keep the opr_sanity regression test from complaining */
    1941             : Datum
    1942          12 : regexp_substr_no_subexpr(PG_FUNCTION_ARGS)
    1943             : {
    1944          12 :     return regexp_substr(fcinfo);
    1945             : }
    1946             : 
    1947             : /*
    1948             :  * regexp_fixed_prefix - extract fixed prefix, if any, for a regexp
    1949             :  *
    1950             :  * The result is NULL if there is no fixed prefix, else a palloc'd string.
    1951             :  * If it is an exact match, not just a prefix, *exact is returned as true.
    1952             :  */
    1953             : char *
    1954       13540 : regexp_fixed_prefix(text *text_re, bool case_insensitive, Oid collation,
    1955             :                     bool *exact)
    1956             : {
    1957             :     char       *result;
    1958             :     regex_t    *re;
    1959             :     int         cflags;
    1960             :     int         re_result;
    1961             :     pg_wchar   *str;
    1962             :     size_t      slen;
    1963             :     size_t      maxlen;
    1964             :     char        errMsg[100];
    1965             : 
    1966       13540 :     *exact = false;             /* default result */
    1967             : 
    1968             :     /* Compile RE */
    1969       13540 :     cflags = REG_ADVANCED;
    1970       13540 :     if (case_insensitive)
    1971          42 :         cflags |= REG_ICASE;
    1972             : 
    1973       13540 :     re = RE_compile_and_cache(text_re, cflags | REG_NOSUB, collation);
    1974             : 
    1975             :     /* Examine it to see if there's a fixed prefix */
    1976       13532 :     re_result = pg_regprefix(re, &str, &slen);
    1977             : 
    1978       13532 :     switch (re_result)
    1979             :     {
    1980         672 :         case REG_NOMATCH:
    1981         672 :             return NULL;
    1982             : 
    1983        1598 :         case REG_PREFIX:
    1984             :             /* continue with wchar conversion */
    1985        1598 :             break;
    1986             : 
    1987       11262 :         case REG_EXACT:
    1988       11262 :             *exact = true;
    1989             :             /* continue with wchar conversion */
    1990       11262 :             break;
    1991             : 
    1992           0 :         default:
    1993             :             /* re failed??? */
    1994           0 :             pg_regerror(re_result, re, errMsg, sizeof(errMsg));
    1995           0 :             ereport(ERROR,
    1996             :                     (errcode(ERRCODE_INVALID_REGULAR_EXPRESSION),
    1997             :                      errmsg("regular expression failed: %s", errMsg)));
    1998             :             break;
    1999             :     }
    2000             : 
    2001             :     /* Convert pg_wchar result back to database encoding */
    2002       12860 :     maxlen = pg_database_encoding_max_length() * slen + 1;
    2003       12860 :     result = (char *) palloc(maxlen);
    2004       12860 :     slen = pg_wchar2mb_with_len(str, result, slen);
    2005             :     Assert(slen < maxlen);
    2006             : 
    2007       12860 :     pfree(str);
    2008             : 
    2009       12860 :     return result;
    2010             : }

Generated by: LCOV version 1.14