/* all the options of interest for regex functions */ typedefstruct 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_match and regexp_split functions */ typedefstruct 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_match_result() */
Datum *elems; /* has npatterns elements */ bool *nulls; /* has npatterns elements */
pg_wchar *wide_str; /* wide-char version of original string */ char *conv_buf; /* conversion buffer, if needed */ int conv_bufsiz; /* size thereof */
} regexp_matches_ctx;
/* this is the maximum number of cached regular expressions */ #ifndef MAX_CACHED_RES #define MAX_CACHED_RES 32 #endif
/* A parent memory context for regular expressions. */ static MemoryContext RegexpCacheMemoryContext;
/* this structure describes one cached regular expression */ typedefstruct cached_re_str
{
MemoryContext cre_context; /* memory context for this regexp */ 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 */
Oid cre_collation; /* collation to use */
regex_t cre_re; /* the compiled regular expression */
} cached_re_str;
/* Local functions */ static regexp_matches_ctx *setup_regexp_matches(text *orig_str, text *pattern,
pg_re_flags *re_flags, int start_search,
Oid collation, bool use_subpatterns, bool ignore_degenerate, bool fetching_unmatched); static ArrayType *build_regexp_match_result(regexp_matches_ctx *matchctx); static Datum build_regexp_split_result(regexp_matches_ctx *splitctx);
/* *RE_compile_and_cache-compileaRE,cachingifpossible * *Returnsregex_t* * *text_re---thepattern,expressedasaTEXTobject *cflags---compileoptionsforthepattern *collation---collationtouseforLC_CTYPE-dependentbehavior * *Patternisgiveninthedatabaseencoding.Weinternallyconvertto *anarrayofpg_wchar,whichiswhatSpencer'sregexpackagewants.
*/
regex_t *
RE_compile_and_cache(text *text_re, int cflags, Oid collation)
{ 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];
MemoryContext oldcontext;
/* *LookforamatchamongpreviouslycompiledREs.Sincethedata *structureisself-organizingwithmost-usedentriesatthefront,our *searchstrategycanjustbetoscanfromthefront.
*/ for (i = 0; i < num_res; i++)
{ if (re_array[i].cre_pat_len == text_re_len &&
re_array[i].cre_flags == cflags &&
re_array[i].cre_collation == collation &&
memcmp(re_array[i].cre_pat, text_re_val, text_re_len) == 0)
{ /* *Foundamatch;moveittofrontifnottherealready.
*/ 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;
}
}
/* Set up the cache memory on first go through. */ if (unlikely(RegexpCacheMemoryContext == NULL))
RegexpCacheMemoryContext =
AllocSetContextCreate(TopMemoryContext, "RegexpCacheMemoryContext",
ALLOCSET_SMALL_SIZES);
if (regcomp_result != REG_OKAY)
{ /* re didn't compile (no need for pg_regfree, if so) */
pg_regerror(regcomp_result, &re_temp.cre_re, errMsg, sizeof(errMsg));
ereport(ERROR,
(errcode(ERRCODE_INVALID_REGULAR_EXPRESSION),
errmsg("invalid regular expression: %s", errMsg)));
}
/* Copy the pattern into the per-regexp memory context. */
re_temp.cre_pat = palloc(text_re_len + 1);
memcpy(re_temp.cre_pat, text_re_val, text_re_len);
/* *textregexsubstr() *Returnasubstringmatchedbyaregularexpression.
*/
Datum
textregexsubstr(PG_FUNCTION_ARGS)
{
text *s = PG_GETARG_TEXT_PP(0);
text *p = PG_GETARG_TEXT_PP(1);
regex_t *re;
regmatch_t pmatch[2]; int so,
eo;
/* Compile RE */
re = RE_compile_and_cache(p, REG_ADVANCED, PG_GET_COLLATION());
/* *Wepasstworegmatch_tstructstogetinfoabouttheoverallmatchand *thematchforthefirstparenthesizedsubexpression(ifany).Ifthere *isaparenthesizedsubexpression,wereturnwhatitmatched;else *returnwhatthewholeregexpmatched.
*/ if (!RE_execute(re,
VARDATA_ANY(s), VARSIZE_ANY_EXHDR(s), 2, pmatch))
PG_RETURN_NULL(); /* definitely no match */
if (re->re_nsub > 0)
{ /* has parenthesized subexpressions, use the first one */
so = pmatch[1].rm_so;
eo = pmatch[1].rm_eo;
} else
{ /* no parenthesized subexpression, use whole match */
so = pmatch[0].rm_so;
eo = pmatch[0].rm_eo;
}
/* *Itispossibletohaveamatchtothewholepatternbutnomatchfora *subexpression;forexample'foo(bar)?'isconsideredtomatch'foo'but *thereisnosubexpressionmatch.Sothisextratestformatchfailure *isnotredundant.
*/ if (so < 0 || eo < 0)
PG_RETURN_NULL();
/* *textregexreplace() *Returnastringmatchedbyaregularexpression,withreplacement.
*/
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);
pg_re_flags flags;
if (*opt_p >= '0' && *opt_p <= '9')
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("invalid regular expression option: \"%.*s\"",
pg_mblen_range(opt_p, end_p), opt_p),
errhint("If you meant to use regexp_replace() with a start parameter, cast the fourth argument to integer explicitly.")));
}
/* *textregexreplace_extended() *Returnastringmatchedbyaregularexpression,withreplacement. *Extendstextregexreplacebyallowingastartpositionandthe *choiceoftheoccurrencetoreplace(0meansalloccurrences).
*/
Datum
textregexreplace_extended(PG_FUNCTION_ARGS)
{
text *s = PG_GETARG_TEXT_PP(0);
text *p = PG_GETARG_TEXT_PP(1);
text *r = PG_GETARG_TEXT_PP(2); int start = 1; int n = 1;
text *flags = PG_GETARG_TEXT_PP_IF_EXISTS(5);
pg_re_flags re_flags;
/* Collect optional parameters */ if (PG_NARGS() > 3)
{
start = PG_GETARG_INT32(3); if (start <= 0)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("invalid value for parameter \"%s\": %d", "start", start)));
} if (PG_NARGS() > 4)
{
n = PG_GETARG_INT32(4); if (n < 0)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("invalid value for parameter \"%s\": %d", "n", n)));
}
/* If N was not specified, deduce it from the 'g' flag */ if (PG_NARGS() <= 4)
n = re_flags.glob ? 0 : 1;
/* Do the replacement(s) */
PG_RETURN_TEXT_P(replace_text_regexp(s, p, r,
re_flags.cflags, PG_GET_COLLATION(),
start - 1, n));
}
/* This is separate to keep the opr_sanity regression test from complaining */
Datum
textregexreplace_extended_no_n(PG_FUNCTION_ARGS)
{ return textregexreplace_extended(fcinfo);
}
/* This is separate to keep the opr_sanity regression test from complaining */
Datum
textregexreplace_extended_no_flags(PG_FUNCTION_ARGS)
{ return textregexreplace_extended(fcinfo);
}
/* *similar_to_escape(),similar_escape() * *ConvertaSQL"SIMILARTO"regexppatterntoPOSIXstyle,soitcanbe *usedbyourregexpengine. * *similar_escape_internal()isthecommonworkhorseforthreeSQL-exposed *functions.esc_textcanbepassedasNULLtoselectthedefaultescape *(whichis'\'),orasanemptystringtoselectnoescapecharacter.
*/ static text *
similar_escape_internal(text *pat_text, text *esc_text)
{
text *result; char *p,
*e,
*r; int plen,
elen; constchar *pend; bool afterescape = false; int nquotes = 0; int bracket_depth = 0; /* square bracket nesting level */ int charclass_pos = 0; /* position inside a character class */
p = VARDATA_ANY(pat_text);
plen = VARSIZE_ANY_EXHDR(pat_text);
pend = p + plen; if (esc_text == NULL)
{ /* No ESCAPE clause provided; default to backslash as escape */
e = "\\";
elen = 1;
} else
{
e = VARDATA_ANY(esc_text);
elen = VARSIZE_ANY_EXHDR(esc_text); if (elen == 0)
e = NULL; /* no escape character */ elseif (elen > 1)
{ int escape_mblen = pg_mbstrlen_with_len(e, elen);
if (escape_mblen > 1)
ereport(ERROR,
(errcode(ERRCODE_INVALID_ESCAPE_SEQUENCE),
errmsg("invalid escape string"),
errhint("Escape string must be empty or one character.")));
}
}
/* *Ifweencounteranescapedcharacterinacharacterclass, *wearenolongeratthebeginning.
*/
charclass_pos = 3;
}
afterescape = false;
} elseif (e && pchar == *e)
{ /* SQL escape character; do not send to output */
afterescape = true;
} elseif (bracket_depth > 0)
{ /* inside a character class */ if (pchar == '\\')
{ /* *Ifwe'rehere,backslashisnottheSQLescapecharacter, *sotreatitasaliteralclasselement,whichrequires *doublingit.(Thismatchesourbehaviorforbackslashes *outsidecharacterclasses.)
*/
*r++ = '\\';
}
*r++ = pchar;
/* parse the character class well enough to identify ending ']' */ if (pchar == ']' && charclass_pos > 2)
{ /* found the real end of a bracket pair */
bracket_depth--; /* don't reset charclass_pos, this may be an inner bracket */
} elseif (pchar == '[')
{ /* start of a nested bracket pair */
bracket_depth++;
/* *similar_to_escape(pattern,escape)
*/
Datum
similar_to_escape_2(PG_FUNCTION_ARGS)
{
text *pat_text = PG_GETARG_TEXT_PP(0);
text *esc_text = PG_GETARG_TEXT_PP(1);
text *result;
result = similar_escape_internal(pat_text, esc_text);
PG_RETURN_TEXT_P(result);
}
/* *similar_to_escape(pattern) *Insertsadefaultescapecharacter.
*/
Datum
similar_to_escape_1(PG_FUNCTION_ARGS)
{
text *pat_text = PG_GETARG_TEXT_PP(0);
text *result;
result = similar_escape_internal(pat_text, NULL);
PG_RETURN_TEXT_P(result);
}
/* *similar_escape(pattern,escape) * *Legacyfunctionforcompatibilitywithviewsstoredusingthe *pre-v13expansionofSIMILARTO.Unliketheabovefunctions,this *isnon-strict,whichleadstonot-per-spechandlingof"ESCAPENULL".
*/
Datum
similar_escape(PG_FUNCTION_ARGS)
{
text *pat_text;
text *esc_text;
text *result;
/* This function is not strict, so must test explicitly */ if (PG_ARGISNULL(0))
PG_RETURN_NULL();
pat_text = PG_GETARG_TEXT_PP(0);
if (PG_ARGISNULL(1))
esc_text = NULL; /* use default escape character */ else
esc_text = PG_GETARG_TEXT_PP(1);
result = similar_escape_internal(pat_text, esc_text);
PG_RETURN_TEXT_P(result);
}
/* *regexp_count() *Returnthenumberofmatchesofapatternwithinastring.
*/
Datum
regexp_count(PG_FUNCTION_ARGS)
{
text *str = PG_GETARG_TEXT_PP(0);
text *pattern = PG_GETARG_TEXT_PP(1); int start = 1;
text *flags = PG_GETARG_TEXT_PP_IF_EXISTS(3);
pg_re_flags re_flags;
regexp_matches_ctx *matchctx;
/* Collect optional parameters */ if (PG_NARGS() > 2)
{
start = PG_GETARG_INT32(2); if (start <= 0)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("invalid value for parameter \"%s\": %d", "start", start)));
}
/* Determine options */
parse_re_flags(&re_flags, flags); /* User mustn't specify 'g' */ if (re_flags.glob)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE), /* translator: %s is a SQL function name */
errmsg("%s does not support the \"global\" option", "regexp_count()"))); /* But we find all the matches anyway */
re_flags.glob = true;
/* Do the matching */
matchctx = setup_regexp_matches(str, pattern, &re_flags, start - 1,
PG_GET_COLLATION(), false, /* can ignore subexprs */ false, false);
PG_RETURN_INT32(matchctx->nmatches);
}
/* This is separate to keep the opr_sanity regression test from complaining */
Datum
regexp_count_no_start(PG_FUNCTION_ARGS)
{ return regexp_count(fcinfo);
}
/* This is separate to keep the opr_sanity regression test from complaining */
Datum
regexp_count_no_flags(PG_FUNCTION_ARGS)
{ return regexp_count(fcinfo);
}
/* *regexp_instr() *Returnthematch'spositionwithinthestring
*/
Datum
regexp_instr(PG_FUNCTION_ARGS)
{
text *str = PG_GETARG_TEXT_PP(0);
text *pattern = PG_GETARG_TEXT_PP(1); int start = 1; int n = 1; int endoption = 0;
text *flags = PG_GETARG_TEXT_PP_IF_EXISTS(5); int subexpr = 0; int pos;
pg_re_flags re_flags;
regexp_matches_ctx *matchctx;
/* Collect optional parameters */ if (PG_NARGS() > 2)
{
start = PG_GETARG_INT32(2); if (start <= 0)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("invalid value for parameter \"%s\": %d", "start", start)));
} if (PG_NARGS() > 3)
{
n = PG_GETARG_INT32(3); if (n <= 0)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("invalid value for parameter \"%s\": %d", "n", n)));
} if (PG_NARGS() > 4)
{
endoption = PG_GETARG_INT32(4); if (endoption != 0 && endoption != 1)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("invalid value for parameter \"%s\": %d", "endoption", endoption)));
} if (PG_NARGS() > 6)
{
subexpr = PG_GETARG_INT32(6); if (subexpr < 0)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("invalid value for parameter \"%s\": %d", "subexpr", subexpr)));
}
/* Determine options */
parse_re_flags(&re_flags, flags); /* User mustn't specify 'g' */ if (re_flags.glob)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE), /* translator: %s is a SQL function name */
errmsg("%s does not support the \"global\" option", "regexp_instr()"))); /* But we find all the matches anyway */
re_flags.glob = true;
/* Do the matching */
matchctx = setup_regexp_matches(str, pattern, &re_flags, start - 1,
PG_GET_COLLATION(),
(subexpr > 0), /* need submatches? */ false, false);
/* When n exceeds matches return 0 (includes case of no matches) */ if (n > matchctx->nmatches)
PG_RETURN_INT32(0);
/* When subexpr exceeds number of subexpressions return 0 */ if (subexpr > matchctx->npatterns)
PG_RETURN_INT32(0);
/* Select the appropriate match position to return */
pos = (n - 1) * matchctx->npatterns; if (subexpr > 0)
pos += subexpr - 1;
pos *= 2; if (endoption == 1)
pos += 1;
if (matchctx->match_locs[pos] >= 0)
PG_RETURN_INT32(matchctx->match_locs[pos] + 1); else
PG_RETURN_INT32(0); /* position not identifiable */
}
/* This is separate to keep the opr_sanity regression test from complaining */
Datum
regexp_instr_no_start(PG_FUNCTION_ARGS)
{ return regexp_instr(fcinfo);
}
/* This is separate to keep the opr_sanity regression test from complaining */
Datum
regexp_instr_no_n(PG_FUNCTION_ARGS)
{ return regexp_instr(fcinfo);
}
/* This is separate to keep the opr_sanity regression test from complaining */
Datum
regexp_instr_no_endoption(PG_FUNCTION_ARGS)
{ return regexp_instr(fcinfo);
}
/* This is separate to keep the opr_sanity regression test from complaining */
Datum
regexp_instr_no_flags(PG_FUNCTION_ARGS)
{ return regexp_instr(fcinfo);
}
/* This is separate to keep the opr_sanity regression test from complaining */
Datum
regexp_instr_no_subexpr(PG_FUNCTION_ARGS)
{ return regexp_instr(fcinfo);
}
/* *regexp_like() *Testforapatternmatchwithinastring.
*/
Datum
regexp_like(PG_FUNCTION_ARGS)
{
text *str = PG_GETARG_TEXT_PP(0);
text *pattern = PG_GETARG_TEXT_PP(1);
text *flags = PG_GETARG_TEXT_PP_IF_EXISTS(2);
pg_re_flags re_flags;
/* Determine options */
parse_re_flags(&re_flags, flags); /* User mustn't specify 'g' */ if (re_flags.glob)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE), /* translator: %s is a SQL function name */
errmsg("%s does not support the \"global\" option", "regexp_like()")));
/* This is separate to keep the opr_sanity regression test from complaining */
Datum
regexp_like_no_flags(PG_FUNCTION_ARGS)
{ return regexp_like(fcinfo);
}
/* *regexp_match() *Returnthefirstsubstring(s)matchingapatternwithinastring.
*/
Datum
regexp_match(PG_FUNCTION_ARGS)
{
text *orig_str = PG_GETARG_TEXT_PP(0);
text *pattern = PG_GETARG_TEXT_PP(1);
text *flags = PG_GETARG_TEXT_PP_IF_EXISTS(2);
pg_re_flags re_flags;
regexp_matches_ctx *matchctx;
/* Determine options */
parse_re_flags(&re_flags, flags); /* User mustn't specify 'g' */ if (re_flags.glob)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE), /* translator: %s is a SQL function name */
errmsg("%s does not support the \"global\" option", "regexp_match()"),
errhint("Use the regexp_matches function instead.")));
/* This is separate to keep the opr_sanity regression test from complaining */
Datum
regexp_match_no_flags(PG_FUNCTION_ARGS)
{ return regexp_match(fcinfo);
}
if (SRF_IS_FIRSTCALL())
{
text *pattern = PG_GETARG_TEXT_PP(1);
text *flags = PG_GETARG_TEXT_PP_IF_EXISTS(2);
pg_re_flags re_flags;
MemoryContext oldcontext;
/* be sure to copy the input string into the multi-call ctx */
matchctx = setup_regexp_matches(PG_GETARG_TEXT_P_COPY(0), pattern,
&re_flags, 0,
PG_GET_COLLATION(), true, false, false);
/* 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---dotheinitialmatchingforregexp_match, *regexp_split,andrelatedfunctions * *Toavoidhavingtore-findthecompiledpatternoneachcall,wedo *allthematchinginoneswoop.Thereturnedregexp_matches_ctxcontains *thelocationsofallthesubstringsmatchingthepattern. * *start_search:thecharacter(notbyte)offsetinorig_stratwhichto *beginthesearch.Returnedpositionsarerelativetoorig_stranyway. *use_subpatterns:collectdataaboutmatchestoparenthesizedsubexpressions. *ignore_degenerate:ignorezero-lengthmatches. *fetching_unmatched:callerwantstofetchunmatchedsubstrings. * *Wedon'tcurrentlyassumethatfetching_unmatchedisexclusiveoffetching *thematchedtexttoo;ifit'sset,theconversionbufferislargeenoughto *fetchanysinglematchedorunmatchedstring,butnotanylarger *substring.(Inpractice,whensplittingthematchesareusuallysmall *anyway,anditdidn'tseemworthcomplicatingthecodefurther.)
*/ static regexp_matches_ctx *
setup_regexp_matches(text *orig_str, text *pattern, pg_re_flags *re_flags, int start_search,
Oid collation, bool use_subpatterns, bool ignore_degenerate, bool fetching_unmatched)
{
regexp_matches_ctx *matchctx = palloc0(sizeof(regexp_matches_ctx)); int eml = pg_database_encoding_max_length(); int orig_len;
pg_wchar *wide_str; int wide_len; int cflags;
regex_t *cpattern;
regmatch_t *pmatch; int pmatch_len; int array_len; int array_idx; int prev_match_end; int prev_valid_match_end; int maxlen = 0; /* largest fetch length in characters */
/* 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);
/* set up the compiled pattern */
cflags = re_flags->cflags; if (!use_subpatterns)
cflags |= REG_NOSUB;
cpattern = RE_compile_and_cache(pattern, cflags, collation);
/* 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);
matchctx->conv_buf = palloc(conv_bufsiz);
matchctx->conv_bufsiz = conv_bufsiz;
matchctx->wide_str = wide_str;
} else
{ /* No need to keep the wide string if we're in a single-byte charset. */
pfree(wide_str);
matchctx->wide_str = NULL;
matchctx->conv_buf = NULL;
matchctx->conv_bufsiz = 0;
}
/* Clean up temp storage */
pfree(pmatch);
return matchctx;
}
/* *build_regexp_match_result-buildoutputarrayforcurrentmatch
*/ static ArrayType *
build_regexp_match_result(regexp_matches_ctx *matchctx)
{ char *buf = matchctx->conv_buf;
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;
} elseif (buf)
{ int len = pg_wchar2mb_with_len(matchctx->wide_str + so,
buf,
eo - so);
/* 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, TYPALIGN_INT);
}
if (SRF_IS_FIRSTCALL())
{
text *pattern = PG_GETARG_TEXT_PP(1);
text *flags = PG_GETARG_TEXT_PP_IF_EXISTS(2);
pg_re_flags re_flags;
MemoryContext oldcontext;
/* Determine options */
parse_re_flags(&re_flags, flags); /* User mustn't specify 'g' */ if (re_flags.glob)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE), /* translator: %s is a SQL function name */
errmsg("%s does not support the \"global\" option", "regexp_split_to_table()"))); /* But we find all the matches anyway */
re_flags.glob = true;
/* be sure to copy the input string into the multi-call ctx */
splitctx = setup_regexp_matches(PG_GETARG_TEXT_P_COPY(0), pattern,
&re_flags, 0,
PG_GET_COLLATION(), false, true, true);
/* 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);
}
/* Determine options */
parse_re_flags(&re_flags, PG_GETARG_TEXT_PP_IF_EXISTS(2)); /* User mustn't specify 'g' */ if (re_flags.glob)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE), /* translator: %s is a SQL function name */
errmsg("%s does not support the \"global\" option", "regexp_split_to_array()"))); /* But we find all the matches anyway */
re_flags.glob = true;
/* 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-buildoutputstringforcurrentmatch * *Wereturnthestringbetweenthecurrentmatchandthepreviousone, *orthestringafterthelastmatchwhennext_match==nmatches.
*/ static Datum
build_regexp_split_result(regexp_matches_ctx *splitctx)
{ char *buf = splitctx->conv_buf; 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");
endpos = splitctx->match_locs[splitctx->next_match * 2]; if (endpos < startpos)
elog(ERROR, "invalid match starting position");
/* *regexp_substr() *Returnthesubstringthatmatchesaregularexpressionpattern
*/
Datum
regexp_substr(PG_FUNCTION_ARGS)
{
text *str = PG_GETARG_TEXT_PP(0);
text *pattern = PG_GETARG_TEXT_PP(1); int start = 1; int n = 1;
text *flags = PG_GETARG_TEXT_PP_IF_EXISTS(4); int subexpr = 0; int so,
eo,
pos;
pg_re_flags re_flags;
regexp_matches_ctx *matchctx;
/* Collect optional parameters */ if (PG_NARGS() > 2)
{
start = PG_GETARG_INT32(2); if (start <= 0)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("invalid value for parameter \"%s\": %d", "start", start)));
} if (PG_NARGS() > 3)
{
n = PG_GETARG_INT32(3); if (n <= 0)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("invalid value for parameter \"%s\": %d", "n", n)));
} if (PG_NARGS() > 5)
{
subexpr = PG_GETARG_INT32(5); if (subexpr < 0)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("invalid value for parameter \"%s\": %d", "subexpr", subexpr)));
}
/* Determine options */
parse_re_flags(&re_flags, flags); /* User mustn't specify 'g' */ if (re_flags.glob)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE), /* translator: %s is a SQL function name */
errmsg("%s does not support the \"global\" option", "regexp_substr()"))); /* But we find all the matches anyway */
re_flags.glob = true;
/* Do the matching */
matchctx = setup_regexp_matches(str, pattern, &re_flags, start - 1,
PG_GET_COLLATION(),
(subexpr > 0), /* need submatches? */ false, false);
/* When n exceeds matches return NULL (includes case of no matches) */ if (n > matchctx->nmatches)
PG_RETURN_NULL();
/* When subexpr exceeds number of subexpressions return NULL */ if (subexpr > matchctx->npatterns)
PG_RETURN_NULL();
/* Select the appropriate match position to return */
pos = (n - 1) * matchctx->npatterns; if (subexpr > 0)
pos += subexpr - 1;
pos *= 2;
so = matchctx->match_locs[pos];
eo = matchctx->match_locs[pos + 1];
if (so < 0 || eo < 0)
PG_RETURN_NULL(); /* unidentifiable location */
/* This is separate to keep the opr_sanity regression test from complaining */
Datum
regexp_substr_no_start(PG_FUNCTION_ARGS)
{ return regexp_substr(fcinfo);
}
/* This is separate to keep the opr_sanity regression test from complaining */
Datum
regexp_substr_no_n(PG_FUNCTION_ARGS)
{ return regexp_substr(fcinfo);
}
/* This is separate to keep the opr_sanity regression test from complaining */
Datum
regexp_substr_no_flags(PG_FUNCTION_ARGS)
{ return regexp_substr(fcinfo);
}
/* This is separate to keep the opr_sanity regression test from complaining */
Datum
regexp_substr_no_subexpr(PG_FUNCTION_ARGS)
{ return regexp_substr(fcinfo);
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.