Index: Makefile.in --- Makefile.in.orig 2010-07-18 21:30:41.000000000 +0200 +++ Makefile.in 2010-07-22 21:25:22.000000000 +0200 @@ -191,6 +191,21 @@ # LIBOBJ = $(LIBOBJS$(USE_AMALGAMATION)) +# FTS3 support +ifdef FTS3 +TCC += -DSQLITE_ENABLE_FTS3 -I$(TOP)/ext/fts3 +endif + +# RTREE support +ifdef RTREE +TCC += -DSQLITE_ENABLE_RTREE -I$(TOP)/ext/rtree +endif + +# REGEXP support +ifdef REGEXP +TCC += -DSQLITE_ENABLE_REGEXP +LIBOBJ += regexp.lo +endif # All of the source code files. # @@ -325,6 +340,8 @@ SRC += \ $(TOP)/ext/rtree/rtree.h \ $(TOP)/ext/rtree/rtree.c +SRC += \ + $(TOP)/ext/regexp/regexp.c # Generated source code files @@ -835,6 +852,8 @@ rtree.lo: $(TOP)/ext/rtree/rtree.c $(HDR) $(EXTHDR) $(LTCOMPILE) -DSQLITE_CORE -c $(TOP)/ext/rtree/rtree.c +regexp.lo: $(TOP)/ext/regexp/regexp.c $(HDR) $(EXTHDR) + $(LTCOMPILE) -DSQLITE_CORE -c $(TOP)/ext/regexp/regexp.c # Rules to build the 'testfixture' application. # Index: configure --- configure.orig 2010-07-18 21:30:41.000000000 +0200 +++ configure 2010-07-22 21:24:57.000000000 +0200 @@ -6012,11 +6012,7 @@ if $ac_preproc_ok; then : else - { { $as_echo "$as_me:$LINENO: error: C preprocessor \"$CPP\" fails sanity check -See \`config.log' for more details." >&5 -$as_echo "$as_me: error: C preprocessor \"$CPP\" fails sanity check -See \`config.log' for more details." >&2;} - { (exit 1); exit 1; }; } + : fi ac_ext=c Index: ext/regexp/regexp.c --- ext/regexp/regexp.c.orig 2010-07-22 21:24:57.000000000 +0200 +++ ext/regexp/regexp.c 2010-07-22 21:24:57.000000000 +0200 @@ -0,0 +1,147 @@ +/* + * SQLite REGEXP(regex, string) function + * Copyright (c) 2010 by Ralf S. Engelschall + * + * Based on Public Domain code from 2007 by Alexey Tourbin + * which added plain PCRE based REGEXP function to + * SQLite. In order to not require the SQLite _library_ to require the + * external PCRE library, the code was adapted to the regex(3) API as + * standardized by IEEE Std 1003.2 ("POSIX.2"), sections 2.8 (Regular + * Expression Notation) and B.5 (C Binding for Regular Expression + * Matching) and which is usually provided already by any modern Unix + * system in libc. + */ + +#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_REGEXP) + +/* standard includes */ +#include +#include +#include +#include + +/* SQLite includes */ +#ifndef SQLITE_CORE +#include "sqlite3ext.h" +SQLITE_EXTENSION_INIT1 +#else +#include "sqlite3.h" +#endif + +/* regex cache configuration */ +#ifndef CACHE_SIZE +#define CACHE_SIZE 32 +#endif +typedef struct { + char *str; + regex_t re; +} cache_entry; + +/* the SQL REGEXP(regex, string) function C implementation */ +static void regexp(sqlite3_context *ctx, int argc, sqlite3_value **argv) +{ + const char *re_str, *str; + regex_t *re; + int i; + int rc; + int found; + cache_entry *cache; + cache_entry c; + char err[1024]; + + /* sanity check arguments */ + if (argc != 2) { + sqlite3_result_error(ctx, "invalid number of arguments to REGEXP function", -1); + return; + } + if ((re_str = (const char *)sqlite3_value_text(argv[0])) == NULL) { + sqlite3_result_error(ctx, "no regexp argument given to REGEXP function", -1); + return; + } + if ((str = (const char *)sqlite3_value_text(argv[1])) == NULL) { + sqlite3_result_error(ctx, "no string argument given to REGEXP function", -1); + return; + } + + /* simple regex LRU caching */ + if ((cache = sqlite3_user_data(ctx)) == NULL) { + sqlite3_result_error(ctx, "no regex cache available", -1); + return; + } + found = 0; + for (i = 0; i < CACHE_SIZE && cache[i].str != NULL; i++) { + if (strcmp(re_str, cache[i].str) == 0) { + found = 1; + break; + } + } + if (found) { + if (i > 0) { + /* move cache[i] element to front for faster searching next time */ + c = cache[i]; + memmove(cache + 1, cache, i * sizeof(cache_entry)); + cache[0] = c; + } + } + else { + /* compile and store regular expression */ + int rc = regcomp(&(c.re), re_str, REG_EXTENDED|REG_NOSUB); + if (rc != 0) { + regerror(rc, &(c.re), err, sizeof(err)); + char *e2 = sqlite3_mprintf("regex \"%s\" failed to compile: %s", re_str, err); + sqlite3_result_error(ctx, e2, -1); + sqlite3_free(e2); + return; + } + if ((c.str = strdup(re_str)) == NULL) { + sqlite3_result_error(ctx, "strdup: ENOMEM", -1); + regfree(&(c.re)); + return; + } + + /* insert regex into cache (at first position) */ + i = CACHE_SIZE - 1; + if (cache[i].str != NULL) { + /* expire oldest cache entry */ + free(cache[i].str); + regfree(&(cache[i].re)); + } + memmove(cache + 1, cache, i * sizeof(cache_entry)); + cache[0] = c; + } + + /* take compiled regular expression (either found old one or created new one) */ + re = &(cache[0].re); + + /* apply regular expression onto given string and report matching result */ + rc = regexec(re, str, 0, NULL, 0); + sqlite3_result_int(ctx, rc == 0); + + return; +} + +/* SQLITE_CORE (built-in) entry point */ +int sqlite3RegexpInit(sqlite3 *); +int sqlite3RegexpInit(sqlite3 *db) +{ + cache_entry *cache = NULL; + int rc = SQLITE_OK; + + if ((cache = calloc(CACHE_SIZE, sizeof(cache_entry))) == NULL) + return SQLITE_NOMEM; + rc = sqlite3_create_function(db, "REGEXP", 2, SQLITE_UTF8, cache, regexp, NULL, NULL); + return rc; +} + +#ifndef SQLITE_CORE +/* DSO entry point */ +int sqlite3_extension_init(sqlite3 *, char **, const sqlite3_api_routines *); +int sqlite3_extension_init(sqlite3 *db, char **err, const sqlite3_api_routines *api) +{ + SQLITE_EXTENSION_INIT2(api) + return sqlite3RegexpInit(db); +} +#endif + +#endif + Index: sqlite3.pc.in --- sqlite3.pc.in.orig 2010-06-21 13:48:22.000000000 +0200 +++ sqlite3.pc.in 2010-07-22 21:24:57.000000000 +0200 @@ -8,6 +8,5 @@ Name: SQLite Description: SQL database engine Version: @RELEASE@ -Libs: -L${libdir} -lsqlite3 -Libs.private: @LIBS@ +Libs: -L${libdir} -lsqlite3 @LIBS@ Cflags: -I${includedir} Index: src/main.c --- src/main.c.orig 2010-07-18 21:30:41.000000000 +0200 +++ src/main.c 2010-07-22 21:24:57.000000000 +0200 @@ -1869,6 +1869,13 @@ } #endif +#ifdef SQLITE_ENABLE_REGEXP + if( !db->mallocFailed && rc==SQLITE_OK){ + extern int sqlite3RegexpInit(sqlite3*); + rc = sqlite3RegexpInit(db); + } +#endif + sqlite3Error(db, rc, 0); /* -DSQLITE_DEFAULT_LOCKING_MODE=1 makes EXCLUSIVE the default locking