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
<[email protected]> wrote in
<[email protected]>
> At Mon, 16 Nov 2015 12:16:07 -0300, Alvaro Herrera <[email protected]>
> wrote in <[email protected]>
> > 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
<[email protected]> wrote in
<[email protected]>
> 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 <[email protected]>
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 <[email protected]>
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 ([email protected])
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers