diff --git a/.exrc b/.exrc new file mode 100644 index 0000000..4afa138 --- /dev/null +++ b/.exrc @@ -0,0 +1 @@ +set ts=8 sts=4 sw=4 noet diff --git a/.gear-rules b/.gear-rules new file mode 100644 index 0000000..461a5cb --- /dev/null +++ b/.gear-rules @@ -0,0 +1 @@ +tar: . diff --git a/pcre.c b/pcre.c new file mode 100644 index 0000000..25a59cf --- /dev/null +++ b/pcre.c @@ -0,0 +1,108 @@ +#include +#include +#include +#include +#include +SQLITE_EXTENSION_INIT1 + +typedef struct { + char *s; + pcre *p; + pcre_extra *e; +} cache_entry; + +#define CACHE_SIZE 16 + +static +void regexp(sqlite3_context *ctx, int argc, sqlite3_value **argv) +{ + const char *re, *str; + pcre *p; + pcre_extra *e; + + assert(argc == 2); + + re = (const char *) sqlite3_value_text(argv[0]); + if (!re) { + sqlite3_result_error(ctx, "no regexp", -1); + return; + } + + str = (const char *) sqlite3_value_text(argv[1]); + if (!str) { + sqlite3_result_error(ctx, "no string", -1); + return; + } + + /* simple LRU cache */ + { + int i; + int found = 0; + cache_entry *cache = sqlite3_user_data(ctx); + + assert(cache); + + for (i = 0; i < CACHE_SIZE && cache[i].s; i++) + if (strcmp(re, cache[i].s) == 0) { + found = 1; + break; + } + if (found) { + if (i > 0) { + cache_entry c = cache[i]; + memmove(cache + 1, cache, i * sizeof(cache_entry)); + cache[0] = c; + } + } + else { + cache_entry c; + const char *err; + int pos; + c.p = pcre_compile(re, 0, &err, &pos, NULL); + if (!c.p) { + char *e2 = sqlite3_mprintf("%s: %s (offset %d)", re, err, pos); + sqlite3_result_error(ctx, e2, -1); + sqlite3_free(e2); + return; + } + c.e = pcre_study(c.p, 0, &err); + c.s = strdup(re); + if (!c.s) { + sqlite3_result_error(ctx, "strdup: ENOMEM", -1); + pcre_free(c.p); + pcre_free(c.e); + return; + } + if (cache[CACHE_SIZE-1].s) { + free(cache[CACHE_SIZE-1].s); + assert(cache[CACHE_SIZE-1].p); + pcre_free(cache[CACHE_SIZE-1].p); + pcre_free(cache[CACHE_SIZE-1].e); + } + memmove(cache + 1, cache, (CACHE_SIZE - 1) * sizeof(cache_entry)); + cache[0] = c; + } + p = cache[0].p; + e = cache[0].e; + } + + { + int rc; + assert(p); + rc = pcre_exec(p, e, str, strlen(str), 0, 0, NULL, 0); + sqlite3_result_int(ctx, rc >= 0); + return; + } +} + +int sqlite3_extension_init(sqlite3 *db, char **err, const sqlite3_api_routines *api) +{ + SQLITE_EXTENSION_INIT2(api) + cache_entry *cache = calloc(CACHE_SIZE, sizeof(cache_entry)); + if (!cache) { + *err = "calloc: ENOMEM"; + return 1; + } + sqlite3_create_function(db, "REGEXP", 2, SQLITE_UTF8, cache, regexp, NULL, NULL); + return 0; +} diff --git a/sqlite3-pcre.spec b/sqlite3-pcre.spec new file mode 100644 index 0000000..6c0a1f1 --- /dev/null +++ b/sqlite3-pcre.spec @@ -0,0 +1,43 @@ +Name: sqlite3-pcre +Version: 0.1 +Release: alt1 + +Summary: Perl-compatible regular expression support for the SQLite +License: XXX (GPL or Public Domain) +Group: Databases + +Source: %name-%version.tar + +# Automatically added by buildreq on Thu Nov 02 2006 +BuildRequires: libpcre-devel libsqlite3-devel sqlite3 + +%description +This SQLite loadable extensing enables the REGEXP operator, +which is not implemented by default, to call PCRE routines +for regular expression matching. + +%prep +%setup -q + +%build +cflags=`pkg-config --cflags sqlite3 libpcre` +libs=`pkg-config --libs sqlite3 libpcre` +gcc -shared -o pcre.so $cflags %optflags %optflags_shared -W -Werror pcre.c $libs -Wl,-z,defs + +#check +sqlite3 >out < 0.1-alt1 +- initial revision