text: /*-------------------------------------------------------------------------
   *
   * regexp.c
   *	  Postgres' interface to the regular expression package.
   *
   * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
   * Portions Copyright (c) 1994, Regents of the University of California
   *
   *
   * IDENTIFICATION
   *	  $PostgreSQL: pgsql/src/backend/utils/adt/regexp.c,v 1.78 2008/01/01 19:45:52 momjian Exp $
   *
   *		Alistair Crooks added the code for the regex caching
   *		agc - cached the regular expressions used - there's a good chance
   *		that we'll get a hit, so this saves a compile step for every
   *		attempted match. I haven't actually measured the speed improvement,
   *		but it `looks' a lot quicker visually when watching regression
   *		test output.
   *
   *		agc - incorporated Keith Bostic's Berkeley regex code into
   *		the tree for all ports. To distinguish this regex code from any that
   *		is existent on a platform, I've prepended the string "pg_" to
   *		the functions regcomp, regerror, regexec and regfree.
   *		Fixed a bug that was originally a typo by me, where `i' was used
   *		instead of `oldest' when compiling regular expressions - benign
   *		results mostly, although occasionally it bit you...
   *
   *-------------------------------------------------------------------------
   */
  #include "postgres.h"
  
  #include "catalog/pg_type.h"
  #include "funcapi.h"
  #include "regex/regex.h"
  #include "utils/builtins.h"
  #include "utils/guc.h"
  
  #define PG_GETARG_TEXT_PP_IF_EXISTS(_n) \
  	(PG_NARGS() > (_n) ? PG_GETARG_TEXT_PP(_n) : NULL)
  
  
  /* GUC-settable flavor parameter */
  static int	regex_flavor = REG_ADVANCED;
  
  
  /* all the options of interest for regex functions */
  typedef struct pg_re_flags
  {
  	int			cflags;			/* compile flags for Spencer's regex code */
  	bool		glob;			/* do it globally (for each occurrence) */
  } pg_re_flags;
  
  /* cross-call state for regexp_matches(), also regexp_split() */
  typedef struct regexp_matches_ctx
  {
  	text	   *orig_str;		/* data string in original TEXT form */
  	int			nmatches;		/* number of places where pattern matched */
  	int			npatterns;		/* number of capturing subpatterns */
  	/* We store start char index and end+1 char index for each match */
  	/* so the number of entries in match_locs is nmatches * npatterns * 2 */
  	int		   *match_locs;		/* 0-based character indexes */
  	int			next_match;		/* 0-based index of next match to process */
  	/* workspace for build_regexp_matches_result() */
  	Datum	   *elems;			/* has npatterns elements */
  	bool	   *nulls;			/* has npatterns elements */
  } regexp_matches_ctx;
  
  /*
   * We cache precompiled regular expressions using a "self organizing list"
   * structure, in which recently-used items tend to be near the front.
   * Whenever we use an entry, it's moved up to the front of the list.
   * Over time, an item's average position corresponds to its frequency of use.
   *
   * When we first create an entry, it's inserted at the front of
   * the array, dropping the entry at the end of the array if necessary to
   * make room.  (This might seem to be weighting the new entry too heavily,
   * but if we insert new entries further back, we'll be unable to adjust to
   * a sudden shift in the query mix where we are presented with MAX_CACHED_RES
   * never-before-seen items used circularly.  We ought to be able to handle
   * that case, so we have to insert at the front.)
   *
   * Knuth mentions a variant strategy in which a used item is moved up just
   * one place in the list.  Although he says this uses fewer comparisons on
   * average, it seems not to adapt very well to the situation where you have
   * both some reusable patterns and a steady stream of non-reusable patterns.
   * A reusable pattern that isn't used at least as often as non-reusable
   * patterns are seen will "fail to keep up" and will drop off the end of the
   * cache.  With move-to-front, a reusable pattern is guaranteed to stay in
   * the cache as long as it's used at least once in every MAX_CACHED_RES uses.
   */
  
  /* this is the maximum number of cached regular expressions */
  #ifndef MAX_CACHED_RES
  #define MAX_CACHED_RES	32
  #endif
  
  /* this structure describes one cached regular expression */
  typedef struct cached_re_str
  {
  	char	   *cre_pat;		/* original RE (not null terminated!) */
  	int			cre_pat_len;	/* length of original RE, in bytes */
  	int			cre_flags;		/* compile flags: extended,icase etc */
  	regex_t		cre_re;			/* the compiled regular expression */
  } cached_re_str;
  
  static int	num_res = 0;		/* # of cached re's */
  static cached_re_str re_array[MAX_CACHED_RES];	/* cached re's */
  
  
  /* Local functions */
  static regexp_matches_ctx *setup_regexp_matches(text *orig_str, text *pattern,
  					 text *flags,
  					 bool force_glob,
  					 bool use_subpatterns,
  					 bool ignore_degenerate);
  static void cleanup_regexp_matches(regexp_matches_ctx *matchctx);
  static ArrayType *build_regexp_matches_result(regexp_matches_ctx *matchctx);
  static Datum build_regexp_split_result(regexp_matches_ctx *splitctx);
  
  
  /*
   * RE_compile_and_cache - compile a RE, caching if possible
   *
   * Returns regex_t *
   *
   *	text_re --- the pattern, expressed as a TEXT object
   *	cflags --- compile options for the pattern
   *
   * Pattern is given in the database encoding.  We internally convert to
   * an array of pg_wchar, which is what Spencer's regex package wants.
   */
  static regex_t *
  RE_compile_and_cache(text *text_re, int cflags)
  {
  	int			text_re_len = VARSIZE_ANY_EXHDR(text_re);
  	char	   *text_re_val = VARDATA_ANY(text_re);
  	pg_wchar   *pattern;
  	int			pattern_len;
  	int			i;
  	int			regcomp_result;
  	cached_re_str re_temp;
  	char		errMsg[100];
  
  	/*
  	 * Look for a match among previously compiled REs.	Since the data
  	 * structure is self-organizing with most-used entries at the front, our
  	 * search strategy can just be to scan from the front.
  	 */
  	for (i = 0; i < num_res; i++)
  	{
  		if (re_array[i].cre_pat_len == text_re_len &&
  			re_array[i].cre_flags == cflags &&
  			memcmp(re_array[i].cre_pat, text_re_val, text_re_len) == 0)
  		{
  			/*
  			 * Found a match; move it to front if not there already.
  			 */
  			if (i > 0)
  			{
  				re_temp = re_array[i];
  				memmove(&re_array[1], &re_array[0], i * sizeof(cached_re_str));
  				re_array[0] = re_temp;
  			}
  
  			return &re_array[0].cre_re;
  		}
  	}
  
  	/*
  	 * Couldn't find it, so try to compile the new RE.  To avoid leaking
  	 * resources on failure, we build into the re_temp local.
  	 */
  
  	/* Convert pattern string to wide characters */
  	pattern = (pg_wchar *) palloc((text_re_len + 1) * sizeof(pg_wchar));
  	pattern_len = pg_mb2wchar_with_len(text_re_val,
  									   pattern,
  									   text_re_len);
  
  	regcomp_result = pg_regcomp(&re_temp.cre_re,
  								pattern,
  								pattern_len,
  								cflags);
  
  	pfree(pattern);
  
  	if (regcomp_result != REG_OKAY)
  	{
  		/* re didn't compile */
  		pg_regerror(regcomp_result, &re_temp.cre_re, errMsg, sizeof(errMsg));
  		/* XXX should we pg_regfree here? */
  		ereport(ERROR,
  				(errcode(ERRCODE_INVALID_REGULAR_EXPRESSION),
  				 errmsg("invalid regular expression: %s", errMsg)));
  	}
  
  	/*
  	 * We use malloc/free for the cre_pat field because the storage has to
  	 * persist across transactions, and because we want to get control back on
  	 * out-of-memory.  The Max() is because some malloc implementations return
  	 * NULL for malloc(0).
  	 */
  	re_temp.cre_pat = malloc(Max(text_re_len, 1));
  	if (re_temp.cre_pat == NULL)
  	{
  		pg_regfree(&re_temp.cre_re);
  		ereport(ERROR,
  				(errcode(ERRCODE_OUT_OF_MEMORY),
  				 errmsg("out of memory")));
  	}
  	memcpy(re_temp.cre_pat, text_re_val, text_re_len);
  	re_temp.cre_pat_len = text_re_len;
  	re_temp.cre_flags = cflags;
  
  	/*
  	 * Okay, we have a valid new item in re_temp; insert it into the storage
  	 * array.  Discard last entry if needed.
  	 */
  	if (num_res >= MAX_CACHED_RES)
  	{
  		--num_res;
  		Assert(num_res < MAX_CACHED_RES);
  		pg_regfree(&re_array[num_res].cre_re);
  		free(re_array[num_res].cre_pat);
  	}
  
  	if (num_res > 0)
  		memmove(&re_array[1], &re_array[0], num_res * sizeof(cached_re_str));
  
  	re_array[0] = re_temp;
  	num_res++;
  
  	return &re_array[0].cre_re;
  }
  
  /*
   * RE_wchar_execute - execute a RE on pg_wchar data
   *
   * Returns TRUE on match, FALSE on no match
   *
   *	re --- the compiled pattern as returned by RE_compile_and_cache
   *	data --- the data to match against (need not be null-terminated)
   *	data_len --- the length of the data string
   *	start_search -- the offset in the data to start searching
   *	nmatch, pmatch	--- optional return area for match details
   *
   * Data is given as array of pg_wchar which is what Spencer's regex package
   * wants.
   */
  static bool
  RE_wchar_execute(regex_t *re, pg_wchar *data, int data_len,
  				 int start_search, int nmatch, regmatch_t *pmatch)
  {
  	int			regexec_result;
  	char		errMsg[100];
  
  	/* Perform RE match and return result */
  	regexec_result = pg_regexec(re,
  								data,
  								data_len,
  								start_search,
  								NULL,	/* no details */
  								nmatch,
  								pmatch,
  								0);
  
  	if (regexec_result != REG_OKAY && regexec_result != REG_NOMATCH)
  	{
  		/* re failed??? */
  		pg_regerror(regexec_result, re, errMsg, sizeof(errMsg));
  		ereport(ERROR,
  				(errcode(ERRCODE_INVALID_REGULAR_EXPRESSION),
  				 errmsg("regular expression failed: %s", errMsg)));
  	}
  
  	return (regexec_result == REG_OKAY);
  }
  
  /*
   * RE_execute - execute a RE
   *
   * Returns TRUE on match, FALSE on no match
   *
   *	re --- the compiled pattern as returned by RE_compile_and_cache
   *	dat --- the data to match against (need not be null-terminated)
   *	dat_len --- the length of the data string
   *	nmatch, pmatch	--- optional return area for match details
   *
   * Data is given in the database encoding.	We internally
   * convert to array of pg_wchar which is what Spencer's regex package wants.
   */
  static bool
  RE_execute(regex_t *re, char *dat, int dat_len,
  		   int nmatch, regmatch_t *pmatch)
  {
  	pg_wchar   *data;
  	int			data_len;
  	bool		match;
  
  	/* Convert data string to wide characters */
  	data = (pg_wchar *) palloc((dat_len + 1) * sizeof(pg_wchar));
  	data_len = pg_mb2wchar_with_len(dat, data, dat_len);
  
  	/* Perform RE match and return result */
  	match = RE_wchar_execute(re, data, data_len, 0, nmatch, pmatch);
  
  	pfree(data);
  	return match;
  }
  
  /*
   * RE_compile_and_execute - compile and execute a RE
   *
   * Returns TRUE on match, FALSE on no match
   *
   *	text_re --- the pattern, expressed as a TEXT object
   *	dat --- the data to match against (need not be null-terminated)
   *	dat_len --- the length of the data string
   *	cflags --- compile options for the pattern
   *	nmatch, pmatch	--- optional return area for match details
   *
   * Both pattern and data are given in the database encoding.  We internally
   * convert to array of pg_wchar which is what Spencer's regex package wants.
   */
  static bool
  RE_compile_and_execute(text *text_re, char *dat, int dat_len,
  					   int cflags, int nmatch, regmatch_t *pmatch)
  {
  	regex_t    *re;
  
  	/* Compile RE */
  	re = RE_compile_and_cache(text_re, cflags);
  
  	return RE_execute(re, dat, dat_len, nmatch, pmatch);
  }
  
  
  /*
   * parse_re_flags - parse the options argument of regexp_matches and friends
   *
   *	flags --- output argument, filled with desired options
   *	opts --- TEXT object, or NULL for defaults
   *
   * This accepts all the options allowed by any of the callers; callers that
   * don't want some have to reject them after the fact.
   */
  static void
  parse_re_flags(pg_re_flags *flags, text *opts)
  {
  	/* regex_flavor is always folded into the compile flags */
  	flags->cflags = regex_flavor;
  	flags->glob = false;
  
  	if (opts)
  	{
  		char	   *opt_p = VARDATA_ANY(opts);
  		int			opt_len = VARSIZE_ANY_EXHDR(opts);
  		int			i;
  
  		for (i = 0; i < opt_len; i++)
  		{
  			switch (opt_p[i])
  			{
  				case 'g':
  					flags->glob = true;
  					break;
  				case 'b':		/* BREs (but why???) */
  					flags->cflags &= ~(REG_ADVANCED | REG_EXTENDED | REG_QUOTE);
  					break;
  				case 'c':		/* case sensitive */
  					flags->cflags &= ~REG_ICASE;
  					break;
  				case 'e':		/* plain EREs */
  					flags->cflags |= REG_EXTENDED;
  					flags->cflags &= ~(REG_ADVANCED | REG_QUOTE);
  					break;
  				case 'i':		/* case insensitive */
  					flags->cflags |= REG_ICASE;
  					break;
  				case 'm':		/* Perloid synonym for n */
  				case 'n':		/* \n affects ^ $ . [^ */
  					flags->cflags |= REG_NEWLINE;
  					break;
  				case 'p':		/* ~Perl, \n affects . [^ */
  					flags->cflags |= REG_NLSTOP;
  					flags->cflags &= ~REG_NLANCH;
  					break;
  				case 'q':		/* literal string */
  					flags->cflags |= REG_QUOTE;
  					flags->cflags &= ~(REG_ADVANCED | REG_EXTENDED);
  					break;
  				case 's':		/* single line, \n ordinary */
  					flags->cflags &= ~REG_NEWLINE;
  					break;
  				case 't':		/* tight syntax */
  					flags->cflags &= ~REG_EXPANDED;
  					break;
  				case 'w':		/* weird, \n affects ^ $ only */
  					flags->cflags &= ~REG_NLSTOP;
  					flags->cflags |= REG_NLANCH;
  					break;
  				case 'x':		/* expanded syntax */
  					flags->cflags |= REG_EXPANDED;
  					break;
  				default:
  					ereport(ERROR,
  							(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
  							 errmsg("invalid regexp option: \"%c\"",
  									opt_p[i])));
  					break;
  			}
  		}
  	}
  }
  
  
  /*
   * assign_regex_flavor - GUC hook to validate and set REGEX_FLAVOR
   */
  const char *
  assign_regex_flavor(const char *value, bool doit, GucSource source)
  {
  	if (pg_strcasecmp(value, "advanced") == 0)
  	{
  		if (doit)
  			regex_flavor = REG_ADVANCED;
  	}
  	else if (pg_strcasecmp(value, "extended") == 0)
  	{
  		if (doit)
  			regex_flavor = REG_EXTENDED;
  	}
  	else if (pg_strcasecmp(value, "basic") == 0)
  	{
  		if (doit)
  			regex_flavor = REG_BASIC;
  	}
  	else
  		return NULL;			/* fail */
  	return value;				/* OK */
  }
  
  
  /*
   * report whether regex_flavor is currently BASIC
   */
  bool
  regex_flavor_is_basic(void)
  {
  	return (regex_flavor == REG_BASIC);
  }
  
  
  /*
   *	interface routines called by the function manager
   */
  
  Datum
  nameregexeq(PG_FUNCTION_ARGS)
  {
  	Name		n = PG_GETARG_NAME(0);
  	text	   *p = PG_GETARG_TEXT_PP(1);
  
  	PG_RETURN_BOOL(RE_compile_and_execute(p,
  										  NameStr(*n),
  										  strlen(NameStr(*n)),
  										  regex_flavor,
  										  0, NULL));
  }
  
  Datum
  nameregexne(PG_FUNCTION_ARGS)
  {
  	Name		n = PG_GETARG_NAME(0);
  	text	   *p = PG_GETARG_TEXT_PP(1);
  
  	PG_RETURN_BOOL(!RE_compile_and_execute(p,
  										   NameStr(*n),
  										   strlen(NameStr(*n)),
  										   regex_flavor,
  										   0, NULL));
  }
  
  Datum
  textregexeq(PG_FUNCTION_ARGS)
  {
  	text	   *s = PG_GETARG_TEXT_PP(0);
  	text	   *p = PG_GETARG_TEXT_PP(1);
  
  	PG_RETURN_BOOL(RE_compile_and_execute(p,
  										  VARDATA_ANY(s),
  										  VARSIZE_ANY_EXHDR(s),
  										  regex_flavor,
  										  0, NULL));
  }
  
  Datum
  textregexne(PG_FUNCTION_ARGS)
  {
  	text	   *s = PG_GETARG_TEXT_PP(0);
  	text	   *p = PG_GETARG_TEXT_PP(1);
  
  	PG_RETURN_BOOL(!RE_compile_and_execute(p,
  										   VARDATA_ANY(s),
  										   VARSIZE_ANY_EXHDR(s),
  										   regex_flavor,
  										   0, NULL));
  }
  
  
  /*
   *	routines that use the regexp stuff, but ignore the case.
   *	for this, we use the REG_ICASE flag to pg_regcomp
   */
  
  
  Datum
  nameicregexeq(PG_FUNCTION_ARGS)
  {
  	Name		n = PG_GETARG_NAME(0);
  	text	   *p = PG_GETARG_TEXT_PP(1);
  
  	PG_RETURN_BOOL(RE_compile_and_execute(p,
  										  NameStr(*n),
  										  strlen(NameStr(*n)),
  										  regex_flavor | REG_ICASE,
  										  0, NULL));
  }
  
  Datum
  nameicregexne(PG_FUNCTION_ARGS)
  {
  	Name		n = PG_GETARG_NAME(0);
  	text	   *p = PG_GETARG_TEXT_PP(1);
  
  	PG_RETURN_BOOL(!RE_compile_and_execute(p,
  										   NameStr(*n),
  										   strlen(NameStr(*n)),
  										   regex_flavor | REG_ICASE,
  										   0, NULL));
  }
  
  Datum
  texticregexeq(PG_FUNCTION_ARGS)
  {
  	text	   *s = PG_GETARG_TEXT_PP(0);
  	text	   *p = PG_GETARG_TEXT_PP(1);
  
  	PG_RETURN_BOOL(RE_compile_and_execute(p,
  										  VARDATA_ANY(s),
  										  VARSIZE_ANY_EXHDR(s),
  										  regex_flavor | REG_ICASE,
  										  0, NULL));
  }
  
  Datum
  texticregexne(PG_FUNCTION_ARGS)
  {
  	text	   *s = PG_GETARG_TEXT_PP(0);
  	text	   *p = PG_GETARG_TEXT_PP(1);
  
  	PG_RETURN_BOOL(!RE_compile_and_execute(p,
  										   VARDATA_ANY(s),
  										   VARSIZE_ANY_EXHDR(s),
  										   regex_flavor | REG_ICASE,
  										   0, NULL));
  }
  
  
  /*
   * textregexsubstr()
   *		Return a substring matched by a regular expression.
   */
  Datum
  textregexsubstr(PG_FUNCTION_ARGS)
  {
  	text	   *s = PG_GETARG_TEXT_PP(0);
  	text	   *p = PG_GETARG_TEXT_PP(1);
  	bool		match;
  	regmatch_t	pmatch[2];
  
  	/*
  	 * We pass two regmatch_t structs to get info about the overall match and
  	 * the match for the first parenthesized subexpression (if any). If there
  	 * is a parenthesized subexpression, we return what it matched; else
  	 * return what the whole regexp matched.
  	 */
  	match = RE_compile_and_execute(p,
  								   VARDATA_ANY(s),
  								   VARSIZE_ANY_EXHDR(s),
  								   regex_flavor,
  								   2, pmatch);
  
  	/* match? then return the substring matching the pattern */
  	if (match)
  	{
  		int			so,
  					eo;
  
  		so = pmatch[1].rm_so;
  		eo = pmatch[1].rm_eo;
  		if (so < 0 || eo < 0)
  		{
  			/* no parenthesized subexpression */
  			so = pmatch[0].rm_so;
  			eo = pmatch[0].rm_eo;
  		}
  
  		return DirectFunctionCall3(text_substr,
  								   PointerGetDatum(s),
  								   Int32GetDatum(so + 1),
  								   Int32GetDatum(eo - so));
  	}
  
  	PG_RETURN_NULL();
  }
  
  /*
   * textregexreplace_noopt()
   *		Return a string matched by a regular expression, with replacement.
   *
   * This version doesn't have an option argument: we default to case
   * sensitive match, replace the first instance only.
   */
  Datum
  textregexreplace_noopt(PG_FUNCTION_ARGS)
  {
  	text	   *s = PG_GETARG_TEXT_PP(0);
  	text	   *p = PG_GETARG_TEXT_PP(1);
  	text	   *r = PG_GETARG_TEXT_PP(2);
  	regex_t    *re;
  
  	re = RE_compile_and_cache(p, regex_flavor);
  
  	PG_RETURN_TEXT_P(replace_text_regexp(s, (void *) re, r, false));
  }
  
  /*
   * textregexreplace()
   *		Return a string matched by a regular expression, with replacement.
   */
  Datum
  textregexreplace(PG_FUNCTION_ARGS)
  {
  	text	   *s = PG_GETARG_TEXT_PP(0);
  	text	   *p = PG_GETARG_TEXT_PP(1);
  	text	   *r = PG_GETARG_TEXT_PP(2);
  	text	   *opt = PG_GETARG_TEXT_PP(3);
  	regex_t    *re;
  	pg_re_flags flags;
  
  	parse_re_flags(&flags, opt);
  
  	re = RE_compile_and_cache(p, flags.cflags);
  
  	PG_RETURN_TEXT_P(replace_text_regexp(s, (void *) re, r, flags.glob));
  }
  
  /*
   * similar_escape()
   * Convert a SQL99 regexp pattern to POSIX style, so it can be used by
   * our regexp engine.
   */
  Datum
  similar_escape(PG_FUNCTION_ARGS)
  {
  	text	   *pat_text;
  	text	   *esc_text;
  	text	   *result;
  	char	   *p,
  			   *e,
  			   *r;
  	int			plen,
  				elen;
  	bool		afterescape = false;
  	int			nquotes = 0;
  
  	/* This function is not strict, so must test explicitly */
  	if (PG_ARGISNULL(0))
  		PG_RETURN_NULL();
  	pat_text = PG_GETARG_TEXT_PP(0);
  	p = VARDATA_ANY(pat_text);
  	plen = VARSIZE_ANY_EXHDR(pat_text);
  	if (PG_ARGISNULL(1))
  	{
  		/* No ESCAPE clause provided; default to backslash as escape */
  		e = "\\";
  		elen = 1;
  	}
  	else
  	{
  		esc_text = PG_GETARG_TEXT_PP(1);
  		e = VARDATA_ANY(esc_text);
  		elen = VARSIZE_ANY_EXHDR(esc_text);
  		if (elen == 0)
  			e = NULL;			/* no escape character */
  		else if (elen != 1)
  			ereport(ERROR,
  					(errcode(ERRCODE_INVALID_ESCAPE_SEQUENCE),
  					 errmsg("invalid escape string"),
  				  errhint("Escape string must be empty or one character.")));
  	}
  
  	/*----------
  	 * We surround the transformed input string with
  	 *			***:^(?: ... )$
  	 * which is bizarre enough to require some explanation.  "***:" is a
  	 * director prefix to force the regex to be treated as an ARE regardless
  	 * of the current regex_flavor setting.  We need "^" and "$" to force
  	 * the pattern to match the entire input string as per SQL99 spec.	The
  	 * "(?:" and ")" are a non-capturing set of parens; we have to have
  	 * parens in case the string contains "|", else the "^" and "$" will
  	 * be bound into the first and last alternatives which is not what we
  	 * want, and the parens must be non capturing because we don't want them
  	 * to count when selecting output for SUBSTRING.
  	 *----------
  	 */
  
  	/*
  	 * We need room for the prefix/postfix plus as many as 2 output bytes per
  	 * input byte
  	 */
  	result = (text *) palloc(VARHDRSZ + 10 + 2 * plen);
  	r = VARDATA(result);
  
  	*r++ = '*';
  	*r++ = '*';
  	*r++ = '*';
  	*r++ = ':';
  	*r++ = '^';
  	*r++ = '(';
  	*r++ = '?';
  	*r++ = ':';
  
  	while (plen > 0)
  	{
  		char		pchar = *p;
  
  		if (afterescape)
  		{
  			if (pchar == '"')	/* for SUBSTRING patterns */
  				*r++ = ((nquotes++ % 2) == 0) ? '(' : ')';
  			else
  			{
  				*r++ = '\\';
  				*r++ = pchar;
  			}
  			afterescape = false;
  		}
  		else if (e && pchar == *e)
  		{
  			/* SQL99 escape character; do not send to output */
  			afterescape = true;
  		}
  		else if (pchar == '%')
  		{
  			*r++ = '.';
  			*r++ = '*';
  		}
  		else if (pchar == '_')
  			*r++ = '.';
  		else if (pchar == '\\' || pchar == '.' || pchar == '?' ||
  				 pchar == '{')
  		{
  			*r++ = '\\';
  			*r++ = pchar;
  		}
  		else
  			*r++ = pchar;
  		p++, plen--;
  	}
  
  	*r++ = ')';
  	*r++ = '$';
  
  	SET_VARSIZE(result, r - ((char *) result));
  
  	PG_RETURN_TEXT_P(result);
  }
  
  /*
   * regexp_matches()
   *		Return a table of matches of a pattern within a string.
   */
  Datum
  regexp_matches(PG_FUNCTION_ARGS)
  {
  	FuncCallContext *funcctx;
  	regexp_matches_ctx *matchctx;
  
  	if (SRF_IS_FIRSTCALL())
  	{
  		text	   *pattern = PG_GETARG_TEXT_PP(1);
  		text	   *flags = PG_GETARG_TEXT_PP_IF_EXISTS(2);
  		MemoryContext oldcontext;
  
  		funcctx = SRF_FIRSTCALL_INIT();
  		oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
  
  		/* be sure to copy the input string into the multi-call ctx */
  		matchctx = setup_regexp_matches(PG_GETARG_TEXT_P_COPY(0), pattern,
  										flags, false, true, false);
  
  		/* Pre-create workspace that build_regexp_matches_result needs */
  		matchctx->elems = (Datum *) palloc(sizeof(Datum) * matchctx->npatterns);
  		matchctx->nulls = (bool *) palloc(sizeof(bool) * matchctx->npatterns);
  
  		MemoryContextSwitchTo(oldcontext);
  		funcctx->user_fctx = (void *) matchctx;
  	}
  
  	funcctx = SRF_PERCALL_SETUP();
  	matchctx = (regexp_matches_ctx *) funcctx->user_fctx;
  
  	if (matchctx->next_match < matchctx->nmatches)
  	{
  		ArrayType  *result_ary;
  
  		result_ary = build_regexp_matches_result(matchctx);
  		matchctx->next_match++;
  		SRF_RETURN_NEXT(funcctx, PointerGetDatum(result_ary));
  	}
  
  	/* release space in multi-call ctx to avoid intraquery memory leak */
  	cleanup_regexp_matches(matchctx);
  
  	SRF_RETURN_DONE(funcctx);
  }
  
  /* This is separate to keep the opr_sanity regression test from complaining */
  Datum
  regexp_matches_no_flags(PG_FUNCTION_ARGS)
  {
  	return regexp_matches(fcinfo);
  }
  
  /*
   * setup_regexp_matches --- do the initial matching for regexp_matches()
   *		or regexp_split()
   *
   * To avoid having to re-find the compiled pattern on each call, we do
   * all the matching in one swoop.  The returned regexp_matches_ctx contains
   * the locations of all the substrings matching the pattern.
   *
   * The three bool parameters have only two patterns (one for each caller)
   * but it seems clearer to distinguish the functionality this way than to
   * key it all off one "is_split" flag.
   */
  static regexp_matches_ctx *
  setup_regexp_matches(text *orig_str, text *pattern, text *flags,
  					 bool force_glob, bool use_subpatterns,
  					 bool ignore_degenerate)
  {
  	regexp_matches_ctx *matchctx = palloc0(sizeof(regexp_matches_ctx));
  	int			orig_len;
  	pg_wchar   *wide_str;
  	int			wide_len;
  	pg_re_flags re_flags;
  	regex_t    *cpattern;
  	regmatch_t *pmatch;
  	int			pmatch_len;
  	int			array_len;
  	int			array_idx;
  	int			prev_match_end;
  	int			start_search;
  
  	/* save original string --- we'll extract result substrings from it */
  	matchctx->orig_str = orig_str;
  
  	/* convert string to pg_wchar form for matching */
  	orig_len = VARSIZE_ANY_EXHDR(orig_str);
  	wide_str = (pg_wchar *) palloc(sizeof(pg_wchar) * (orig_len + 1));
  	wide_len = pg_mb2wchar_with_len(VARDATA_ANY(orig_str), wide_str, orig_len);
  
  	/* determine options */
  	parse_re_flags(&re_flags, flags);
  	if (force_glob)
  	{
  		/* user mustn't specify 'g' for regexp_split */
  		if (re_flags.glob)
  			ereport(ERROR,
  					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
  				 errmsg("regexp_split does not support the global option")));
  		/* but we find all the matches anyway */
  		re_flags.glob = true;
  	}
  
  	/* set up the compiled pattern */
  	cpattern = RE_compile_and_cache(pattern, re_flags.cflags);
  
  	/* do we want to remember subpatterns? */
  	if (use_subpatterns && cpattern->re_nsub > 0)
  	{
  		matchctx->npatterns = cpattern->re_nsub;
  		pmatch_len = cpattern->re_nsub + 1;
  	}
  	else
  	{
  		use_subpatterns = false;
  		matchctx->npatterns = 1;
  		pmatch_len = 1;
  	}
  
  	/* temporary output space for RE package */
  	pmatch = palloc(sizeof(regmatch_t) * pmatch_len);
  
  	/* the real output space (grown dynamically if needed) */
  	array_len = re_flags.glob ? 256 : 32;
  	matchctx->match_locs = (int *) palloc(sizeof(int) * array_len);
  	array_idx = 0;
  
  	/* search for the pattern, perhaps repeatedly */
  	prev_match_end = 0;
  	start_search = 0;
  	while (RE_wchar_execute(cpattern, wide_str, wide_len, start_search,
  							pmatch_len, pmatch))
  	{
  		/*
  		 * If requested, ignore degenerate matches, which are zero-length
  		 * matches occurring at the start or end of a string or just after a
  		 * previous match.
  		 */
  		if (!ignore_degenerate ||
  			(pmatch[0].rm_so < wide_len &&
  			 pmatch[0].rm_eo > prev_match_end))
  		{
  			/* enlarge output space if needed */
  			while (array_idx + matchctx->npatterns * 2 > array_len)
  			{
  				array_len *= 2;
  				matchctx->match_locs = (int *) repalloc(matchctx->match_locs,
  													sizeof(int) * array_len);
  			}
  
  			/* save this match's locations */
  			if (use_subpatterns)
  			{
  				int			i;
  
  				for (i = 1; i <= matchctx->npatterns; i++)
  				{
  					matchctx->match_locs[array_idx++] = pmatch[i].rm_so;
  					matchctx->match_locs[array_idx++] = pmatch[i].rm_eo;
  				}
  			}
  			else
  			{
  				matchctx->match_locs[array_idx++] = pmatch[0].rm_so;
  				matchctx->match_locs[array_idx++] = pmatch[0].rm_eo;
  			}
  			matchctx->nmatches++;
  		}
  		prev_match_end = pmatch[0].rm_eo;
  
  		/* if not glob, stop after one match */
  		if (!re_flags.glob)
  			break;
  
  		/*
  		 * Advance search position.  Normally we start just after the end of
  		 * the previous match, but always advance at least one character (the
  		 * special case can occur if the pattern matches zero characters just
  		 * after the prior match or at the end of the string).
  		 */
  		if (start_search < pmatch[0].rm_eo)
  			start_search = pmatch[0].rm_eo;
  		else
  			start_search++;
  		if (start_search > wide_len)
  			break;
  	}
  
  	/* Clean up temp storage */
  	pfree(wide_str);
  	pfree(pmatch);
  
  	return matchctx;
  }
  
  /*
   * cleanup_regexp_matches - release memory of a regexp_matches_ctx
   */
  static void
  cleanup_regexp_matches(regexp_matches_ctx *matchctx)
  {
  	pfree(matchctx->orig_str);
  	pfree(matchctx->match_locs);
  	if (matchctx->elems)
  		pfree(matchctx->elems);
  	if (matchctx->nulls)
  		pfree(matchctx->nulls);
  	pfree(matchctx);
  }
  
  /*
   * build_regexp_matches_result - build output array for current match
   */
  static ArrayType *
  build_regexp_matches_result(regexp_matches_ctx *matchctx)
  {
  	Datum	   *elems = matchctx->elems;
  	bool	   *nulls = matchctx->nulls;
  	int			dims[1];
  	int			lbs[1];
  	int			loc;
  	int			i;
  
  	/* Extract matching substrings from the original string */
  	loc = matchctx->next_match * matchctx->npatterns * 2;
  	for (i = 0; i < matchctx->npatterns; i++)
  	{
  		int			so = matchctx->match_locs[loc++];
  		int			eo = matchctx->match_locs[loc++];
  
  		if (so < 0 || eo < 0)
  		{
  			elems[i] = (Datum) 0;
  			nulls[i] = true;
  		}
  		else
  		{
  			elems[i] = DirectFunctionCall3(text_substr,
  										 PointerGetDatum(matchctx->orig_str),
  										   Int32GetDatum(so + 1),
  										   Int32GetDatum(eo - so));
  			nulls[i] = false;
  		}
  	}
  
  	/* And form an array */
  	dims[0] = matchctx->npatterns;
  	lbs[0] = 1;
  	/* XXX: this hardcodes assumptions about the text type */
  	return construct_md_array(elems, nulls, 1, dims, lbs,
  							  TEXTOID, -1, false, 'i');
  }
  
  /*
   * regexp_split_to_table()
   *		Split the string at matches of the pattern, returning the
   *		split-out substrings as a table.
   */
  Datum
  regexp_split_to_table(PG_FUNCTION_ARGS)
  {
  	FuncCallContext *funcctx;
  	regexp_matches_ctx *splitctx;
  
  	if (SRF_IS_FIRSTCALL())
  	{
  		text	   *pattern = PG_GETARG_TEXT_PP(1);
  		text	   *flags = PG_GETARG_TEXT_PP_IF_EXISTS(2);
  		MemoryContext oldcontext;
  
  		funcctx = SRF_FIRSTCALL_INIT();
  		oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
  
  		/* be sure to copy the input string into the multi-call ctx */
  		splitctx = setup_regexp_matches(PG_GETARG_TEXT_P_COPY(0), pattern,
  										flags, true, false, true);
  
  		MemoryContextSwitchTo(oldcontext);
  		funcctx->user_fctx = (void *) splitctx;
  	}
  
  	funcctx = SRF_PERCALL_SETUP();
  	splitctx = (regexp_matches_ctx *) funcctx->user_fctx;
  
  	if (splitctx->next_match <= splitctx->nmatches)
  	{
  		Datum		result = build_regexp_split_result(splitctx);
  
  		splitctx->next_match++;
  		SRF_RETURN_NEXT(funcctx, result);
  	}
  
  	/* release space in multi-call ctx to avoid intraquery memory leak */
  	cleanup_regexp_matches(splitctx);
  
  	SRF_RETURN_DONE(funcctx);
  }
  
  /* This is separate to keep the opr_sanity regression test from complaining */
  Datum
  regexp_split_to_table_no_flags(PG_FUNCTION_ARGS)
  {
  	return regexp_split_to_table(fcinfo);
  }
  
  /*
   * regexp_split_to_array()
   *		Split the string at matches of the pattern, returning the
   *		split-out substrings as an array.
   */
  Datum
  regexp_split_to_array(PG_FUNCTION_ARGS)
  {
  	ArrayBuildState *astate = NULL;
  	regexp_matches_ctx *splitctx;
  
  	splitctx = setup_regexp_matches(PG_GETARG_TEXT_PP(0),
  									PG_GETARG_TEXT_PP(1),
  									PG_GETARG_TEXT_PP_IF_EXISTS(2),
  									true, false, true);
  
  	while (splitctx->next_match <= splitctx->nmatches)
  	{
  		astate = accumArrayResult(astate,
  								  build_regexp_split_result(splitctx),
  								  false,
  								  TEXTOID,
  								  CurrentMemoryContext);
  		splitctx->next_match++;
  	}
  
  	/*
  	 * We don't call cleanup_regexp_matches here; it would try to pfree the
  	 * input string, which we didn't copy.  The space is not in a long-lived
  	 * memory context anyway.
  	 */
  
  	PG_RETURN_ARRAYTYPE_P(makeArrayResult(astate, CurrentMemoryContext));
  }
  
  /* This is separate to keep the opr_sanity regression test from complaining */
  Datum
  regexp_split_to_array_no_flags(PG_FUNCTION_ARGS)
  {
  	return regexp_split_to_array(fcinfo);
  }
  
  /*
   * build_regexp_split_result - build output string for current match
   *
   * We return the string between the current match and the previous one,
   * or the string after the last match when next_match == nmatches.
   */
  static Datum
  build_regexp_split_result(regexp_matches_ctx *splitctx)
  {
  	int			startpos;
  	int			endpos;
  
  	if (splitctx->next_match > 0)
  		startpos = splitctx->match_locs[splitctx->next_match * 2 - 1];
  	else
  		startpos = 0;
  	if (startpos < 0)
  		elog(ERROR, "invalid match ending position");
  
  	if (splitctx->next_match < splitctx->nmatches)
  	{
  		endpos = splitctx->match_locs[splitctx->next_match * 2];
  		if (endpos < startpos)
  			elog(ERROR, "invalid match starting position");
  		return DirectFunctionCall3(text_substr,
  								   PointerGetDatum(splitctx->orig_str),
  								   Int32GetDatum(startpos + 1),
  								   Int32GetDatum(endpos - startpos));
  	}
  	else
  	{
  		/* no more matches, return rest of string */
  		return DirectFunctionCall2(text_substr_no_len,
  								   PointerGetDatum(splitctx->orig_str),
  								   Int32GetDatum(startpos + 1));
  	}
  }
  