PostgreSQL Source Code git master
test_regex.c
Go to the documentation of this file.
1/*--------------------------------------------------------------------------
2 *
3 * test_regex.c
4 * Test harness for the regular expression package.
5 *
6 * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
8 *
9 * IDENTIFICATION
10 * src/test/modules/test_regex/test_regex.c
11 *
12 * -------------------------------------------------------------------------
13 */
14
15#include "postgres.h"
16
17#include "funcapi.h"
18#include "regex/regex.h"
19#include "utils/array.h"
20#include "utils/builtins.h"
21
23
24
25/* all the options of interest for regex functions */
26typedef struct test_re_flags
27{
28 int cflags; /* compile flags for Spencer's regex code */
29 int eflags; /* execute flags for Spencer's regex code */
30 long info; /* expected re_info bits */
31 bool glob; /* do it globally (for each occurrence) */
32 bool indices; /* report indices not actual strings */
33 bool partial; /* expect partial match */
35
36/* cross-call state for test_regex() */
37typedef struct test_regex_ctx
38{
40 rm_detail_t details; /* "details" from execution */
41 text *orig_str; /* data string in original TEXT form */
42 int nmatches; /* number of places where pattern matched */
43 int npatterns; /* number of capturing subpatterns */
44 /* We store start char index and end+1 char index for each match */
45 /* so the number of entries in match_locs is nmatches * npatterns * 2 */
46 int *match_locs; /* 0-based character indexes */
47 int next_match; /* 0-based index of next match to process */
48 /* workspace for build_test_match_result() */
49 Datum *elems; /* has npatterns+1 elements */
50 bool *nulls; /* has npatterns+1 elements */
51 pg_wchar *wide_str; /* wide-char version of original string */
52 char *conv_buf; /* conversion buffer, if needed */
53 int conv_bufsiz; /* size thereof */
55
56/* Local functions */
57static void test_re_compile(text *text_re, int cflags, Oid collation,
58 regex_t *result_re);
59static void parse_test_flags(test_re_flags *flags, text *opts);
60static test_regex_ctx *setup_test_matches(text *orig_str,
61 regex_t *cpattern,
62 test_re_flags *re_flags,
63 Oid collation,
64 bool use_subpatterns);
66 test_re_flags *flags);
68
69
70/*
71 * test_regex(pattern text, string text, flags text) returns setof text[]
72 *
73 * This is largely based on regexp.c's regexp_matches, with additions
74 * for debugging purposes.
75 */
77
80{
81 FuncCallContext *funcctx;
82 test_regex_ctx *matchctx;
83 ArrayType *result_ary;
84
85 if (SRF_IS_FIRSTCALL())
86 {
87 text *pattern = PG_GETARG_TEXT_PP(0);
88 text *flags = PG_GETARG_TEXT_PP(2);
89 Oid collation = PG_GET_COLLATION();
90 test_re_flags re_flags;
91 regex_t cpattern;
92 MemoryContext oldcontext;
93
94 funcctx = SRF_FIRSTCALL_INIT();
95 oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
96
97 /* Determine options */
98 parse_test_flags(&re_flags, flags);
99
100 /* set up the compiled pattern */
101 test_re_compile(pattern, re_flags.cflags, collation, &cpattern);
102
103 /* be sure to copy the input string into the multi-call ctx */
104 matchctx = setup_test_matches(PG_GETARG_TEXT_P_COPY(1), &cpattern,
105 &re_flags,
106 collation,
107 true);
108
109 /* Pre-create workspace that build_test_match_result needs */
110 matchctx->elems = palloc_array(Datum, matchctx->npatterns + 1);
111 matchctx->nulls = palloc_array(bool, matchctx->npatterns + 1);
112
113 MemoryContextSwitchTo(oldcontext);
114 funcctx->user_fctx = matchctx;
115
116 /*
117 * Return the first result row, which is info equivalent to Tcl's
118 * "regexp -about" output
119 */
120 result_ary = build_test_info_result(&cpattern, &re_flags);
121
122 pg_regfree(&cpattern);
123
124 SRF_RETURN_NEXT(funcctx, PointerGetDatum(result_ary));
125 }
126 else
127 {
128 /* Each subsequent row describes one match */
129 funcctx = SRF_PERCALL_SETUP();
130 matchctx = (test_regex_ctx *) funcctx->user_fctx;
131
132 if (matchctx->next_match < matchctx->nmatches)
133 {
134 result_ary = build_test_match_result(matchctx);
135 matchctx->next_match++;
136 SRF_RETURN_NEXT(funcctx, PointerGetDatum(result_ary));
137 }
138 }
139
140 SRF_RETURN_DONE(funcctx);
141}
142
143
144/*
145 * test_re_compile - compile a RE
146 *
147 * text_re --- the pattern, expressed as a TEXT object
148 * cflags --- compile options for the pattern
149 * collation --- collation to use for LC_CTYPE-dependent behavior
150 * result_re --- output, compiled RE is stored here
151 *
152 * Pattern is given in the database encoding. We internally convert to
153 * an array of pg_wchar, which is what Spencer's regex package wants.
154 *
155 * Caller must eventually pg_regfree the resulting RE to avoid memory leaks.
156 */
157static void
158test_re_compile(text *text_re, int cflags, Oid collation,
159 regex_t *result_re)
160{
161 int text_re_len = VARSIZE_ANY_EXHDR(text_re);
162 char *text_re_val = VARDATA_ANY(text_re);
163 pg_wchar *pattern;
164 int pattern_len;
165 int regcomp_result;
166 char errMsg[100];
167
168 /* Convert pattern string to wide characters */
169 pattern = (pg_wchar *) palloc((text_re_len + 1) * sizeof(pg_wchar));
170 pattern_len = pg_mb2wchar_with_len(text_re_val,
171 pattern,
172 text_re_len);
173
174 regcomp_result = pg_regcomp(result_re,
175 pattern,
176 pattern_len,
177 cflags,
178 collation);
179
180 pfree(pattern);
181
182 if (regcomp_result != REG_OKAY)
183 {
184 /* re didn't compile (no need for pg_regfree, if so) */
185 pg_regerror(regcomp_result, result_re, errMsg, sizeof(errMsg));
187 (errcode(ERRCODE_INVALID_REGULAR_EXPRESSION),
188 errmsg("invalid regular expression: %s", errMsg)));
189 }
190}
191
192/*
193 * test_re_execute - execute a RE on pg_wchar data
194 *
195 * Returns true on match, false on no match
196 * Arguments are as for pg_regexec
197 */
198static bool
200 int start_search,
201 rm_detail_t *details,
202 int nmatch, regmatch_t *pmatch,
203 int eflags)
204{
205 int regexec_result;
206 char errMsg[100];
207
208 /* Initialize match locations in case engine doesn't */
209 details->rm_extend.rm_so = -1;
210 details->rm_extend.rm_eo = -1;
211 for (int i = 0; i < nmatch; i++)
212 {
213 pmatch[i].rm_so = -1;
214 pmatch[i].rm_eo = -1;
215 }
216
217 /* Perform RE match and return result */
218 regexec_result = pg_regexec(re,
219 data,
220 data_len,
221 start_search,
222 details,
223 nmatch,
224 pmatch,
225 eflags);
226
227 if (regexec_result != REG_OKAY && regexec_result != REG_NOMATCH)
228 {
229 /* re failed??? */
230 pg_regerror(regexec_result, re, errMsg, sizeof(errMsg));
232 (errcode(ERRCODE_INVALID_REGULAR_EXPRESSION),
233 errmsg("regular expression failed: %s", errMsg)));
234 }
235
236 return (regexec_result == REG_OKAY);
237}
238
239
240/*
241 * parse_test_flags - parse the flags argument
242 *
243 * flags --- output argument, filled with desired options
244 * opts --- TEXT object, or NULL for defaults
245 */
246static void
248{
249 /* these defaults must match Tcl's */
250 int cflags = REG_ADVANCED;
251 int eflags = 0;
252 long info = 0;
253
254 flags->glob = false;
255 flags->indices = false;
256 flags->partial = false;
257
258 if (opts)
259 {
260 char *opt_p = VARDATA_ANY(opts);
261 int opt_len = VARSIZE_ANY_EXHDR(opts);
262 int i;
263
264 for (i = 0; i < opt_len; i++)
265 {
266 switch (opt_p[i])
267 {
268 case '-':
269 /* allowed, no-op */
270 break;
271 case '!':
272 flags->partial = true;
273 break;
274 case '*':
275 /* test requires Unicode --- ignored here */
276 break;
277 case '0':
278 flags->indices = true;
279 break;
280
281 /* These flags correspond to user-exposed RE options: */
282 case 'g': /* global match */
283 flags->glob = true;
284 break;
285 case 'i': /* case insensitive */
286 cflags |= REG_ICASE;
287 break;
288 case 'n': /* \n affects ^ $ . [^ */
289 cflags |= REG_NEWLINE;
290 break;
291 case 'p': /* ~Perl, \n affects . [^ */
292 cflags |= REG_NLSTOP;
293 cflags &= ~REG_NLANCH;
294 break;
295 case 'w': /* weird, \n affects ^ $ only */
296 cflags &= ~REG_NLSTOP;
297 cflags |= REG_NLANCH;
298 break;
299 case 'x': /* expanded syntax */
300 cflags |= REG_EXPANDED;
301 break;
302
303 /* These flags correspond to Tcl's -xflags options: */
304 case 'a':
305 cflags |= REG_ADVF;
306 break;
307 case 'b':
308 cflags &= ~REG_ADVANCED;
309 break;
310 case 'c':
311
312 /*
313 * Tcl calls this TCL_REG_CANMATCH, but it's really
314 * REG_EXPECT. In this implementation we must also set
315 * the partial and indices flags, so that
316 * setup_test_matches and build_test_match_result will
317 * emit the desired data. (They'll emit more fields than
318 * Tcl would, but that's fine.)
319 */
320 cflags |= REG_EXPECT;
321 flags->partial = true;
322 flags->indices = true;
323 break;
324 case 'e':
325 cflags &= ~REG_ADVANCED;
326 cflags |= REG_EXTENDED;
327 break;
328 case 'q':
329 cflags &= ~REG_ADVANCED;
330 cflags |= REG_QUOTE;
331 break;
332 case 'o': /* o for opaque */
333 cflags |= REG_NOSUB;
334 break;
335 case 's': /* s for start */
336 cflags |= REG_BOSONLY;
337 break;
338 case '+':
339 cflags |= REG_FAKE;
340 break;
341 case ',':
342 cflags |= REG_PROGRESS;
343 break;
344 case '.':
345 cflags |= REG_DUMP;
346 break;
347 case ':':
348 eflags |= REG_MTRACE;
349 break;
350 case ';':
351 eflags |= REG_FTRACE;
352 break;
353 case '^':
354 eflags |= REG_NOTBOL;
355 break;
356 case '$':
357 eflags |= REG_NOTEOL;
358 break;
359 case 't':
360 cflags |= REG_EXPECT;
361 break;
362 case '%':
363 eflags |= REG_SMALL;
364 break;
365
366 /* These flags define expected info bits: */
367 case 'A':
368 info |= REG_UBSALNUM;
369 break;
370 case 'B':
371 info |= REG_UBRACES;
372 break;
373 case 'E':
374 info |= REG_UBBS;
375 break;
376 case 'H':
377 info |= REG_ULOOKAROUND;
378 break;
379 case 'I':
380 info |= REG_UIMPOSSIBLE;
381 break;
382 case 'L':
383 info |= REG_ULOCALE;
384 break;
385 case 'M':
386 info |= REG_UUNPORT;
387 break;
388 case 'N':
389 info |= REG_UEMPTYMATCH;
390 break;
391 case 'P':
392 info |= REG_UNONPOSIX;
393 break;
394 case 'Q':
395 info |= REG_UBOUNDS;
396 break;
397 case 'R':
398 info |= REG_UBACKREF;
399 break;
400 case 'S':
401 info |= REG_UUNSPEC;
402 break;
403 case 'T':
404 info |= REG_USHORTEST;
405 break;
406 case 'U':
407 info |= REG_UPBOTCH;
408 break;
409
410 default:
412 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
413 errmsg("invalid regular expression test option: \"%.*s\"",
414 pg_mblen(opt_p + i), opt_p + i)));
415 break;
416 }
417 }
418 }
419 flags->cflags = cflags;
420 flags->eflags = eflags;
421 flags->info = info;
422}
423
424/*
425 * setup_test_matches --- do the initial matching
426 *
427 * To simplify memory management, we do all the matching in one swoop.
428 * The returned test_regex_ctx contains the locations of all the substrings
429 * matching the pattern.
430 */
431static test_regex_ctx *
433 regex_t *cpattern, test_re_flags *re_flags,
434 Oid collation,
435 bool use_subpatterns)
436{
439 int orig_len;
440 pg_wchar *wide_str;
441 int wide_len;
442 regmatch_t *pmatch;
443 int pmatch_len;
444 int array_len;
445 int array_idx;
446 int prev_match_end;
447 int start_search;
448 int maxlen = 0; /* largest fetch length in characters */
449
450 /* save flags */
451 matchctx->re_flags = *re_flags;
452
453 /* save original string --- we'll extract result substrings from it */
454 matchctx->orig_str = orig_str;
455
456 /* convert string to pg_wchar form for matching */
457 orig_len = VARSIZE_ANY_EXHDR(orig_str);
458 wide_str = palloc_array(pg_wchar, orig_len + 1);
459 wide_len = pg_mb2wchar_with_len(VARDATA_ANY(orig_str), wide_str, orig_len);
460
461 /* do we want to remember subpatterns? */
462 if (use_subpatterns && cpattern->re_nsub > 0)
463 {
464 matchctx->npatterns = cpattern->re_nsub + 1;
465 pmatch_len = cpattern->re_nsub + 1;
466 }
467 else
468 {
469 use_subpatterns = false;
470 matchctx->npatterns = 1;
471 pmatch_len = 1;
472 }
473
474 /* temporary output space for RE package */
475 pmatch = palloc_array(regmatch_t, pmatch_len);
476
477 /*
478 * the real output space (grown dynamically if needed)
479 *
480 * use values 2^n-1, not 2^n, so that we hit the limit at 2^28-1 rather
481 * than at 2^27
482 */
483 array_len = re_flags->glob ? 255 : 31;
484 matchctx->match_locs = palloc_array(int, array_len);
485 array_idx = 0;
486
487 /* search for the pattern, perhaps repeatedly */
488 prev_match_end = 0;
489 start_search = 0;
490 while (test_re_execute(cpattern, wide_str, wide_len,
491 start_search,
492 &matchctx->details,
493 pmatch_len, pmatch,
494 re_flags->eflags))
495 {
496 /* enlarge output space if needed */
497 while (array_idx + matchctx->npatterns * 2 + 1 > array_len)
498 {
499 array_len += array_len + 1; /* 2^n-1 => 2^(n+1)-1 */
500 if (array_len > MaxAllocSize / sizeof(int))
502 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
503 errmsg("too many regular expression matches")));
504 matchctx->match_locs = (int *) repalloc(matchctx->match_locs,
505 sizeof(int) * array_len);
506 }
507
508 /* save this match's locations */
509 for (int i = 0; i < matchctx->npatterns; i++)
510 {
511 int so = pmatch[i].rm_so;
512 int eo = pmatch[i].rm_eo;
513
514 matchctx->match_locs[array_idx++] = so;
515 matchctx->match_locs[array_idx++] = eo;
516 if (so >= 0 && eo >= 0 && (eo - so) > maxlen)
517 maxlen = (eo - so);
518 }
519 matchctx->nmatches++;
520 prev_match_end = pmatch[0].rm_eo;
521
522 /* if not glob, stop after one match */
523 if (!re_flags->glob)
524 break;
525
526 /*
527 * Advance search position. Normally we start the next search at the
528 * end of the previous match; but if the match was of zero length, we
529 * have to advance by one character, or we'd just find the same match
530 * again.
531 */
532 start_search = prev_match_end;
533 if (pmatch[0].rm_so == pmatch[0].rm_eo)
534 start_search++;
535 if (start_search > wide_len)
536 break;
537 }
538
539 /*
540 * If we had no match, but "partial" and "indices" are set, emit the
541 * details.
542 */
543 if (matchctx->nmatches == 0 && re_flags->partial && re_flags->indices)
544 {
545 /* enlarge output space if needed */
546 while (array_idx + matchctx->npatterns * 2 + 1 > array_len)
547 {
548 array_len += array_len + 1; /* 2^n-1 => 2^(n+1)-1 */
549 if (array_len > MaxAllocSize / sizeof(int))
551 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
552 errmsg("too many regular expression matches")));
553 matchctx->match_locs = (int *) repalloc(matchctx->match_locs,
554 sizeof(int) * array_len);
555 }
556
557 matchctx->match_locs[array_idx++] = matchctx->details.rm_extend.rm_so;
558 matchctx->match_locs[array_idx++] = matchctx->details.rm_extend.rm_eo;
559 /* we don't have pmatch data, so emit -1 */
560 for (int i = 1; i < matchctx->npatterns; i++)
561 {
562 matchctx->match_locs[array_idx++] = -1;
563 matchctx->match_locs[array_idx++] = -1;
564 }
565 matchctx->nmatches++;
566 }
567
568 Assert(array_idx <= array_len);
569
570 if (eml > 1)
571 {
572 int64 maxsiz = eml * (int64) maxlen;
573 int conv_bufsiz;
574
575 /*
576 * Make the conversion buffer large enough for any substring of
577 * interest.
578 *
579 * Worst case: assume we need the maximum size (maxlen*eml), but take
580 * advantage of the fact that the original string length in bytes is
581 * an upper bound on the byte length of any fetched substring (and we
582 * know that len+1 is safe to allocate because the varlena header is
583 * longer than 1 byte).
584 */
585 if (maxsiz > orig_len)
586 conv_bufsiz = orig_len + 1;
587 else
588 conv_bufsiz = maxsiz + 1; /* safe since maxsiz < 2^30 */
589
590 matchctx->conv_buf = palloc(conv_bufsiz);
591 matchctx->conv_bufsiz = conv_bufsiz;
592 matchctx->wide_str = wide_str;
593 }
594 else
595 {
596 /* No need to keep the wide string if we're in a single-byte charset. */
597 pfree(wide_str);
598 matchctx->wide_str = NULL;
599 matchctx->conv_buf = NULL;
600 matchctx->conv_bufsiz = 0;
601 }
602
603 /* Clean up temp storage */
604 pfree(pmatch);
605
606 return matchctx;
607}
608
609/*
610 * build_test_info_result - build output array describing compiled regexp
611 *
612 * This borrows some code from Tcl's TclRegAbout().
613 */
614static ArrayType *
616{
617 /* Translation data for flag bits in regex_t.re_info */
618 struct infoname
619 {
620 int bit;
621 const char *text;
622 };
623 static const struct infoname infonames[] = {
624 {REG_UBACKREF, "REG_UBACKREF"},
625 {REG_ULOOKAROUND, "REG_ULOOKAROUND"},
626 {REG_UBOUNDS, "REG_UBOUNDS"},
627 {REG_UBRACES, "REG_UBRACES"},
628 {REG_UBSALNUM, "REG_UBSALNUM"},
629 {REG_UPBOTCH, "REG_UPBOTCH"},
630 {REG_UBBS, "REG_UBBS"},
631 {REG_UNONPOSIX, "REG_UNONPOSIX"},
632 {REG_UUNSPEC, "REG_UUNSPEC"},
633 {REG_UUNPORT, "REG_UUNPORT"},
634 {REG_ULOCALE, "REG_ULOCALE"},
635 {REG_UEMPTYMATCH, "REG_UEMPTYMATCH"},
636 {REG_UIMPOSSIBLE, "REG_UIMPOSSIBLE"},
637 {REG_USHORTEST, "REG_USHORTEST"},
638 {0, NULL}
639 };
640 const struct infoname *inf;
641 Datum elems[lengthof(infonames) + 1];
642 int nresults = 0;
643 char buf[80];
644 int dims[1];
645 int lbs[1];
646
647 /* Set up results: first, the number of subexpressions */
648 snprintf(buf, sizeof(buf), "%d", (int) cpattern->re_nsub);
649 elems[nresults++] = PointerGetDatum(cstring_to_text(buf));
650
651 /* Report individual info bit states */
652 for (inf = infonames; inf->bit != 0; inf++)
653 {
654 if (cpattern->re_info & inf->bit)
655 {
656 if (flags->info & inf->bit)
657 elems[nresults++] = PointerGetDatum(cstring_to_text(inf->text));
658 else
659 {
660 snprintf(buf, sizeof(buf), "unexpected %s!", inf->text);
661 elems[nresults++] = PointerGetDatum(cstring_to_text(buf));
662 }
663 }
664 else
665 {
666 if (flags->info & inf->bit)
667 {
668 snprintf(buf, sizeof(buf), "missing %s!", inf->text);
669 elems[nresults++] = PointerGetDatum(cstring_to_text(buf));
670 }
671 }
672 }
673
674 /* And form an array */
675 dims[0] = nresults;
676 lbs[0] = 1;
677 /* XXX: this hardcodes assumptions about the text type */
678 return construct_md_array(elems, NULL, 1, dims, lbs,
679 TEXTOID, -1, false, TYPALIGN_INT);
680}
681
682/*
683 * build_test_match_result - build output array for current match
684 *
685 * Note that if the indices flag is set, we don't need any strings,
686 * just the location data.
687 */
688static ArrayType *
690{
691 char *buf = matchctx->conv_buf;
692 Datum *elems = matchctx->elems;
693 bool *nulls = matchctx->nulls;
694 bool indices = matchctx->re_flags.indices;
695 char bufstr[80];
696 int dims[1];
697 int lbs[1];
698 int loc;
699 int i;
700
701 /* Extract matching substrings from the original string */
702 loc = matchctx->next_match * matchctx->npatterns * 2;
703 for (i = 0; i < matchctx->npatterns; i++)
704 {
705 int so = matchctx->match_locs[loc++];
706 int eo = matchctx->match_locs[loc++];
707
708 if (indices)
709 {
710 /* Report eo this way for consistency with Tcl */
711 snprintf(bufstr, sizeof(bufstr), "%d %d",
712 so, so < 0 ? eo : eo - 1);
713 elems[i] = PointerGetDatum(cstring_to_text(bufstr));
714 nulls[i] = false;
715 }
716 else if (so < 0 || eo < 0)
717 {
718 elems[i] = (Datum) 0;
719 nulls[i] = true;
720 }
721 else if (buf)
722 {
723 int len = pg_wchar2mb_with_len(matchctx->wide_str + so,
724 buf,
725 eo - so);
726
727 Assert(len < matchctx->conv_bufsiz);
729 nulls[i] = false;
730 }
731 else
732 {
734 PointerGetDatum(matchctx->orig_str),
735 Int32GetDatum(so + 1),
736 Int32GetDatum(eo - so));
737 nulls[i] = false;
738 }
739 }
740
741 /* In EXPECT indices mode, also report the "details" */
742 if (indices && (matchctx->re_flags.cflags & REG_EXPECT))
743 {
744 int so = matchctx->details.rm_extend.rm_so;
745 int eo = matchctx->details.rm_extend.rm_eo;
746
747 snprintf(bufstr, sizeof(bufstr), "%d %d",
748 so, so < 0 ? eo : eo - 1);
749 elems[i] = PointerGetDatum(cstring_to_text(bufstr));
750 nulls[i] = false;
751 i++;
752 }
753
754 /* And form an array */
755 dims[0] = i;
756 lbs[0] = 1;
757 /* XXX: this hardcodes assumptions about the text type */
758 return construct_md_array(elems, nulls, 1, dims, lbs,
759 TEXTOID, -1, false, TYPALIGN_INT);
760}
ArrayType * construct_md_array(Datum *elems, bool *nulls, int ndims, int *dims, int *lbs, Oid elmtype, int elmlen, bool elmbyval, char elmalign)
Definition: arrayfuncs.c:3495
struct varlena text
Definition: c.h:719
int64_t int64
Definition: c.h:549
#define lengthof(array)
Definition: c.h:801
int errcode(int sqlerrcode)
Definition: elog.c:863
int errmsg(const char *fmt,...)
Definition: elog.c:1080
#define ERROR
Definition: elog.h:39
#define ereport(elevel,...)
Definition: elog.h:150
#define MaxAllocSize
Definition: fe_memutils.h:22
#define palloc_array(type, count)
Definition: fe_memutils.h:76
#define palloc0_object(type)
Definition: fe_memutils.h:75
#define PG_GETARG_TEXT_PP(n)
Definition: fmgr.h:309
#define DirectFunctionCall3(func, arg1, arg2, arg3)
Definition: fmgr.h:686
#define PG_GET_COLLATION()
Definition: fmgr.h:198
#define PG_GETARG_TEXT_P_COPY(n)
Definition: fmgr.h:315
#define PG_FUNCTION_ARGS
Definition: fmgr.h:193
#define SRF_IS_FIRSTCALL()
Definition: funcapi.h:304
#define SRF_PERCALL_SETUP()
Definition: funcapi.h:308
#define SRF_RETURN_NEXT(_funcctx, _result)
Definition: funcapi.h:310
#define SRF_FIRSTCALL_INIT()
Definition: funcapi.h:306
#define SRF_RETURN_DONE(_funcctx)
Definition: funcapi.h:328
Assert(PointerIsAligned(start, uint64))
int i
Definition: isn.c:77
if(TABLE==NULL||TABLE_index==NULL)
Definition: isn.c:81
unsigned int pg_wchar
Definition: mbprint.c:31
int pg_wchar2mb_with_len(const pg_wchar *from, char *to, int len)
Definition: mbutils.c:1011
int pg_database_encoding_max_length(void)
Definition: mbutils.c:1549
int pg_mb2wchar_with_len(const char *from, pg_wchar *to, int len)
Definition: mbutils.c:989
int pg_mblen(const char *mbstr)
Definition: mbutils.c:1026
void * repalloc(void *pointer, Size size)
Definition: mcxt.c:1610
void pfree(void *pointer)
Definition: mcxt.c:1594
void * palloc(Size size)
Definition: mcxt.c:1365
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:124
static AmcheckOptions opts
Definition: pg_amcheck.c:112
const void size_t len
const void * data
static char buf[DEFAULT_XLOG_SEG_SIZE]
Definition: pg_test_fsync.c:71
#define snprintf
Definition: port.h:260
static Datum PointerGetDatum(const void *X)
Definition: postgres.h:332
uint64_t Datum
Definition: postgres.h:70
static Datum Int32GetDatum(int32 X)
Definition: postgres.h:222
unsigned int Oid
Definition: postgres_ext.h:32
int pg_regcomp(regex_t *re, const chr *string, size_t len, int flags, Oid collation)
Definition: regcomp.c:372
size_t pg_regerror(int errcode, const regex_t *preg, char *errbuf, size_t errbuf_size)
Definition: regerror.c:60
#define REG_ICASE
Definition: regex.h:184
#define REG_DUMP
Definition: regex.h:193
#define REG_UEMPTYMATCH
Definition: regex.h:149
#define REG_NOMATCH
Definition: regex.h:216
#define REG_PROGRESS
Definition: regex.h:195
#define REG_UBOUNDS
Definition: regex.h:140
#define REG_UIMPOSSIBLE
Definition: regex.h:150
#define REG_UPBOTCH
Definition: regex.h:143
#define REG_ULOOKAROUND
Definition: regex.h:139
#define REG_UBBS
Definition: regex.h:144
#define REG_ADVANCED
Definition: regex.h:181
#define REG_MTRACE
Definition: regex.h:206
#define REG_EXPANDED
Definition: regex.h:186
#define REG_FTRACE
Definition: regex.h:205
#define REG_NLANCH
Definition: regex.h:188
#define REG_EXPECT
Definition: regex.h:191
#define REG_EXTENDED
Definition: regex.h:179
#define REG_NLSTOP
Definition: regex.h:187
#define REG_ADVF
Definition: regex.h:180
#define REG_UUNSPEC
Definition: regex.h:146
#define regmatch_t
Definition: regex.h:246
#define REG_SMALL
Definition: regex.h:207
#define REG_OKAY
Definition: regex.h:215
#define REG_UNONPOSIX
Definition: regex.h:145
#define REG_NOTEOL
Definition: regex.h:203
#define REG_NOTBOL
Definition: regex.h:202
#define REG_NEWLINE
Definition: regex.h:189
#define REG_UBSALNUM
Definition: regex.h:142
#define REG_NOSUB
Definition: regex.h:185
#define REG_USHORTEST
Definition: regex.h:151
#define REG_ULOCALE
Definition: regex.h:148
#define regex_t
Definition: regex.h:245
#define REG_UUNPORT
Definition: regex.h:147
#define REG_BOSONLY
Definition: regex.h:192
#define REG_UBRACES
Definition: regex.h:141
#define REG_FAKE
Definition: regex.h:194
#define REG_UBACKREF
Definition: regex.h:138
#define REG_QUOTE
Definition: regex.h:182
int pg_regexec(regex_t *re, const chr *string, size_t len, size_t search_start, rm_detail_t *details, size_t nmatch, regmatch_t pmatch[], int flags)
Definition: regexec.c:185
void pg_regfree(regex_t *re)
Definition: regfree.c:49
void * user_fctx
Definition: funcapi.h:82
MemoryContext multi_call_memory_ctx
Definition: funcapi.h:101
pg_regoff_t rm_eo
Definition: regex.h:164
pg_regoff_t rm_so
Definition: regex.h:163
pg_regmatch_t rm_extend
Definition: regex.h:170
int * match_locs
Definition: test_regex.c:46
test_re_flags re_flags
Definition: test_regex.c:39
text * orig_str
Definition: test_regex.c:41
pg_wchar * wide_str
Definition: test_regex.c:51
bool * nulls
Definition: test_regex.c:50
rm_detail_t details
Definition: test_regex.c:40
Datum * elems
Definition: test_regex.c:49
char * conv_buf
Definition: test_regex.c:52
Definition: c.h:706
PG_FUNCTION_INFO_V1(test_regex)
struct test_re_flags test_re_flags
static test_regex_ctx * setup_test_matches(text *orig_str, regex_t *cpattern, test_re_flags *re_flags, Oid collation, bool use_subpatterns)
Definition: test_regex.c:432
PG_MODULE_MAGIC
Definition: test_regex.c:22
static void test_re_compile(text *text_re, int cflags, Oid collation, regex_t *result_re)
Definition: test_regex.c:158
struct test_regex_ctx test_regex_ctx
static ArrayType * build_test_match_result(test_regex_ctx *matchctx)
Definition: test_regex.c:689
static bool test_re_execute(regex_t *re, pg_wchar *data, int data_len, int start_search, rm_detail_t *details, int nmatch, regmatch_t *pmatch, int eflags)
Definition: test_regex.c:199
Datum test_regex(PG_FUNCTION_ARGS)
Definition: test_regex.c:79
static void parse_test_flags(test_re_flags *flags, text *opts)
Definition: test_regex.c:247
static ArrayType * build_test_info_result(regex_t *cpattern, test_re_flags *flags)
Definition: test_regex.c:615
static Size VARSIZE_ANY_EXHDR(const void *PTR)
Definition: varatt.h:472
static char * VARDATA_ANY(const void *PTR)
Definition: varatt.h:486
Datum bit(PG_FUNCTION_ARGS)
Definition: varbit.c:391
Datum text_substr(PG_FUNCTION_ARGS)
Definition: varlena.c:547
text * cstring_to_text_with_len(const char *s, int len)
Definition: varlena.c:193
text * cstring_to_text(const char *s)
Definition: varlena.c:181