Hello, I tried to implement the mini-language, which is a simplified reglar expression for this specific use.
As a ultra-POC, the attached patch has very ad-hoc preprocess function and does on-the-fly preprocessing, compilation then execution of regular expression. And it is applied to only the first ten or so matchings in psql_completion(). The first attachment is the same with that of previous patchset. Every matching line looks like the following, > else if (RM("ALTER {AGGREGATE|FUNCTION} [#id](..")) > COMPLETE_WITH_FUNCTION_ARG(CAPTURE(1)); As a temporary desin, "{}" means grouping, "|" means alternatives, "[]" means capture and '#id' means any identifier. The largest problem of this would be its computation cost:( This in turn might be too slow if about three hundred matches run... I see no straight-forward way to preprocess these strings.. A possible solution would be extracting these strings then auto-generate a c-srouce to preprocess them. And when running, RM() retrieves the compiled regular expression using the string as the key. At Tue, 17 Nov 2015 15:35:43 +0900 (Tokyo Standard Time), Kyotaro HORIGUCHI <horiguchi.kyot...@lab.ntt.co.jp> wrote in <20151117.153543.183036803.horiguchi.kyot...@lab.ntt.co.jp> > At Mon, 16 Nov 2015 12:16:07 -0300, Alvaro Herrera <alvhe...@2ndquadrant.com> > wrote in <20151116151606.GW614468@alvherre.pgsql> > > I don't think this is an improvement. It seems to me that a lot more > > work is required to maintain these regular expressions, which while > > straightforward are not entirely trivial (harder to read). > > > > If we could come up with a reasonable format that is pre-processed to a > > regexp, that might be better. I think Thomas' proposed patch is closer > > to that than Horiguchi-san's. > > I aimed that matching mechanism can handle optional elements in > syntexes more robustly. But as all you are saying, bare regular > expression is too complex here. At Tue, 17 Nov 2015 16:09:25 +0900 (Tokyo Standard Time), Kyotaro HORIGUCHI <horiguchi.kyot...@lab.ntt.co.jp> wrote in <20151117.160925.45883793.horiguchi.kyot...@lab.ntt.co.jp> > if (Match("^,ALTER,TABLE,\id,$") || > Match("^,ALTER,TABLE,IF,EXISTS,\id,$"))... > > Interpreting this kind of mini-language into regular expressions > could be doable.. regards, -- Kyotaro Horiguchi NTT Open Source Software Center
>From cdc0b9cce43e38463af0b2b7ad4a0181f41995a2 Mon Sep 17 00:00:00 2001 From: Kyotaro Horiguchi <horiguchi.kyot...@lab.ntt.co.jp> Date: Fri, 30 Oct 2015 18:03:18 +0900 Subject: [PATCH 1/2] Allow regex module to be used outside server. To make regular expression available on frontend, enable pg_regex module and the files included from it to be detached from the features not available when used outside backend. --- src/backend/regex/regc_pg_locale.c | 7 +++++++ src/backend/regex/regcomp.c | 12 ++++++++++++ src/include/regex/regcustom.h | 6 ++++++ src/include/utils/pg_locale.h | 7 +++++-- 4 files changed, 30 insertions(+), 2 deletions(-) diff --git a/src/backend/regex/regc_pg_locale.c b/src/backend/regex/regc_pg_locale.c index b707b06..a631ac2 100644 --- a/src/backend/regex/regc_pg_locale.c +++ b/src/backend/regex/regc_pg_locale.c @@ -220,6 +220,13 @@ static const unsigned char pg_char_properties[128] = { }; +/* Get rid of using server-side feature elsewhere of server. */ +#ifdef FRONTEND +#define lc_ctype_is_c(col) (true) +#define GetDatabaseEncoding() PG_UTF8 +#define ereport(x,...) exit(1) +#endif + /* * pg_set_regex_collation: set collation for these functions to obey * diff --git a/src/backend/regex/regcomp.c b/src/backend/regex/regcomp.c index a165b3b..b35ccb4 100644 --- a/src/backend/regex/regcomp.c +++ b/src/backend/regex/regcomp.c @@ -2040,11 +2040,17 @@ rfree(regex_t *re) * The current implementation is Postgres-specific. If we ever get around * to splitting the regex code out as a standalone library, there will need * to be some API to let applications define a callback function for this. + * + * This check is available only on server side. */ static int rcancelrequested(void) { +#ifndef FRONTEND return InterruptPending && (QueryCancelPending || ProcDiePending); +#else + return 0; +#endif } /* @@ -2056,11 +2062,17 @@ rcancelrequested(void) * The current implementation is Postgres-specific. If we ever get around * to splitting the regex code out as a standalone library, there will need * to be some API to let applications define a callback function for this. + * + * This check is available only on server side. */ static int rstacktoodeep(void) { +#ifndef FRONTEND return stack_is_too_deep(); +#else + return 0; +#endif } #ifdef REG_DEBUG diff --git a/src/include/regex/regcustom.h b/src/include/regex/regcustom.h index dbb461a..ffc4031 100644 --- a/src/include/regex/regcustom.h +++ b/src/include/regex/regcustom.h @@ -29,7 +29,11 @@ */ /* headers if any */ +#ifdef FRONTEND +#include "postgres_fe.h" +#else #include "postgres.h" +#endif #include <ctype.h> #include <limits.h> @@ -53,7 +57,9 @@ #define MALLOC(n) malloc(n) #define FREE(p) free(VS(p)) #define REALLOC(p,n) realloc(VS(p),n) +#ifndef assert #define assert(x) Assert(x) +#endif /* internal character type and related */ typedef pg_wchar chr; /* the type itself */ diff --git a/src/include/utils/pg_locale.h b/src/include/utils/pg_locale.h index 8e91033..d71ec07 100644 --- a/src/include/utils/pg_locale.h +++ b/src/include/utils/pg_locale.h @@ -17,14 +17,16 @@ #include <xlocale.h> #endif +/* Don't look GUCs elsewhere of server */ +#ifndef FRONTEND #include "utils/guc.h" - /* GUC settings */ extern char *locale_messages; extern char *locale_monetary; extern char *locale_numeric; extern char *locale_time; +#endif /* lc_time localization cache */ extern char *localized_abbrev_days[]; @@ -32,7 +34,7 @@ extern char *localized_full_days[]; extern char *localized_abbrev_months[]; extern char *localized_full_months[]; - +#ifndef FRONTEND extern bool check_locale_messages(char **newval, void **extra, GucSource source); extern void assign_locale_messages(const char *newval, void *extra); extern bool check_locale_monetary(char **newval, void **extra, GucSource source); @@ -41,6 +43,7 @@ extern bool check_locale_numeric(char **newval, void **extra, GucSource source); extern void assign_locale_numeric(const char *newval, void *extra); extern bool check_locale_time(char **newval, void **extra, GucSource source); extern void assign_locale_time(const char *newval, void *extra); +#endif extern bool check_locale(int category, const char *locale, char **canonname); extern char *pg_perm_setlocale(int category, const char *locale); -- 1.8.3.1
>From 08965765409f0a339058e12b3e7f55a7c8792e6c Mon Sep 17 00:00:00 2001 From: Kyotaro Horiguchi <horiguchi.kyot...@lab.ntt.co.jp> Date: Fri, 30 Oct 2015 18:18:18 +0900 Subject: [PATCH 2/2] Replace previous matching rule with regexps take 2. This patch simply replaces previous matching rule with regular expressions. As a POC, this patch repalces only first several entries. --- src/bin/psql/Makefile | 15 ++- src/bin/psql/tab-complete.c | 275 ++++++++++++++++++++++++++++++++++---------- 2 files changed, 226 insertions(+), 64 deletions(-) diff --git a/src/bin/psql/Makefile b/src/bin/psql/Makefile index f1336d5..bd0b5cc 100644 --- a/src/bin/psql/Makefile +++ b/src/bin/psql/Makefile @@ -23,12 +23,23 @@ override CPPFLAGS := -I. -I$(srcdir) -I$(libpq_srcdir) -I$(top_srcdir)/src/bin/p OBJS= command.o common.o help.o input.o stringutils.o mainloop.o copy.o \ startup.o prompt.o variables.o large_obj.o print.o describe.o \ tab-complete.o mbprint.o dumputils.o keywords.o kwlookup.o \ - sql_help.o \ + sql_help.o regcomp.o regexec.o regfree.o wstrncmp.o \ $(WIN32RES) - +CFLAGS+= -DFRONTEND all: psql + +regc_color.c regc_cvec.c regc_lex.c regc_locale.c regc_nfa.c regcomp.c regc_pg_locale.c rege_dfa.c regexec.c regfree.c: % :$(top_srcdir)/src/backend/regex/% + rm -f $@ && $(LN_S) $< . + +wstrncmp.c: % : $(top_srcdir)/src/backend/utils/mb/% + rm -f $@ && $(LN_S) $< . + +regcomp.o regexec.o regfree.o:regc_color.c regc_cvec.c regc_lex.c regc_locale.c regc_nfa.c regcomp.c regc_pg_locale.c rege_dfa.c regexec.c regfree.c wstrncmp.c + +tab-complete.o: tab-complete.c + psql: $(OBJS) | submake-libpq submake-libpgport $(CC) $(CFLAGS) $(OBJS) $(libpq_pgport) $(LDFLAGS) $(LDFLAGS_EX) $(LIBS) -o $@$(X) diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index b58ec14..7941020 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -54,6 +54,8 @@ #include "common.h" #include "settings.h" #include "stringutils.h" +#include "regex/regex.h" +#include "catalog/pg_collation.h" #ifdef HAVE_RL_FILENAME_COMPLETION_FUNCTION #define filename_completion_function rl_filename_completion_function @@ -792,6 +794,18 @@ typedef struct #define THING_NO_DROP (1 << 1) /* should not show up after DROP */ #define THING_NO_SHOW (THING_NO_CREATE | THING_NO_DROP) +#define CAPBUFLEN 128 +#define MATCHNUM 5 + +#define RM(pat) (rematch(linebufw, wstrlen, pat, rmatches)) +#define MATCHBEG(n) (rmatches[n].rm_so) +#define MATCHEND(n) (rmatches[n].rm_eo) +#define MATCHLEN(n) (MATCHEND(n) - MATCHBEG(n)) +#define CAPCPYLEN(n)(MATCHLEN(n)<CAPBUFLEN ? MATCHLEN(n):CAPBUFLEN - 1) +#define CAPTURE0(n) (strncpy(capbuf[n], linebuf + MATCHBEG(n), CAPCPYLEN(n)),capbuf[n][CAPCPYLEN(n)] = 0) +#define CAPTURE(n) (CAPTURE0(n), capbuf[n]) +#define CAPBUF(n) (capbuf[n]) + static const pgsql_thing_t words_after_create[] = { {"AGGREGATE", NULL, &Query_for_list_of_aggregates}, {"CAST", NULL, NULL}, /* Casts have complex structures for names, so @@ -842,7 +856,6 @@ static const pgsql_thing_t words_after_create[] = { {NULL} /* end of list */ }; - /* Forward declaration of functions */ static char **psql_completion(const char *text, int start, int end); static char *create_command_generator(const char *text, int state); @@ -868,11 +881,28 @@ static void get_previous_words(int point, char **previous_words, int nwords); static char *get_guctype(const char *varname); +static bool rematch(pg_wchar *line, int linelen, char *pat, regmatch_t *rmatches); + + #ifdef NOT_USED static char *quote_file_name(char *text, int match_type, char *quote_pointer); static char *dequote_file_name(char *text, char quote_char); #endif +static int +pg_ascii2wchar_with_len(const char *from, pg_wchar *to, int len) +{ + int cnt = 0; + + while (len > 0 && *from) + { + *to++ = *from++; + len--; + cnt++; + } + *to = 0; + return cnt; +} /* * Initialize the readline library for our purposes. @@ -893,6 +923,31 @@ initialize_readline(void) */ } +static pg_wchar * +expand_wchar_buffer(pg_wchar *p, int *buflen, int newlen, int limit) +{ + pg_wchar *ret = p; + int len1 = newlen - 1; + + Assert(newlen > 0); + if (newlen >= *buflen) + { + int mask; + + /* Allocate in size of 2^n. Minimum 256 wchars */ + for (mask = 255 ; mask < limit && (len1 & mask) != len1 ; + mask = (mask << 1) | 1); + + /* mask is (<new buffer length> - 1) here */ + if (mask >= limit) return NULL; /* Exceeds limit */ + + *buflen = mask + 1; + + ret = pg_realloc(p, (*buflen) * sizeof(pg_wchar)); + } + + return ret; +} /* * The completion function. @@ -905,6 +960,13 @@ initialize_readline(void) static char ** psql_completion(const char *text, int start, int end) { + const char *linebuf = rl_line_buffer; + static pg_wchar *linebufw = NULL; + static int linebufwlen = 0; + int len, wstrlen; + regmatch_t rmatches[MATCHNUM]; + char capbuf[MATCHNUM][CAPBUFLEN]; + /* This is the variable we'll return. */ char **matches = NULL; @@ -967,6 +1029,10 @@ psql_completion(const char *text, int start, int end) * probably intending to type. */ get_previous_words(start, previous_words, lengthof(previous_words)); + len = strlen(linebuf); + /* Expand string buffer if needed */ + linebufw = expand_wchar_buffer(linebufw, &linebufwlen, len + 1, 2048); + wstrlen = pg_ascii2wchar_with_len(linebuf, linebufw, strlen(linebuf)); /* If a backslash command was started, continue */ if (text[0] == '\\') @@ -984,36 +1050,31 @@ psql_completion(const char *text, int start, int end) } /* If no previous word, suggest one of the basic sql commands */ - else if (prev_wd[0] == '\0') + else if (RM("^")) COMPLETE_WITH_LIST(sql_commands); /* CREATE */ /* complete with something you can create */ - else if (pg_strcasecmp(prev_wd, "CREATE") == 0) + else if (RM("^CREATE ")) matches = completion_matches(text, create_command_generator); /* DROP, but not DROP embedded in other commands */ /* complete with something you can drop */ - else if (pg_strcasecmp(prev_wd, "DROP") == 0 && - prev2_wd[0] == '\0') + else if (RM("^DROP ")) matches = completion_matches(text, drop_command_generator); /* ALTER */ /* ALTER TABLE */ - else if (pg_strcasecmp(prev2_wd, "ALTER") == 0 && - pg_strcasecmp(prev_wd, "TABLE") == 0) - { + else if (RM("^ALTER TABLE ")) COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, "UNION SELECT 'ALL IN TABLESPACE'"); - } /* * complete with what you can alter (TABLE, GROUP, USER, ...) unless we're * in ALTER TABLE sth ALTER */ - else if (pg_strcasecmp(prev_wd, "ALTER") == 0 && - pg_strcasecmp(prev3_wd, "TABLE") != 0) + else if (RM("^ALTER ")) { static const char *const list_ALTER[] = {"AGGREGATE", "COLLATION", "CONVERSION", "DATABASE", "DEFAULT PRIVILEGES", "DOMAIN", @@ -1026,9 +1087,7 @@ psql_completion(const char *text, int start, int end) COMPLETE_WITH_LIST(list_ALTER); } /* ALTER TABLE,INDEX,MATERIALIZED VIEW xxx ALL IN TABLESPACE xxx */ - else if (pg_strcasecmp(prev4_wd, "ALL") == 0 && - pg_strcasecmp(prev3_wd, "IN") == 0 && - pg_strcasecmp(prev2_wd, "TABLESPACE") == 0) + else if (RM("ALL IN TABLESPACE #id ")) { static const char *const list_ALTERALLINTSPC[] = {"SET TABLESPACE", "OWNED BY", NULL}; @@ -1036,38 +1095,24 @@ psql_completion(const char *text, int start, int end) COMPLETE_WITH_LIST(list_ALTERALLINTSPC); } /* ALTER TABLE,INDEX,MATERIALIZED VIEW xxx ALL IN TABLESPACE xxx OWNED BY */ - else if (pg_strcasecmp(prev6_wd, "ALL") == 0 && - pg_strcasecmp(prev5_wd, "IN") == 0 && - pg_strcasecmp(prev4_wd, "TABLESPACE") == 0 && - pg_strcasecmp(prev2_wd, "OWNED") == 0 && - pg_strcasecmp(prev4_wd, "BY") == 0) - { + else if (RM("ALL IN TABLESPACE #id OWNED BY ")) COMPLETE_WITH_QUERY(Query_for_list_of_roles); - } /* ALTER AGGREGATE,FUNCTION <name> */ - else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 && - (pg_strcasecmp(prev2_wd, "AGGREGATE") == 0 || - pg_strcasecmp(prev2_wd, "FUNCTION") == 0)) + else if (RM("^ALTER {AGGREGATE|FUNCTION} #id ")) COMPLETE_WITH_CONST("("); /* ALTER AGGREGATE,FUNCTION <name> (...) */ - else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 && - (pg_strcasecmp(prev3_wd, "AGGREGATE") == 0 || - pg_strcasecmp(prev3_wd, "FUNCTION") == 0)) + else if (RM("ALTER {AGGREGATE|FUNCTION} [#id](..")) + COMPLETE_WITH_FUNCTION_ARG(CAPTURE(1)); + else if (RM("ALTER {AGGREGATE|FUNCTION} [#id](..)")) { - if (prev_wd[strlen(prev_wd) - 1] == ')') - { - static const char *const list_ALTERAGG[] = + static const char *const list_ALTERAGG[] = {"OWNER TO", "RENAME TO", "SET SCHEMA", NULL}; - COMPLETE_WITH_LIST(list_ALTERAGG); - } - else - COMPLETE_WITH_FUNCTION_ARG(prev2_wd); + COMPLETE_WITH_LIST(list_ALTERAGG); } /* ALTER SCHEMA <name> */ - else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 && - pg_strcasecmp(prev2_wd, "SCHEMA") == 0) + else if (RM("ALTER SCHEMA #id ")) { static const char *const list_ALTERGEN[] = {"OWNER TO", "RENAME TO", NULL}; @@ -1076,8 +1121,7 @@ psql_completion(const char *text, int start, int end) } /* ALTER COLLATION <name> */ - else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 && - pg_strcasecmp(prev2_wd, "COLLATION") == 0) + else if (RM("ALTER COLLATION #id ")) { static const char *const list_ALTERGEN[] = {"OWNER TO", "RENAME TO", "SET SCHEMA", NULL}; @@ -1086,8 +1130,7 @@ psql_completion(const char *text, int start, int end) } /* ALTER CONVERSION <name> */ - else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 && - pg_strcasecmp(prev2_wd, "CONVERSION") == 0) + else if (RM("ALTER CONVERSION #id ")) { static const char *const list_ALTERGEN[] = {"OWNER TO", "RENAME TO", "SET SCHEMA", NULL}; @@ -1096,8 +1139,7 @@ psql_completion(const char *text, int start, int end) } /* ALTER DATABASE <name> */ - else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 && - pg_strcasecmp(prev2_wd, "DATABASE") == 0) + else if (RM("ALTER DATABASE #id ")) { static const char *const list_ALTERDATABASE[] = {"RESET", "SET", "OWNER TO", "RENAME TO", "IS_TEMPLATE", @@ -1107,17 +1149,13 @@ psql_completion(const char *text, int start, int end) } /* ALTER EVENT TRIGGER */ - else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 && - pg_strcasecmp(prev2_wd, "EVENT") == 0 && - pg_strcasecmp(prev_wd, "TRIGGER") == 0) + else if (RM("ALTER EVENT TRIGGER")) { COMPLETE_WITH_QUERY(Query_for_list_of_event_triggers); } /* ALTER EVENT TRIGGER <name> */ - else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 && - pg_strcasecmp(prev3_wd, "EVENT") == 0 && - pg_strcasecmp(prev2_wd, "TRIGGER") == 0) + else if (RM("ALTER EVENT TRIGGER #id ")) { static const char *const list_ALTER_EVENT_TRIGGER[] = {"DISABLE", "ENABLE", "OWNER TO", "RENAME TO", NULL}; @@ -1126,10 +1164,7 @@ psql_completion(const char *text, int start, int end) } /* ALTER EVENT TRIGGER <name> ENABLE */ - else if (pg_strcasecmp(prev5_wd, "ALTER") == 0 && - pg_strcasecmp(prev4_wd, "EVENT") == 0 && - pg_strcasecmp(prev3_wd, "TRIGGER") == 0 && - pg_strcasecmp(prev_wd, "ENABLE") == 0) + else if (RM("ALTER EVENT TRIGGER #id ENABLE ")) { static const char *const list_ALTER_EVENT_TRIGGER_ENABLE[] = {"REPLICA", "ALWAYS", NULL}; @@ -1138,8 +1173,7 @@ psql_completion(const char *text, int start, int end) } /* ALTER EXTENSION <name> */ - else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 && - pg_strcasecmp(prev2_wd, "EXTENSION") == 0) + else if (RM("ALTER EXTENSION #id ")) { static const char *const list_ALTEREXTENSION[] = {"ADD", "DROP", "UPDATE", "SET SCHEMA", NULL}; @@ -1148,8 +1182,7 @@ psql_completion(const char *text, int start, int end) } /* ALTER FOREIGN */ - else if (pg_strcasecmp(prev2_wd, "ALTER") == 0 && - pg_strcasecmp(prev_wd, "FOREIGN") == 0) + else if (RM("ALTER FOREIGN ")) { static const char *const list_ALTER_FOREIGN[] = {"DATA WRAPPER", "TABLE", NULL}; @@ -1158,10 +1191,7 @@ psql_completion(const char *text, int start, int end) } /* ALTER FOREIGN DATA WRAPPER <name> */ - else if (pg_strcasecmp(prev5_wd, "ALTER") == 0 && - pg_strcasecmp(prev4_wd, "FOREIGN") == 0 && - pg_strcasecmp(prev3_wd, "DATA") == 0 && - pg_strcasecmp(prev2_wd, "WRAPPER") == 0) + else if (RM("ALTER FOREIGN DATA WRAPPER #id ")) { static const char *const list_ALTER_FDW[] = {"HANDLER", "VALIDATOR", "OPTIONS", "OWNER TO", NULL}; @@ -4185,12 +4215,10 @@ psql_completion(const char *text, int start, int end) for (i = 0; i < lengthof(previous_words); i++) free(previous_words[i]); } - /* Return our Grand List O' Matches */ return matches; } - /* * GENERATOR FUNCTIONS * @@ -4923,4 +4951,127 @@ dequote_file_name(char *text, char quote_char) } #endif /* NOT_USED */ +//#define WB "[\\t\\n@$><=;|&{\\(\\) ]" /* WORD BREAKS */ +//#define NWB "[^\\t\\n@$><=;|&{\\(\\) ]" /* ^WORD BREAKS */ +#define WB "[\\t\\n@$><=;|&{ ]" /* WORD BREAKS */ +#define NWB "[^\\t\\n@$><=;|&{ ]" /* ^WORD BREAKS */ +#define PB "^(?:.*;)?\\s*" /* bind to the beginning of the line */ +#define PM "^(?:.*"NWB WB")?\\s*" /* floating head */ +#define ID NWB"+" /* identifier */ +#define KWD "\\w+" /* any keywords */ + +bool +rematch(pg_wchar *line, int linelen, char *pat, regmatch_t *rmatches) +{ + pg_wchar rewstr[4096]; + char restr[4096]; + char *p, *rp; + int relen; + int ret; + regex_t re; + bool prev_is_ws = false; + char *NORMAL_TAIL = NWB"*$"; + char *INPAREN_TAIL = "[^\\)]*$"; + char *tail; + + restr[0] = 0; + if (*pat != '^') + strcpy(restr, PM); + rp = restr + strlen(restr); + for (p = pat ; *p ; p++) + { + tail = NORMAL_TAIL; + switch (*p) + { + case '.': + if (strncmp(p+1, ".", 1) == 0) + { + p++; + strcpy(rp, "[^\\)]*"); + } + else + exit(1); + break; + case '#': + if (strncmp(p+1, "id", 2) == 0) + { + p += 2; + if (!prev_is_ws) + strcpy(rp, WB"+"); + strcpy(rp, ID NWB "+"); + prev_is_ws = false; + } + else + exit(1); + break; + case '^': + strcpy(rp, "^(?:.*;)?\\s*"); + prev_is_ws = true; + break; + case '$': + strcpy(rp, NWB"*$"); + prev_is_ws = false; + break; + case '?': + strcpy(rp,"?"); + prev_is_ws = false; + break; + case ' ': + if (!prev_is_ws) + strcpy(rp, WB"+"); + prev_is_ws = true; + break; + case '(': + strcpy(rp, WB"*\\([\\)]*"); + prev_is_ws = true; + tail = INPAREN_TAIL; + break; + case ')': + strcpy(rp, WB"*\\)"WB"*"); + prev_is_ws = true; + break; + case '[': + strcpy(rp, "("); + break; + case ']': + strcpy(rp, ")"); + break; + case '{': + strcpy(rp, "(?:"); + break; + case '}': + strcpy(rp, ")"); + break; + case '|': + strcpy(rp, "|"); + break; + default: + if (*p) + { + if (!prev_is_ws) + strcpy(rp, WB"+"); + while(isalnum(*p)) + *rp++ = *p++; + *rp = 0; + p--; + prev_is_ws = false; + } + } + while (*rp) rp++; + /* No overrun check! */ + } + strcpy(rp, tail); + + relen = pg_ascii2wchar_with_len(restr, rewstr, strlen(restr)); + ret = pg_regcomp(&re, rewstr, relen, REG_ADVANCED|REG_ICASE, + C_COLLATION_OID); + if (ret != 0) + exit(1); + + ret = pg_regexec(&re, line, linelen, 0, NULL, MATCHNUM, rmatches, 0) == 0; + pg_regfree(&re); + + return ret; +} + #endif /* USE_READLINE */ -- 1.8.3.1
-- Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers