Index: include/fossil-scm/fossil-confdb.h ================================================================== --- include/fossil-scm/fossil-confdb.h +++ include/fossil-scm/fossil-confdb.h @@ -43,11 +43,20 @@ */ FSL_CONFDB_REPO = 2, /** Signfies the checkout-level (a.k.a. "local") configuration area. */ -FSL_CONFDB_CKOUT = 3 +FSL_CONFDB_CKOUT = 3, +/** + Signifies the versioned-level (via ".fossil-settings") configuration area. +*/ +FSL_CONFDB_VERSIONED = 4, +/** + Signifies the default-level (and other settings metadata) configuration area. + This are is read-only, and only contains the publicly documented settings. +*/ +FSL_CONFDB_METADATA = 5 }; typedef enum fsl_confdb_t fsl_confdb_t; /** Returns the name of the db table associated with the given Index: include/fossil-scm/fossil-core.h ================================================================== --- include/fossil-scm/fossil-core.h +++ include/fossil-scm/fossil-core.h @@ -1299,10 +1299,21 @@ /** The checkout-db counterpart of fsl_needs_repo(). */ FSL_EXPORT fsl_db * fsl_needs_checkout(fsl_cx * f); + +/** + The versioned settings counterpart of fsl_needs_repo(). +*/ +FSL_EXPORT fsl_db * fsl_cx_db_versioned( fsl_cx * f ); + +/** + The settings metadata are read-only aspects of settings. +*/ +FSL_EXPORT fsl_db * fsl_cx_db_metadata( fsl_cx * f ); + /** Opens the given database file as f's configuration database. If f already has a config database opened, it is closed before opening the new one. The database is created and populated with an Index: include/fossil-scm/fossil-ext_regexp.h ================================================================== --- include/fossil-scm/fossil-ext_regexp.h +++ include/fossil-scm/fossil-ext_regexp.h @@ -63,38 +63,38 @@ #ifdef __cplusplus extern "C" { #endif -#include "sqlite3ext.h" +struct sqlite3_api_routines; /*(this file is derived from sqlite3 regexp.c; I wanted to expose the engine to also be used directly*/ -typedef struct ReCompiled ReCompiled; +typedef struct sqlite3_ReCompiled sqlite3_ReCompiled; /* ** Compile a textual regular expression in zIn[] into a compiled regular ** expression suitable for us by re_match() and return a pointer to the ** compiled regular expression in *ppRe. Return NULL on success or an ** error message if something goes wrong. */ -const char* sqlite3re_compile ( ReCompiled** ppRe, const char* zIn, int noCase ); +const char* sqlite3re_compile ( sqlite3_ReCompiled** ppRe, const char* zIn, int noCase ); /* Free and reclaim all the memory used by a previously compiled ** regular expression. Applications should invoke this routine once ** for every call to re_compile() to avoid memory leaks. */ -void sqlite3re_free ( ReCompiled* pRe ); +void sqlite3re_free ( sqlite3_ReCompiled* pRe ); /* Run a compiled regular expression on the zero-terminated input ** string zIn[]. Return true on a match and false if there is no match. */ -int sqlite3re_match ( ReCompiled* pRe, const unsigned char* zIn, int nIn ); +int sqlite3re_match ( sqlite3_ReCompiled* pRe, const unsigned char* zIn, int nIn ); /*registers the REGEXP extension function into a sqlite3 database instance */ int sqlite3_regexp_init ( sqlite3* db, char** pzErrMsg, Index: include/fossil-scm/fossil-vtbl_fossil_settings.h ================================================================== --- include/fossil-scm/fossil-vtbl_fossil_settings.h +++ include/fossil-scm/fossil-vtbl_fossil_settings.h @@ -19,13 +19,14 @@ #ifdef __cplusplus extern "C" { #endif -#include "sqlite3ext.h" +struct sqlite3_api_routines; +/*registers some sqlite3 extensions for handling versioned settings */ int sqlite3_vtbl_fossilsettings_init ( sqlite3* db, char** pzErrMsg, const struct sqlite3_api_routines* pApi ); Index: libfossil_VC6.dsp ================================================================== --- libfossil_VC6.dsp +++ libfossil_VC6.dsp @@ -280,11 +280,11 @@ SOURCE=.\src\fsl_zip.c # End Source File # Begin Source File SOURCE=.\src\sqlite3.c -# ADD CPP /W3 +# ADD CPP /w /W0 # End Source File # End Group # Begin Group "Header Files" # PROP Default_Filter "h;hpp;hxx;hm;inl" Index: src/Makefile.in ================================================================== --- src/Makefile.in +++ src/Makefile.in @@ -1,9 +1,9 @@ all: include ../subdir-inc.make #$(error $(TOP_SRCDIR)) -CPPFLAGS += -DBUILD_libfossil +CPPFLAGS += -DBUILD_libfossil -DSQLITE_CORE #CPPFLAGS += -I$(TOP_INCDIR) #CPPFLAGS += -I$(TOP_SRCDIR)/include #CPPFLAGS += -I$(TOP_SRCDIR)/src# workaround for in-tree sqlite3.h EXTRA_LIBS := -lz EXTRA_LIBS += $(LDFLAGS_MODULE_LOADER) @@ -28,10 +28,11 @@ fsl_db.c \ fsl_delta.c \ fsl_diff.c \ fsl_encode.c \ fsl_event.c \ + fsl_ext_regexp.c \ fsl_fs.c \ fsl_glob.c \ fsl_io.c \ fsl_leaf.c \ fsl_list.c \ @@ -46,10 +47,11 @@ fsl_strftime.c \ fsl_tag.c \ fsl_ticket.c \ fsl_utf8.c \ fsl_vfile.c \ + fsl_vtbl_fossil_settings.c \ fsl_wiki.c \ fsl_zip.c FSL.SRC := $(FSL.SRC.BASE) \ fsl_jni.c \ @@ -175,10 +177,12 @@ $(INCD)/fossil-confdb.h \ $(INCD)/fossil-path.h \ $(INCD)/fossil-internal.h \ $(INCD)/fossil-auth.h \ $(INCD)/fossil-pages.h \ + $(INCD)/fossil-ext_regexp.h \ + $(INCD)/fossil-vtbl_fossil_settings.h \ $(INCD)/fossil-cli.h $(AMAL_H.SRC): $(AMAL_C.SRC): $(AMAL_H): $(MAKEFILE_LIST) $(AMAL_H.SRC) $(AMAL_CONF.H) $(AMAL_C): $(MAKEFILE_LIST) $(AMAL_H) $(AMAL_C.SRC) Index: src/fsl_config.c ================================================================== --- src/fsl_config.c +++ src/fsl_config.c @@ -38,10 +38,12 @@ char const * fsl_config_table_for_role(fsl_confdb_t mode){ switch(mode){ case FSL_CONFDB_REPO: return "config"; case FSL_CONFDB_CKOUT: return "vvar"; case FSL_CONFDB_GLOBAL: return "global_config"; + case FSL_CONFDB_VERSIONED: return "versionedsettings"; + case FSL_CONFDB_METADATA: return "settingsmetadata"; default: assert(!"Invalid fsl_confdb_t value"); return NULL; } } @@ -49,10 +51,12 @@ fsl_db * fsl_config_for_role(fsl_cx * f, fsl_confdb_t mode){ switch(mode){ case FSL_CONFDB_REPO: return fsl_cx_db_repo(f); case FSL_CONFDB_CKOUT: return fsl_cx_db_checkout(f); case FSL_CONFDB_GLOBAL: return fsl_cx_db_config(f); + case FSL_CONFDB_VERSIONED: return fsl_cx_db_versioned(f); + case FSL_CONFDB_METADATA: return fsl_cx_db_metadata(f); default: assert(!"Invalid fsl_confdb_t value"); return NULL; } } Index: src/fsl_cx.c ================================================================== --- src/fsl_cx.c +++ src/fsl_cx.c @@ -18,10 +18,12 @@ ***************************************************************************** This file houses most of the context-related APIs. */ #include "fossil-scm/fossil-internal.h" #include +#include "fossil-scm/fossil-ext_regexp.h" +#include "fossil-scm/fossil-vtbl_fossil_settings.h" #if defined(_WIN32) # include # define F_OK 0 # define W_OK 2 @@ -60,10 +62,22 @@ *tgt = f; } f->output = param->output; f->cxConfig = param->config; +/*HHH idempotent */ +{ + static int bRegistered = 0; + if ( ! bRegistered ) + { + /*register our statically linked extensions to be auto-init'ed at the appropriate time*/ + sqlite3_auto_extension((void(*)(void))(sqlite3_vtbl_fossilsettings_init)); /*our settings vtables*/ + sqlite3_auto_extension((void(*)(void))(sqlite3_regexp_init)); /*sqlite regexp extension*/ + bRegistered = 1; + } +} + return rc; } void fsl_cx_reset(fsl_cx * f, char closeDatabases){ if(!f) return; @@ -274,10 +288,20 @@ if(!f) return NULL; else if(f->ckout.db.dbh) return &f->ckout.db; else if(f->dbMain && (FSL_DB_ROLE_CHECKOUT & f->dbMain->role)) return f->dbMain; else return NULL; } + +fsl_db * fsl_cx_db_versioned( fsl_cx * f ){ + /*the 'versioned settings' is a virtual table strapped onto the checkout db*/ + return fsl_cx_db_checkout ( f ); +} + +fsl_db * fsl_cx_db_metadata( fsl_cx * f ){ + /*the 'settings metadata' is a virtual table strapped onto the main db*/ + return fsl_cx_db ( f ); +} fsl_db * fsl_cx_db( fsl_cx * f ){ return f ? f->dbMain : NULL; } @@ -1093,10 +1117,31 @@ conventions purposes. */ f->ckout.dir = (char *)buf->mem; *buf = fsl_buffer_empty /* we took ownership of buf->mem */; assert(!f->ckout.dir[f->ckout.dirLen]); assert('/' == f->ckout.dir[f->ckout.dirLen-1]); + /*defensively drop any existing table definition; there shouldn't be one, + but maybe could be if the application crashed before closing gracefully*/ + rc = sqlite3_exec ( f->dbMain->dbh, "DROP TABLE versionedsettings;", NULL, NULL, NULL ); + /*now we create our versioned settings table*/ + { + /*alas, we must coin the name ourselves, because parameters to CREATE + VIRTUAL TABLE are not processed by sqlite, so we can't use + FSL_CKOUT_DIR(). Since we go that far, we might as well just add the + surrounding sql as well.*/ + fsl_buffer strCreateSql; + strCreateSql = fsl_buffer_empty; + fsl_buffer_append ( &strCreateSql, "CREATE VIRTUAL TABLE " + "versionedsettings USING VERSIONED_SETTINGS( '", -1 ); + fsl_buffer_append ( &strCreateSql, f->ckout.dir, -1 ); + fsl_buffer_append ( &strCreateSql, ".fossil-settings", -1 ); + fsl_buffer_append ( &strCreateSql, "', MKDIR );", -1 ); + rc = sqlite3_exec ( f->dbMain->dbh, + fsl_buffer_cstr ( &strCreateSql ), + NULL, NULL, NULL ); + fsl_buffer_clear ( &strCreateSql ); + } /* why? rc = fsl_config_open(f, NULL); Seems to me we don't need that at the library level. In my experience global config options for a lib which is used by @@ -1106,10 +1151,11 @@ fossil-based app, which is no longer the case. */ f->flags |= FSL_CX_F_IS_OPENING_CKOUT; rc = fsl_repo_open_for_checkout(f); f->flags &= ~FSL_CX_F_IS_OPENING_CKOUT; +/*HHH this updates some copies of settings!!!*/ rc = fsl_cx_after_open(f); if(rc){ /* Is this sane? Is not doing it sane? */ fsl_checkout_close(f); } Index: src/fsl_db.c ================================================================== --- src/fsl_db.c +++ src/fsl_db.c @@ -144,10 +144,14 @@ if(0!=db->openStatementCount){ MARKER(("WARNING: %d open statement(s) left on db [%s].\n", (int)db->openStatementCount, db->filename)); } if(db->dbh){ + /*drop our virtual tables, if they exist*/ + int rc; + rc = sqlite3_exec ( db->dbh, "DROP TABLE settingsmetadata;", NULL, NULL, NULL ); + rc = sqlite3_exec ( db->dbh, "DROP TABLE versionedsettings;", NULL, NULL, NULL ); sqlite3_close(db->dbh) /* ignoring result in the style of "destructors may not throw". */; } @@ -1472,10 +1476,15 @@ layer. i.e. this belongs downstream. */ sqlite3_create_function(dbh, "cgi", 1, SQLITE_ANY, 0, db_sql_cgi, 0, 0); sqlite3_create_function(dbh, "cgi", 2, SQLITE_ANY, 0, db_sql_cgi, 0, 0); re_add_sql_func(db) /* Requires the regex bits. */; #endif + /*HHH check out the re_add_sql_func; did we already have regex somewhere?*/ + /*create the settingsmetadata virtual table in the db*/ + rc = sqlite3_exec ( dbh, "CREATE VIRTUAL TABLE IF NOT EXISTS " + "settingsmetadata USING SETTINGS_METADATA();", NULL, NULL, NULL ); + assert(SQLITE_OK==rc); /*shoud only fails if you haven't registered the extension*/ }/*if(db->f)*/ } end: if(rc){ #if 1 Index: src/fsl_ext_regexp.c ================================================================== --- src/fsl_ext_regexp.c +++ src/fsl_ext_regexp.c @@ -61,27 +61,19 @@ #if defined(_MSC_VER) #pragma warning ( disable : 4786 ) #endif #ifndef SQLITE_CORE #include "sqlite3ext.h" -//declares extern the vtable of all the sqlite3 functions (for loadable modules only) +/*declares extern the vtable of all the sqlite3 functions (for loadable modules only)*/ SQLITE_EXTENSION_INIT3 #else #include "sqlite3.h" #endif #include "fossil-scm/fossil-ext_regexp.h" #include #include -/* -** The following #defines change the names of some functions implemented in -** this file to prevent name collisions with C-library functions of the -** same name. -*/ -#define re_match sqlite3re_match -#define re_compile sqlite3re_compile -#define re_free sqlite3re_free /* The end-of-input character */ #define RE_EOF 0 /* End of input */ /* The NFA is implemented as sequence of opcodes taken from the following @@ -104,48 +96,49 @@ #define RE_OP_SPACE 15 /* space: [ \t\n\r\v\f] */ #define RE_OP_NOTSPACE 16 /* Not a digit */ #define RE_OP_BOUNDARY 17 /* Boundary between word and non-word */ /* Each opcode is a "state" in the NFA */ -typedef unsigned short ReStateNumber; +typedef unsigned short sqlite3_ReStateNumber; /* Because this is an NFA and not a DFA, multiple states can be active at ** once. An instance of the following object records all active states in ** the NFA. The implementation is optimized for the common case where the ** number of actives states is small. */ -typedef struct ReStateSet { +typedef struct sqlite3_ReStateSet { unsigned nState; /* Number of current states */ - ReStateNumber *aState; /* Current states */ -} ReStateSet; + sqlite3_ReStateNumber *aState; /* Current states */ +} sqlite3_ReStateSet; /* An input string read one character at a time. */ -typedef struct ReInput ReInput; -struct ReInput { +typedef struct sqlite3_ReInput sqlite3_ReInput; +struct sqlite3_ReInput { const unsigned char *z; /* All text */ int i; /* Next byte to read */ int mx; /* EOF when i>=mx */ }; /* A compiled NFA (or an NFA that is in the process of being compiled) is ** an instance of the following object. */ -struct ReCompiled { - ReInput sIn; /* Regular expression text */ +/*typedef struct sqlite3_ReCompiled sqlite3_ReCompiled;*/ +struct sqlite3_ReCompiled { + sqlite3_ReInput sIn; /* Regular expression text */ const char *zErr; /* Error message to return */ char *aOp; /* Operators for the virtual machine */ int *aArg; /* Arguments to each operator */ - unsigned (*xNextChar)(ReInput*); /* Next character function */ + unsigned (*xNextChar)(sqlite3_ReInput*); /* Next character function */ unsigned char zInit[12]; /* Initial text to match */ int nInit; /* Number of characters in zInit */ unsigned nState; /* Number of entries in aOp[] and aArg[] */ unsigned nAlloc; /* Slots allocated for aOp[] and aArg[] */ }; /* Add a state to the given state set if it is not already there */ -static void re_add_state(ReStateSet *pSet, int newState){ +static void sqlite3re_add_state(sqlite3_ReStateSet *pSet, int newState){ unsigned i; for(i=0; inState; i++) if( pSet->aState[i]==newState ) return; pSet->aState[pSet->nState++] = newState; } @@ -152,11 +145,11 @@ /* Extract the next unicode character from *pzIn and return it. Advance ** *pzIn to the first byte past the end of the character returned. To ** be clear: this routine converts utf8 to unicode. This routine is ** optimized for the common case where the next character is a single byte. */ -static unsigned re_next_char(ReInput *p){ +static unsigned sqlite3re_next_char(sqlite3_ReInput *p){ unsigned c; if( p->i>=p->mx ) return 0; c = p->z[p->i++]; if( c>=0x80 ){ if( (c&0xe0)==0xc0 && p->imx && (p->z[p->i]&0xc0)==0x80 ){ @@ -177,45 +170,45 @@ c = 0xfffd; } } return c; } -static unsigned re_next_char_nocase(ReInput *p){ - unsigned c = re_next_char(p); +static unsigned sqlite3re_next_char_nocase(sqlite3_ReInput *p){ + unsigned c = sqlite3re_next_char(p); if( c>='A' && c<='Z' ) c += 'a' - 'A'; return c; } /* Return true if c is a perl "word" character: [A-Za-z0-9_] */ -static int re_word_char(int c){ +static int sqlite3re_word_char(int c){ return (c>='0' && c<='9') || (c>='a' && c<='z') || (c>='A' && c<='Z') || c=='_'; } /* Return true if c is a "digit" character: [0-9] */ -static int re_digit_char(int c){ +static int sqlite3re_digit_char(int c){ return (c>='0' && c<='9'); } /* Return true if c is a perl "space" character: [ \t\r\n\v\f] */ -static int re_space_char(int c){ +static int sqlite3re_space_char(int c){ return c==' ' || c=='\t' || c=='\n' || c=='\r' || c=='\v' || c=='\f'; } /* Run a compiled regular expression on the zero-terminated input ** string zIn[]. Return true on a match and false if there is no match. */ -int re_match(ReCompiled *pRe, const unsigned char *zIn, int nIn){ - ReStateSet aStateSet[2], *pThis, *pNext; - ReStateNumber aSpace[100]; - ReStateNumber *pToFree; +int sqlite3re_match(sqlite3_ReCompiled *pRe, const unsigned char *zIn, int nIn){ + sqlite3_ReStateSet aStateSet[2], *pThis, *pNext; + sqlite3_ReStateNumber aSpace[100]; + sqlite3_ReStateNumber *pToFree; unsigned int i = 0; unsigned int iSwap = 0; int c = RE_EOF+1; int cPrev = 0; int rc = 0; - ReInput in; + sqlite3_ReInput in; in.z = zIn; in.i = 0; in.mx = nIn>=0 ? nIn : (int)strlen((char const*)zIn); @@ -233,18 +226,18 @@ if( pRe->nState<=(sizeof(aSpace)/(sizeof(aSpace[0])*2)) ){ pToFree = 0; aStateSet[0].aState = aSpace; }else{ - pToFree = sqlite3_malloc( sizeof(ReStateNumber)*2*pRe->nState ); + pToFree = sqlite3_malloc( sizeof(sqlite3_ReStateNumber)*2*pRe->nState ); if( pToFree==0 ) return -1; aStateSet[0].aState = pToFree; } aStateSet[1].aState = &aStateSet[0].aState[pRe->nState]; pNext = &aStateSet[1]; pNext->nState = 0; - re_add_state(pNext, 0); + sqlite3re_add_state(pNext, 0); while( c!=RE_EOF && pNext->nState>0 ){ cPrev = c; c = pRe->xNextChar(&in); pThis = pNext; pNext = &aStateSet[iSwap]; @@ -252,57 +245,57 @@ pNext->nState = 0; for(i=0; inState; i++){ int x = pThis->aState[i]; switch( pRe->aOp[x] ){ case RE_OP_MATCH: { - if( pRe->aArg[x]==c ) re_add_state(pNext, x+1); + if( pRe->aArg[x]==c ) sqlite3re_add_state(pNext, x+1); break; } case RE_OP_ANY: { - re_add_state(pNext, x+1); + sqlite3re_add_state(pNext, x+1); break; } case RE_OP_WORD: { - if( re_word_char(c) ) re_add_state(pNext, x+1); + if( sqlite3re_word_char(c) ) sqlite3re_add_state(pNext, x+1); break; } case RE_OP_NOTWORD: { - if( !re_word_char(c) ) re_add_state(pNext, x+1); + if( !sqlite3re_word_char(c) ) sqlite3re_add_state(pNext, x+1); break; } case RE_OP_DIGIT: { - if( re_digit_char(c) ) re_add_state(pNext, x+1); + if( sqlite3re_digit_char(c) ) sqlite3re_add_state(pNext, x+1); break; } case RE_OP_NOTDIGIT: { - if( !re_digit_char(c) ) re_add_state(pNext, x+1); + if( !sqlite3re_digit_char(c) ) sqlite3re_add_state(pNext, x+1); break; } case RE_OP_SPACE: { - if( re_space_char(c) ) re_add_state(pNext, x+1); + if( sqlite3re_space_char(c) ) sqlite3re_add_state(pNext, x+1); break; } case RE_OP_NOTSPACE: { - if( !re_space_char(c) ) re_add_state(pNext, x+1); + if( !sqlite3re_space_char(c) ) sqlite3re_add_state(pNext, x+1); break; } case RE_OP_BOUNDARY: { - if( re_word_char(c)!=re_word_char(cPrev) ) re_add_state(pThis, x+1); + if( sqlite3re_word_char(c)!=sqlite3re_word_char(cPrev) ) sqlite3re_add_state(pThis, x+1); break; } case RE_OP_ANYSTAR: { - re_add_state(pNext, x); - re_add_state(pThis, x+1); + sqlite3re_add_state(pNext, x); + sqlite3re_add_state(pThis, x+1); break; } case RE_OP_FORK: { - re_add_state(pThis, x+pRe->aArg[x]); - re_add_state(pThis, x+1); + sqlite3re_add_state(pThis, x+pRe->aArg[x]); + sqlite3re_add_state(pThis, x+1); break; } case RE_OP_GOTO: { - re_add_state(pThis, x+pRe->aArg[x]); + sqlite3re_add_state(pThis, x+pRe->aArg[x]); break; } case RE_OP_ACCEPT: { rc = 1; goto re_match_end; @@ -326,11 +319,11 @@ j++; } } } if( pRe->aOp[x]==RE_OP_CC_EXC ) hit = !hit; - if( hit ) re_add_state(pNext, x+n); + if( hit ) sqlite3re_add_state(pNext, x+n); break; } } } } @@ -342,11 +335,11 @@ return rc; } /* Resize the opcode and argument arrays for an RE under construction. */ -static int re_resize(ReCompiled *p, int N){ +static int sqlite3re_resize(sqlite3_ReCompiled *p, int N){ char *aOp; int *aArg; aOp = sqlite3_realloc(p->aOp, N*sizeof(p->aOp[0])); if( aOp==0 ) return 1; p->aOp = aOp; @@ -358,13 +351,13 @@ } /* Insert a new opcode and argument into an RE under construction. The ** insertion point is just prior to existing opcode iBefore. */ -static int re_insert(ReCompiled *p, int iBefore, int op, int arg){ +static int sqlite3re_insert(sqlite3_ReCompiled *p, int iBefore, int op, int arg){ int i; - if( p->nAlloc<=p->nState && re_resize(p, p->nAlloc*2) ) return 0; + if( p->nAlloc<=p->nState && sqlite3re_resize(p, p->nAlloc*2) ) return 0; for(i=p->nState; i>iBefore; i--){ p->aOp[i] = p->aOp[i-1]; p->aArg[i] = p->aArg[i-1]; } p->nState++; @@ -373,29 +366,29 @@ return iBefore; } /* Append a new opcode and argument to the end of the RE under construction. */ -static int re_append(ReCompiled *p, int op, int arg){ - return re_insert(p, p->nState, op, arg); +static int sqlite3re_append(sqlite3_ReCompiled *p, int op, int arg){ + return sqlite3re_insert(p, p->nState, op, arg); } /* Make a copy of N opcodes starting at iStart onto the end of the RE ** under construction. */ -static void re_copy(ReCompiled *p, int iStart, int N){ - if( p->nState+N>=p->nAlloc && re_resize(p, p->nAlloc*2+N) ) return; +static void sqlite3re_copy(sqlite3_ReCompiled *p, int iStart, int N){ + if( p->nState+N>=p->nAlloc && sqlite3re_resize(p, p->nAlloc*2+N) ) return; memcpy(&p->aOp[p->nState], &p->aOp[iStart], N*sizeof(p->aOp[0])); memcpy(&p->aArg[p->nState], &p->aArg[iStart], N*sizeof(p->aArg[0])); p->nState += N; } /* Return true if c is a hexadecimal digit character: [0-9a-fA-F] ** If c is a hex digit, also set *pV = (*pV)*16 + valueof(c). If ** c is not a hex digit *pV is unchanged. */ -static int re_hex(int c, int *pV){ +static int sqlite3re_hex(int c, int *pV){ if( c>='0' && c<='9' ){ c -= '0'; }else if( c>='a' && c<='f' ){ c -= 'a' - 10; }else if( c>='A' && c<='F' ){ @@ -408,32 +401,32 @@ } /* A backslash character has been seen, read the next character and ** return its interpretation. */ -static unsigned re_esc_char(ReCompiled *p){ +static unsigned sqlite3re_esc_char(sqlite3_ReCompiled *p){ static const char zEsc[] = "afnrtv\\()*.+?[$^{|}]"; static const char zTrans[] = "\a\f\n\r\t\v"; int i, v = 0; char c; if( p->sIn.i>=p->sIn.mx ) return 0; c = p->sIn.z[p->sIn.i]; if( c=='u' && p->sIn.i+4sIn.mx ){ const unsigned char *zIn = p->sIn.z + p->sIn.i; - if( re_hex(zIn[1],&v) - && re_hex(zIn[2],&v) - && re_hex(zIn[3],&v) - && re_hex(zIn[4],&v) + if( sqlite3re_hex(zIn[1],&v) + && sqlite3re_hex(zIn[2],&v) + && sqlite3re_hex(zIn[3],&v) + && sqlite3re_hex(zIn[4],&v) ){ p->sIn.i += 5; return v; } } if( c=='x' && p->sIn.i+2sIn.mx ){ const unsigned char *zIn = p->sIn.z + p->sIn.i; - if( re_hex(zIn[1],&v) - && re_hex(zIn[2],&v) + if( sqlite3re_hex(zIn[1],&v) + && sqlite3re_hex(zIn[2],&v) ){ p->sIn.i += 3; return v; } } @@ -446,33 +439,33 @@ } return c; } /* Forward declaration */ -static const char *re_subcompile_string(ReCompiled*); +static const char* sqlite3re_subcompile_string(sqlite3_ReCompiled*); /* Peek at the next byte of input */ -static unsigned char rePeek(ReCompiled *p){ +static unsigned char sqlite3rePeek(sqlite3_ReCompiled *p){ return p->sIn.isIn.mx ? p->sIn.z[p->sIn.i] : 0; } /* Compile RE text into a sequence of opcodes. Continue up to the ** first unmatched ")" character, then return. If an error is found, ** return a pointer to the error message string. */ -static const char *re_subcompile_re(ReCompiled *p){ +static const char* sqlite3re_subcompile_re(sqlite3_ReCompiled *p){ const char *zErr; int iStart, iEnd, iGoto; iStart = p->nState; - zErr = re_subcompile_string(p); + zErr = sqlite3re_subcompile_string(p); if( zErr ) return zErr; - while( rePeek(p)=='|' ){ + while( sqlite3rePeek(p)=='|' ){ iEnd = p->nState; - re_insert(p, iStart, RE_OP_FORK, iEnd + 2 - iStart); - iGoto = re_append(p, RE_OP_GOTO, 0); + sqlite3re_insert(p, iStart, RE_OP_FORK, iEnd + 2 - iStart); + iGoto = sqlite3re_append(p, RE_OP_GOTO, 0); p->sIn.i++; - zErr = re_subcompile_string(p); + zErr = sqlite3re_subcompile_string(p); if( zErr ) return zErr; p->aArg[iGoto] = p->nState - iGoto; } return 0; } @@ -479,11 +472,11 @@ /* Compile an element of regular expression text (anything that can be ** an operand to the "|" operator). Return NULL on success or a pointer ** to the error message if there is a problem. */ -static const char *re_subcompile_string(ReCompiled *p){ +static const char* sqlite3re_subcompile_string(sqlite3_ReCompiled *p){ int iPrev = -1; int iStart; unsigned c; const char *zErr; while( (c = p->xNextChar(&p->sIn))!=0 ){ @@ -494,103 +487,103 @@ case ')': { p->sIn.i--; return 0; } case '(': { - zErr = re_subcompile_re(p); + zErr = sqlite3re_subcompile_re(p); if( zErr ) return zErr; - if( rePeek(p)!=')' ) return "unmatched '('"; + if( sqlite3rePeek(p)!=')' ) return "unmatched '('"; p->sIn.i++; break; } case '.': { - if( rePeek(p)=='*' ){ - re_append(p, RE_OP_ANYSTAR, 0); + if( sqlite3rePeek(p)=='*' ){ + sqlite3re_append(p, RE_OP_ANYSTAR, 0); p->sIn.i++; }else{ - re_append(p, RE_OP_ANY, 0); + sqlite3re_append(p, RE_OP_ANY, 0); } break; } case '*': { if( iPrev<0 ) return "'*' without operand"; - re_insert(p, iPrev, RE_OP_GOTO, p->nState - iPrev + 1); - re_append(p, RE_OP_FORK, iPrev - p->nState + 1); + sqlite3re_insert(p, iPrev, RE_OP_GOTO, p->nState - iPrev + 1); + sqlite3re_append(p, RE_OP_FORK, iPrev - p->nState + 1); break; } case '+': { if( iPrev<0 ) return "'+' without operand"; - re_append(p, RE_OP_FORK, iPrev - p->nState); + sqlite3re_append(p, RE_OP_FORK, iPrev - p->nState); break; } case '?': { if( iPrev<0 ) return "'?' without operand"; - re_insert(p, iPrev, RE_OP_FORK, p->nState - iPrev+1); + sqlite3re_insert(p, iPrev, RE_OP_FORK, p->nState - iPrev+1); break; } case '{': { int m = 0, n = 0; int sz, j; if( iPrev<0 ) return "'{m,n}' without operand"; - while( (c=rePeek(p))>='0' && c<='9' ){ m = m*10 + c - '0'; p->sIn.i++; } + while( (c=sqlite3rePeek(p))>='0' && c<='9' ){ m = m*10 + c - '0'; p->sIn.i++; } n = m; if( c==',' ){ p->sIn.i++; n = 0; - while( (c=rePeek(p))>='0' && c<='9' ){ n = n*10 + c-'0'; p->sIn.i++; } + while( (c=sqlite3rePeek(p))>='0' && c<='9' ){ n = n*10 + c-'0'; p->sIn.i++; } } if( c!='}' ) return "unmatched '{'"; if( n>0 && nsIn.i++; sz = p->nState - iPrev; if( m==0 ){ if( n==0 ) return "both m and n are zero in '{m,n}'"; - re_insert(p, iPrev, RE_OP_FORK, sz+1); + sqlite3re_insert(p, iPrev, RE_OP_FORK, sz+1); n--; }else{ - for(j=1; j0 ){ - re_append(p, RE_OP_FORK, -sz); + sqlite3re_append(p, RE_OP_FORK, -sz); } break; } case '[': { int iFirst = p->nState; - if( rePeek(p)=='^' ){ - re_append(p, RE_OP_CC_EXC, 0); + if( sqlite3rePeek(p)=='^' ){ + sqlite3re_append(p, RE_OP_CC_EXC, 0); p->sIn.i++; }else{ - re_append(p, RE_OP_CC_INC, 0); + sqlite3re_append(p, RE_OP_CC_INC, 0); } while( (c = p->xNextChar(&p->sIn))!=0 ){ - if( c=='[' && rePeek(p)==':' ){ + if( c=='[' && sqlite3rePeek(p)==':' ){ return "POSIX character classes not supported"; } - if( c=='\\' ) c = re_esc_char(p); - if( rePeek(p)=='-' ){ - re_append(p, RE_OP_CC_RANGE, c); + if( c=='\\' ) c = sqlite3re_esc_char(p); + if( sqlite3rePeek(p)=='-' ){ + sqlite3re_append(p, RE_OP_CC_RANGE, c); p->sIn.i++; c = p->xNextChar(&p->sIn); - if( c=='\\' ) c = re_esc_char(p); - re_append(p, RE_OP_CC_RANGE, c); + if( c=='\\' ) c = sqlite3re_esc_char(p); + sqlite3re_append(p, RE_OP_CC_RANGE, c); }else{ - re_append(p, RE_OP_CC_VALUE, c); + sqlite3re_append(p, RE_OP_CC_VALUE, c); } - if( rePeek(p)==']' ){ p->sIn.i++; break; } + if( sqlite3rePeek(p)==']' ){ p->sIn.i++; break; } } if( c==0 ) return "unclosed '['"; p->aArg[iFirst] = p->nState - iFirst; break; } case '\\': { int specialOp = 0; - switch( rePeek(p) ){ + switch( sqlite3rePeek(p) ){ case 'b': specialOp = RE_OP_BOUNDARY; break; case 'd': specialOp = RE_OP_DIGIT; break; case 'D': specialOp = RE_OP_NOTDIGIT; break; case 's': specialOp = RE_OP_SPACE; break; case 'S': specialOp = RE_OP_NOTSPACE; break; @@ -597,19 +590,19 @@ case 'w': specialOp = RE_OP_WORD; break; case 'W': specialOp = RE_OP_NOTWORD; break; } if( specialOp ){ p->sIn.i++; - re_append(p, specialOp, 0); + sqlite3re_append(p, specialOp, 0); }else{ - c = re_esc_char(p); - re_append(p, RE_OP_MATCH, c); + c = sqlite3re_esc_char(p); + sqlite3re_append(p, RE_OP_MATCH, c); } break; } default: { - re_append(p, RE_OP_MATCH, c); + sqlite3re_append(p, RE_OP_MATCH, c); break; } } iPrev = iStart; } @@ -618,11 +611,11 @@ /* Free and reclaim all the memory used by a previously compiled ** regular expression. Applications should invoke this routine once ** for every call to re_compile() to avoid memory leaks. */ -void re_free(ReCompiled *pRe){ +void sqlite3re_free(sqlite3_ReCompiled *pRe){ if( pRe ){ sqlite3_free(pRe->aOp); sqlite3_free(pRe->aArg); sqlite3_free(pRe); } @@ -632,48 +625,48 @@ ** Compile a textual regular expression in zIn[] into a compiled regular ** expression suitable for us by re_match() and return a pointer to the ** compiled regular expression in *ppRe. Return NULL on success or an ** error message if something goes wrong. */ -const char *re_compile(ReCompiled **ppRe, const char *zIn, int noCase){ - ReCompiled *pRe; +const char* sqlite3re_compile(sqlite3_ReCompiled **ppRe, const char *zIn, int noCase){ + sqlite3_ReCompiled *pRe; const char *zErr; int i, j; *ppRe = 0; pRe = sqlite3_malloc( sizeof(*pRe) ); if( pRe==0 ){ return "out of memory"; } memset(pRe, 0, sizeof(*pRe)); - pRe->xNextChar = noCase ? re_next_char_nocase : re_next_char; - if( re_resize(pRe, 30) ){ - re_free(pRe); + pRe->xNextChar = noCase ? sqlite3re_next_char_nocase : sqlite3re_next_char; + if( sqlite3re_resize(pRe, 30) ){ + sqlite3re_free(pRe); return "out of memory"; } if( zIn[0]=='^' ){ zIn++; }else{ - re_append(pRe, RE_OP_ANYSTAR, 0); + sqlite3re_append(pRe, RE_OP_ANYSTAR, 0); } pRe->sIn.z = (unsigned char*)zIn; pRe->sIn.i = 0; pRe->sIn.mx = (int)strlen(zIn); - zErr = re_subcompile_re(pRe); + zErr = sqlite3re_subcompile_re(pRe); if( zErr ){ - re_free(pRe); + sqlite3re_free(pRe); return zErr; } - if( rePeek(pRe)=='$' && pRe->sIn.i+1>=pRe->sIn.mx ){ - re_append(pRe, RE_OP_MATCH, RE_EOF); - re_append(pRe, RE_OP_ACCEPT, 0); + if( sqlite3rePeek(pRe)=='$' && pRe->sIn.i+1>=pRe->sIn.mx ){ + sqlite3re_append(pRe, RE_OP_MATCH, RE_EOF); + sqlite3re_append(pRe, RE_OP_ACCEPT, 0); *ppRe = pRe; }else if( pRe->sIn.i>=pRe->sIn.mx ){ - re_append(pRe, RE_OP_ACCEPT, 0); + sqlite3re_append(pRe, RE_OP_ACCEPT, 0); *ppRe = pRe; }else{ - re_free(pRe); + sqlite3re_free(pRe); return "unrecognized character"; } /* The following is a performance optimization. If the regex begins with ** ".*" (if the input regex lacks an initial "^") and afterwards there are @@ -682,11 +675,11 @@ ** string looking for the initial match without having to run the whole ** regex engine over the string. Do not worry able trying to match ** unicode characters beyond plane 0 - those are very rare and this is ** just an optimization. */ if( pRe->aOp[0]==RE_OP_ANYSTAR ){ - for(j=0, i=1; jzInit)-2 && pRe->aOp[i]==RE_OP_MATCH; i++){ + for(j=0, i=1; j<(int)sizeof(pRe->zInit)-2 && pRe->aOp[i]==RE_OP_MATCH; i++){ unsigned x = pRe->aArg[i]; if( x<=127 ){ pRe->zInit[j++] = x; }else if( x<=0xfff ){ pRe->zInit[j++] = 0xc0 | (x>>6); @@ -717,23 +710,23 @@ static void re_sql_func( sqlite3_context *context, int argc, sqlite3_value **argv ){ - ReCompiled *pRe; /* Compiled regular expression */ + sqlite3_ReCompiled *pRe; /* Compiled regular expression */ const char *zPattern; /* The regular expression */ const unsigned char *zStr;/* String being searched */ const char *zErr; /* Compile error message */ int setAux = 0; /* True to invoke sqlite3_set_auxdata() */ pRe = sqlite3_get_auxdata(context, 0); if( pRe==0 ){ zPattern = (const char*)sqlite3_value_text(argv[0]); if( zPattern==0 ) return; - zErr = re_compile(&pRe, zPattern, 0); + zErr = sqlite3re_compile(&pRe, zPattern, 0); if( zErr ){ - re_free(pRe); + sqlite3re_free(pRe); sqlite3_result_error(context, zErr, -1); return; } if( pRe==0 ){ sqlite3_result_error_nomem(context); @@ -741,14 +734,14 @@ } setAux = 1; } zStr = (const unsigned char*)sqlite3_value_text(argv[1]); if( zStr!=0 ){ - sqlite3_result_int(context, re_match(pRe, zStr, -1)); + sqlite3_result_int(context, sqlite3re_match(pRe, zStr, -1)); } if( setAux ){ - sqlite3_set_auxdata(context, 0, pRe, (void(*)(void*))re_free); + sqlite3_set_auxdata(context, 0, pRe, (void(*)(void*))sqlite3re_free); } } /* ** Invoke this routine to register the regexp() function with the @@ -759,12 +752,12 @@ char **pzErrMsg, const struct sqlite3_api_routines *pApi ){ int rc = SQLITE_OK; #ifndef SQLITE_CORE - SQLITE_EXTENSION_INIT2(pApi) //sets up the 'vtable' to the host exe sqlite3 impl + SQLITE_EXTENSION_INIT2(pApi) /*sets up the 'vtable' to the host exe sqlite3 impl*/ #else #endif rc = sqlite3_create_function(db, "regexp", 2, SQLITE_UTF8, 0, re_sql_func, 0, 0); return rc; } Index: src/fsl_vtbl_fossil_settings.c ================================================================== --- src/fsl_vtbl_fossil_settings.c +++ src/fsl_vtbl_fossil_settings.c @@ -37,10 +37,11 @@ #include #include #endif #include "fossil-scm/fossil-ext_regexp.h" /*sqlite3's regex engine*/ +#include "fossil-scm/fossil-core.h" /*for some public defs*/ #include "fossil-scm/fossil-util.h" /*for some internal class defs*/ #ifndef COUNTOF #define COUNTOF(arr) (sizeof(arr)/sizeof(*arr)) @@ -87,20 +88,38 @@ const char* _pszName; /*setting name*/ bool _bIsVersionable; /*can it be in .fossil-settings?*/ const char* _pszDefault; /*the default value, if any*/ const char* _pszHelp; /*Help text, tooltip*/ int _nHelpTextID; /*id of Help text (for eventual i18n)*/ + bool _bIsMultivalue; /*should there be list processing?*/ const char* _pszRegexAccept; /*regex to validate input value*/ }; typedef struct tagSettingDesc SettingDesc; /*common validation regexes*/ -static const char sg_pszreBoolean[] = "^\\s*(true|false|0|1|on|off|yes|no)\\s*$"; -static const char sg_pszreNumber[] = "^\\s*\\d+\\s*$"; -static const char sg_pszreDecNumber[] = "^\\s*(\\d+\\.?\\d*)\\s*|\\s*(\\d*\\.?\\d+)\\s*$"; +/*Note: we have created some 'extended' metacharacters beyond those supported +by the regex engine proper: +\$i regex is case-insensitive +\$m regex is for a multi-valued setting (e.g. 'ignore-glob') +\$e regex should match the empty setting as well +these metacharacters must be at the beginning of the regex, because we handle +their processing programattically, and use the rest of the string to compile +as per usual. +These metacharacters are needed principally because MATCH_OR_FAIL doesn't +have context of the setting it is validating, to get to the metadata for that +setting. I could have made MATCH_OR_FAIL also take the setting name, so that +it can find the metatdata, but this way it is also generally useful for other +non-setting-related data validation purposes. +*/ +static const char sg_pszreNonvalidated[] = ".*"; +static const char sg_pszreBasicMultivalue[] = "\\$e\\$m^\\s*[^,\n\r]*\\s*$"; +static const char sg_pszreBoolean[] = "\\$e\\$i^\\s*(true|false|1|0|on|off|yes|no)\\s*$"; +static const char sg_pszreNumber[] = "\\$e^\\s*\\d+\\s*$"; +static const char sg_pszreDecNumber[] = "\\$e^\\s*(\\d+\\.?\\d*)\\s*|\\s*(\\d*\\.?\\d+)\\s*$"; +static const char sg_pszrePermissions[] = "^[abcdefghijklmnopqrstuvwxz]*$"; /*Note, these MUST be in alpha order by name, because we do binary searches*/ /*over the collection*/ @@ -110,10 +129,11 @@ false, "off", "If enabled, record successful and failed login attempts " "in the \"accesslog\" table.", 0, + false, sg_pszreBoolean }, { "allow-symlinks", true, @@ -121,19 +141,21 @@ "If enabled, don't follow symlinks, and instead treat " "them as symlinks on Unix. Has no effect on Windows " "(existing links in repository created on Unix become " "plain-text files with link destination path inside).", 0, + false, sg_pszreBoolean }, { "auto-captcha", false, "on", "If enabled, the Login page provides a button to " "fill in the captcha password.", 0, + false, sg_pszreBoolean }, { "auto-hyperlink", false, @@ -141,19 +163,21 @@ "Use javascript to enable hyperlinks on web pages " "for all users (regardless of the \"h\" privilege) if the " "User-Agent string in the HTTP header look like it came " "from real person, not a spider or bot.", 0, + false, sg_pszreBoolean }, { "auto-shun", false, "on", "If enabled, automatically pull the shunning list " "from a server to which the client autosyncs.", 0, + false, sg_pszreBoolean }, { "autosync", false, @@ -161,10 +185,11 @@ "If enabled, automatically pull prior to commit " "or update and automatically push after commit or " "tag or branch creation. If the value is \"pullonly\" " "then only pull operations occur automatically.", 0, + false, sg_pszreBoolean }, { "binary-glob", true, @@ -171,11 +196,12 @@ "", "The VALUE is a comma or newline-separated list of " "GLOB patterns that should be treated as binary files " "for committing and merging purposes. Example: *.jpg ", 0, - "" /*YYY need regex*/ + true, + sg_pszreBasicMultivalue }, { "case-sensitive", false, "false", @@ -182,10 +208,11 @@ "If TRUE, the files whose names differ only in case " "are considered distinct. If FALSE files whose names " "differ only in case are the same file.\r\n" "Defaults to TRUE for unix and FALSE for Cygwin, Mac and Windows.", 0, + false, sg_pszreBoolean }, { "clean-glob", true, @@ -193,20 +220,22 @@ "The VALUE is a comma or newline-separated list of GLOB " "patterns specifying files that the \"clean\" command will " "delete without prompting even when the -force flag has " "not been used. Example: *.a *.lib *.o", 0, - "" /*YYY need regex*/ + true, + sg_pszreBasicMultivalue }, { "clearsign", false, "off", "When enabled, fossil will attempt to sign all commits " "with gpg. When disabled (the default), commits will " "be unsigned.", 0, + false, sg_pszreBoolean }, { "crnl-glob", true, @@ -213,57 +242,69 @@ "", "A comma or newline-separated list of GLOB patterns for " "text files in which it is ok to have CR, CR+NL or mixed " "line endings. Set to \"*\" to disable CR+NL checking.", 0, - "" /*YYY need regex*/ + true, + sg_pszreBasicMultivalue }, { "default-perms", false, "u", "Permissions given automatically to new users. For more " "information on permissions see Users page in Server " "Administration of the HTTP UI.", 0, - "" /*YYY need regex*/ + false, + sg_pszrePermissions }, { "diff-binary", false, "true", "If TRUE (the default), permit files that may be binary " "or that match the \"binary-glob\" setting to be used with " "external diff programs. If FALSE, skip these files.", 0, + false, sg_pszreBoolean }, { "diff-command", false, "", "External command to run when performing a diff.\r\n" "If undefined, the internal text diff will be used.", 0, - "" /*YYY need regex*/ + false, + sg_pszreNonvalidated }, { "dont-push", false, "false", "Prevent this repository from pushing from client to " "server. Useful when setting up a private branch.", 0, + false, sg_pszreBoolean }, { "editor", false, +#ifdef WIN32 "notepad.exe", +#elif defined ( __APPLE__ ) || defined ( __OSX__ ) + "", /*YYY verify*/ +#else + "", /*YYY verify*/ +#endif "Text editor command used for check-in comments.", 0, - "" /*YYY need regex*/ + false, + sg_pszreNonvalidated }, { "empty-dirs", true, "", @@ -270,11 +311,12 @@ "A comma or newline-separated list of pathnames. On " "update and checkout commands, if no file or directory " "exists with that name, an empty directory will be " "created.", 0, - "" /*YYY need regex*/ + true, + sg_pszreBasicMultivalue }, { "encoding-glob", true, "", @@ -282,20 +324,28 @@ "patterns specifying files that the \"commit\" command will " "ignore when issuing warnings about text files that may " "use another encoding than ASCII or UTF-8. Set to \"*\" " "to disable encoding checking.", 0, - "" /*YYY need regex*/ + true, + sg_pszreBasicMultivalue }, { "gdiff-command", false, +#ifdef WIN32 "WinDiff.exe", +#elif defined ( __APPLE__ ) || defined ( __OSX__ ) + "", /*YYY verify*/ +#else + "", /*YYY verify*/ +#endif "External command to run when performing a graphical " "diff. If undefined, text diff will be used.", 0, - "" /*YYY need regex*/ + false, + sg_pszreNonvalidated }, { "gmerge-command", false, "", @@ -303,28 +353,31 @@ "on four files.\r\n" "Ex: kdiff3 \"%baseline\" \"%original\" \"%merge\" -o \"%output\"\r\n" "Ex: xxdiff \"%original\" \"%baseline\" \"%merge\" -M \"%output\"\r\n" "Ex: meld \"%baseline\" \"%original\" \"%merge\" \"%output\"", 0, - "" /*YYY need regex*/ + false, + sg_pszreNonvalidated }, { "http-port", false, "8080", "The TCP/IP port number to use by the \"server\"" "and \"ui\" commands.", 0, + false, sg_pszreNumber }, { "https-login", false, "", "Send login credentials using HTTPS instead of HTTP " "even if the login page request came via HTTP.", 0, + false, sg_pszreBoolean }, { "ignore-glob", true, @@ -332,21 +385,23 @@ "The VALUE is a comma or newline-separated list of GLOB " "patterns specifying files that the \"add\", \"addremove\", " "\"clean\", and \"extra\" commands will ignore.\r\n" "Example: *.log customCode.c notes.txt", 0, - "" /*YYY need regex*/ + true, + sg_pszreBasicMultivalue }, { "keep-glob", true, "", "The VALUE is a comma or newline-separated list of GLOB " "patterns specifying files that the \"clean\" command will " "keep.", 0, - "" /*YYY need regex*/ + true, + sg_pszreBasicMultivalue }, { "localauth", false, "false", @@ -353,28 +408,31 @@ "If enabled, require that HTTP connections from " "127.0.0.1 be authenticated by password. If " "false, all HTTP requests from localhost have " "unrestricted access to the repository.", 0, + false, sg_pszreBoolean }, { "main-branch", false, "trunk", "The primary branch for the project.", 0, - "" /*YYY need regex*/ + false, + sg_pszreNonvalidated /*YYY need regex*/ }, { "manifest", true, "off", "If enabled, automatically create files \"manifest\" and " "\"manifest.uuid\" in every checkout. The SQLite and " "Fossil repositories both require this.", 0, + false, sg_pszreBoolean }, { "max-loadavg", false, @@ -384,37 +442,41 @@ "value. \"0.0\" means no limit. This only works on unix.\r\n" "Only local settings of this value make a difference since " "when running as a web-server, Fossil does not open the " "global configuration database.", 0, + false, sg_pszreDecNumber }, { "max-upload", false, "250000", "A limit on the size of uplink HTTP requests. The " "default is 250000 bytes.", 0, + false, sg_pszreNumber }, { "mtime-changes", false, "on", "Use file modification times (mtimes) to detect when " "files have been modified.", 0, + false, sg_pszreBoolean }, { "pgp-command", false, "gpg --clearsign -o ", "Command used to clear-sign manifests at check-in.", 0, - "" /*YYY need regex*/ + false, + sg_pszreNonvalidated }, { "proxy", false, "", @@ -421,19 +483,21 @@ "URL of the HTTP proxy. If undefined or \"off\" then " "the \"http_proxy\" environment variable is consulted.\r\n" "If the http_proxy environment variable is undefined " "then a direct HTTP connection is used.", 0, - "" /*YYY need regex*/ + false, + sg_pszreNonvalidated /*YYY could/should be better?*/ }, { "relative-paths", false, "on", "When showing changes and extras, report paths relative " "to the current working directory.", 0, + false, sg_pszreBoolean }, { "repo-cksum", false, @@ -441,10 +505,11 @@ "Compute checksums over all files in each checkout " "as a double-check of correctness. Defaults to \"on\". " "Disable on large repositories for a performance " "improvement.", 0, + false, sg_pszreBoolean }, { "self-register", false, @@ -452,20 +517,22 @@ "Allow users to register themselves through the HTTP UI.\r\n" "This is useful if you want to see other names than " "\"Anonymous\" in e.g. ticketing system. On the other hand " "users can not be deleted.", 0, + false, sg_pszreBoolean }, { "ssh-command", false, "", "Command used to talk to a remote machine with " "the \"ssh://\" protocol.", 0, - "" /*YYY need regex*/ + false, + sg_pszreNonvalidated }, { "ssl-ca-location", false, "", @@ -478,11 +545,12 @@ "Some platforms may add additional certificates.\r\n" "Check your platform behaviour is as required if the " "exact contents of the CA root is critical for your " "application.", 0, - "" /*YYY need regex*/ + false, + sg_pszreNonvalidated }, { "ssl-identity", false, "", @@ -491,11 +559,12 @@ "the certificate and private key files.\r\n" "This identity will be presented to SSL servers to " "authenticate this client, in addition to the normal " "password authentication.", 0, - "" /*YYY need regex*/ + false, + sg_pszreNonvalidated }, { "tcl", false, "off", @@ -504,10 +573,11 @@ "interpreter, allowing arbitrary Tcl expressions and " "scripts to be evaluated from TH1. Additionally, the Tcl " "interpreter will be able to evaluate arbitrary TH1 " "expressions and scripts.", 0, + false, sg_pszreBoolean }, { "tcl-setup", true, @@ -514,46 +584,56 @@ "", "This is the setup script to be evaluated after creating " "and initializing the Tcl interpreter. By default, this " "is empty and no extra setup is performed.", 0, - "" /*YYY need regex*/ + false, + sg_pszreNonvalidated }, { "th1-setup", true, "", "This is the setup script to be evaluated after creating " "and initializing the TH1 interpreter. By default, this " "is empty and no extra setup is performed.", 0, - "" /*YYY need regex*/ + false, + sg_pszreNonvalidated }, { "th1-uri-regexp", true, "", "Specify which URI's are allowed in HTTP requests from " "TH1 scripts. If empty, no HTTP requests are allowed " "whatsoever. The default is an empty string.", 0, - "" /*YYY need regex*/ + false, + sg_pszreNonvalidated }, { "web-browser", false, +#ifdef WIN32 "start", +#elif defined ( __APPLE__ ) || defined ( __OSX__ ) + "open", +#else + "firefox", +#endif "A shell command used to launch your preferred " "web browser when given a URL as an argument.\r\n" "Defaults to \"start\" on windows, \"open\" on Mac, " "and \"firefox\" on Unix.", 0, - "" /*YYY need regex*/ + false, + sg_pszreNonvalidated }, }; -const size_t g_asdSettingsDescriptorsCount = COUNTOF(g_asdSettingsDescriptors); +static const size_t g_asdSettingsDescriptorsCount = COUNTOF(g_asdSettingsDescriptors); /*=========================================== @@ -568,20 +648,20 @@ /*Virtual table implementations will typically add additional fields*/ }; typedef struct tagvtblSetMetDat_table vtblSetMetDat_table; -vtblSetMetDat_table* vtblSetMetDat_table_new ( void ) { +static vtblSetMetDat_table* vtblSetMetDat_table_new ( void ) { vtblSetMetDat_table* pThis = (vtblSetMetDat_table*) malloc ( sizeof(vtblSetMetDat_table) ); /*any ctor actions...*/ /*YYY (BBB docs say lib will set these up, but it doesn't. it does setup pModule, however)*/ pThis->_base.nRef = 0; /*unused, but we clear it anyway*/ pThis->_base.zErrMsg = NULL; /*ensure null lest there be crashes when lib tries to free*/ return pThis; } -void vtblSetMetDat_table_delete ( vtblSetMetDat_table* pThis ) { +static void vtblSetMetDat_table_delete ( vtblSetMetDat_table* pThis ) { if ( NULL != pThis ) { /*any dtor actions...*/ free ( pThis ); } @@ -596,19 +676,19 @@ size_t _nIdxEntry; }; typedef struct tagvtblSetMetDat_cursor vtblSetMetDat_cursor; -vtblSetMetDat_cursor* vtblSetMetDat_cursor_new() { +static vtblSetMetDat_cursor* vtblSetMetDat_cursor_new() { vtblSetMetDat_cursor* pThis = (vtblSetMetDat_cursor*) malloc ( sizeof(vtblSetMetDat_cursor) ); /*(any ctor actions...)*/ pThis->_nIdxEntry = 0; return pThis; } -void vtblSetMetDat_cursor_delete ( vtblSetMetDat_cursor* pThis ) { +static void vtblSetMetDat_cursor_delete ( vtblSetMetDat_cursor* pThis ) { if ( NULL != pThis ) { /*(any dtor actions...)*/ free ( pThis ); } } @@ -620,11 +700,11 @@ /*'table' stuff*/ /*xCreate -- 'create virtual table' called; make a sqlite3_vtab*/ -int vtblSetMetDat_Create ( sqlite3* db, void* pAux, int argc, const char*const* argv, sqlite3_vtab** ppVTab, char** pzErr ) { +static int vtblSetMetDat_Create ( sqlite3* db, void* pAux, int argc, const char*const* argv, sqlite3_vtab** ppVTab, char** pzErr ) { /*argv[0] / *name of module*/ /*argv[1] / *name of database; "main", "temp", ...*/ /*argv[2] / *name of the virtual table*/ /*argv[3...] / *arguments of the sql create virtual table statement; if present*/ @@ -631,10 +711,11 @@ /*first, declare our effective schema*/ int ret = sqlite3_declare_vtab ( db, "CREATE TABLE [ignored] ( " "NAME TEXT," /*setting name*/ "ISVERSIONABLE INTEGER," /*can it be in .fossil-settings*/ "DEFAULT_VALUE TEXT," /*the default value, if any*/ + "VALUE TEXT HIDDEN," /*the default value, aliased to 'value'*/ "HELP TEXT," /*Help text, tooltip*/ "HELP_ID INTEGER," /*id of Help text (for eventual i18n)*/ "REGEX_ACCEPT TEXT" /*regex to validate input value*/ ")" ); if ( SQLITE_OK != ret ) @@ -648,36 +729,36 @@ /*xDestroy -- drop virtual table; the last one, so global cleanup can be done*/ /*last connection to this table is going away*/ -int vtblSetMetDat_Destroy ( sqlite3_vtab* pVTab ) { +static int vtblSetMetDat_Destroy ( sqlite3_vtab* pVTab ) { vtblSetMetDat_table* pThis = DOWNCAST(vtblSetMetDat_table,_base,pVTab); vtblSetMetDat_table_delete ( pThis ); return SQLITE_OK; } /*xConnect -- connect to an existing vtable; relevant for dealing with idempotently instantiated state, otherwise the same as xCreate */ /*new connection to existing table; we just treat it the same as 'create'*/ -int vtblSetMetDat_Connect ( sqlite3* db, void* pAux, int argc, const char*const* argv, sqlite3_vtab** ppVTab, char** pzErr ) +static int vtblSetMetDat_Connect ( sqlite3* db, void* pAux, int argc, const char*const* argv, sqlite3_vtab** ppVTab, char** pzErr ) { return vtblSetMetDat_Create ( db, pAux, argc, argv, ppVTab, pzErr ); } /*xDisconnect -- drop virtual table, but not the last one*/ /*table is going away, but not last one*/ /*(BBB actually I have seen the last one go away via this method in the*/ /*case of database closure when there was not an explicit drop table)*/ -int vtblSetMetDat_Disconnect ( sqlite3_vtab* pVTab ) +static int vtblSetMetDat_Disconnect ( sqlite3_vtab* pVTab ) { return vtblSetMetDat_Destroy ( pVTab ); } /*xBestIndex -- prepare for query, indicate what filtering we can do efficiently ourselves*/ /*think about what query is being done, and indicate what possible internal*/ /*quasi-index we might have*/ -int vtblSetMetDat_BestIndex ( sqlite3_vtab* pVTab, sqlite3_index_info* pIdxInfo ) { +static int vtblSetMetDat_BestIndex ( sqlite3_vtab* pVTab, sqlite3_index_info* pIdxInfo ) { /* vtblSetMetDat_table* pThis = DOWNCAST(vtblSetMetDat_table,_base,pVTab); int nIdx; / *Inputs* / pIdxInfo->nConstraint; / *Number of entries in aConstraint* / @@ -717,26 +798,26 @@ /*===========================================*/ /*'cursor' stuff*/ /*xOpen -- open a cursor*/ -int vtblSetMetDat_cursor_Open ( sqlite3_vtab* pVTab, sqlite3_vtab_cursor** ppCursor ) { +static int vtblSetMetDat_cursor_Open ( sqlite3_vtab* pVTab, sqlite3_vtab_cursor** ppCursor ) { *ppCursor = &vtblSetMetDat_cursor_new()->_base; return SQLITE_OK; } /*xClose -- close a cursor*/ -int vtblSetMetDat_cursor_Close ( sqlite3_vtab_cursor* pCur ) { +static int vtblSetMetDat_cursor_Close ( sqlite3_vtab_cursor* pCur ) { vtblSetMetDat_cursor* pThis = DOWNCAST(vtblSetMetDat_cursor,_base,pCur); vtblSetMetDat_cursor_delete ( pThis ); return SQLITE_OK; } -int vtblSetMetDat_cursor_Filter ( sqlite3_vtab_cursor* pCur, int idxNum, const char* idxStr, int argc, sqlite3_value** argv ) { +static int vtblSetMetDat_cursor_Filter ( sqlite3_vtab_cursor* pCur, int idxNum, const char* idxStr, int argc, sqlite3_value** argv ) { vtblSetMetDat_cursor* pThis = DOWNCAST(vtblSetMetDat_cursor,_base,pCur); /*position cursor to location based on value arguments and parameters that ultimately came from xBestIndex*/ /*this will 'rewind' the cursor to the beginning*/ pThis->_nIdxEntry = 0; return SQLITE_OK; @@ -743,11 +824,11 @@ } /*xNext -- advance a cursor*/ -int vtblSetMetDat_cursor_Next ( sqlite3_vtab_cursor* pCur ) { +static int vtblSetMetDat_cursor_Next ( sqlite3_vtab_cursor* pCur ) { vtblSetMetDat_cursor* pThis = DOWNCAST(vtblSetMetDat_cursor,_base,pCur); /*advance a cursor*/ /*we iterate through the list*/ if ( pThis->_nIdxEntry != g_asdSettingsDescriptorsCount ) { /*still some left*/ ++pThis->_nIdxEntry; /*next*/ @@ -758,20 +839,20 @@ } /*xEof -- check for end of scan*/ -int vtblSetMetDat_cursor_Eof ( sqlite3_vtab_cursor* pCur ) { +static int vtblSetMetDat_cursor_Eof ( sqlite3_vtab_cursor* pCur ) { vtblSetMetDat_cursor* pThis = DOWNCAST(vtblSetMetDat_cursor,_base,pCur); /*check for end of iteration*/ return ( g_asdSettingsDescriptorsCount == pThis->_nIdxEntry ) ? 1 : 0; } /*xColumn -- read data*/ -int vtblSetMetDat_cursor_Column ( sqlite3_vtab_cursor* pCur, sqlite3_context* pCtx, int nCol ) { +static int vtblSetMetDat_cursor_Column ( sqlite3_vtab_cursor* pCur, sqlite3_context* pCtx, int nCol ) { vtblSetMetDat_cursor* pThis = DOWNCAST(vtblSetMetDat_cursor,_base,pCur); const SettingDesc* psd; /*read data*/ if ( g_asdSettingsDescriptorsCount == pThis->_nIdxEntry ) { /*shouldn't happen; I don't think*/ @@ -787,30 +868,32 @@ break; /*"ISVERSIONABLE INTEGER," / *can it be in .fossil-settings*/ case 1: sqlite3_result_int ( pCtx, psd->_bIsVersionable ? 1 : 0 ); break; - /*"DEFAULT TEXT," / *the default value, if any*/ + /*"DEFAULT_VALUE TEXT," / *the default value, if any*/ + /*"VALUE TEXT," / *the default value, aliased to 'value'*/ case 2: + case 3: if ( NULL == psd->_pszDefault || '\0' == psd->_pszDefault[0] ) sqlite3_result_null ( pCtx ); else sqlite3_result_text ( pCtx, psd->_pszDefault, -1, SQLITE_STATIC ); break; /*HELP TEXT," / *Help text, tooltip*/ - case 3: + case 4: if ( NULL == psd->_pszHelp || '\0' == psd->_pszHelp[0] ) sqlite3_result_null ( pCtx ); else sqlite3_result_text ( pCtx, psd->_pszHelp, -1, SQLITE_STATIC ); break; /*"HELP_ID INTEGER," / *id of Help text (for eventual i18n)*/ - case 4: + case 5: sqlite3_result_int ( pCtx, psd->_nHelpTextID ); break; /*"REGEX_ACCEPT TEXT" / *regex to validate input value*/ - case 5: + case 6: if ( NULL == psd->_pszRegexAccept || '\0' == psd->_pszRegexAccept[0] ) sqlite3_result_null ( pCtx ); else sqlite3_result_text ( pCtx, psd->_pszRegexAccept, -1, SQLITE_STATIC ); break; @@ -821,21 +904,21 @@ return SQLITE_OK; } /*xRowid -- read data*/ -int vtblSetMetDat_cursor_Rowid ( sqlite3_vtab_cursor* pCur, sqlite3_int64* pRowid ) { +static int vtblSetMetDat_cursor_Rowid ( sqlite3_vtab_cursor* pCur, sqlite3_int64* pRowid ) { vtblSetMetDat_cursor* pThis = DOWNCAST(vtblSetMetDat_cursor,_base,pCur); /*read data*/ *pRowid = pThis->_nIdxEntry; return SQLITE_OK; } -const sqlite3_module sg_pvtblSettingsMetadata = { +static const sqlite3_module sg_pvtblSettingsMetadata = { 2, /*iVersion -- version of this structure, so sqlite doesn't access past end*/ vtblSetMetDat_Create, /*xCreate -- 'create virtual table' called; make a sqlite3_vtab*/ vtblSetMetDat_Connect, /*xConnect -- connect to an existing vtable; relevant for dealing with idempotently instantiated state, otherwise the same as xCreate*/ vtblSetMetDat_BestIndex, /*xBestIndex -- prepare for query, indicate what filtering we can do efficiently ourselves*/ vtblSetMetDat_Disconnect, /*xDisconnect -- drop virtual table, but not the last one*/ @@ -878,31 +961,35 @@ /*since there are only a handful of 'versioned' settings that are supported,*/ /*and since the ones that are, are small textual values, we will just make*/ /*our life easier here, and read them all into memory at once.*/ - +/*for multi-valued types (e.g. 'ignore-glob'), we attempt to detect and +preserve the style of the original file to minimize diffs*/ typedef enum StyleValueSeparation { SEPSTYLE_UNKNOWN = 0, /*couldn't figure it out; probably because there were no multi-values*/ SEPSTYLE_NEWLINE = 1, SEPSTYLE_COMMA = 2, + SEPSTYLE_NONE = 3 /*this type is known to not be multivalue; inhibit list processing*/ } StyleValueSeparation; +/*we attempt to detect and preserve the newline style of the original +file to minimize diffs*/ typedef enum tagStyleLineEnding { NLSTYLE_UNKNOWN = 0, /*couldn't figure it out; probably because there were no newlines*/ NLSTYLE_CRLF = 1, /*DOS/Windows*/ NLSTYLE_CR = 2, /*Mac*/ - NLSTYLE_LF = 3, /*Unices*/ + NLSTYLE_LF = 3 /*Unices*/ } StyleLineEnding; #if defined(WIN32) || defined(WINDOWS) || defined(DOS) -const char sg_chPathSep = '\\'; +static const char sg_chPathSep = '\\'; #else -const char sg_chPathSep = '/'; +static const char sg_chPathSep = '/'; #endif struct tagSettingInfo { @@ -918,11 +1005,11 @@ sqlite_int64 _nRowId; /*sqlite needs this, and it can only be int64*/ }; typedef struct tagSettingInfo SettingInfo; -void SettingInfo_ctor ( SettingInfo* pThis ) { +static void SettingInfo_ctor ( SettingInfo* pThis ) { pThis->_strName = fsl_buffer_empty; /*name*/ pThis->_strValue = fsl_buffer_empty; /*value, normalized to CSV*/ /*We try to figure out the separators from the file itself, so we can*/ /*preserve those choices when persisting back out. This is done as a*/ /*courtesy, to avoid creating unnecessary diffs.*/ @@ -932,60 +1019,62 @@ /*synthesized properties*/ pThis->_nRowId = 0; /*sqlite needs this, and it can only be int64*/ } -void SettingInfo_dtor ( SettingInfo* pThis ) { +static void SettingInfo_dtor ( SettingInfo* pThis ) { fsl_buffer_clear( &pThis->_strName ); fsl_buffer_clear( &pThis->_strValue ); fsl_buffer_clear( &pThis->_strOriginalPath ); } -SettingInfo* SettingInfo_new ( void ) { +static SettingInfo* SettingInfo_new ( void ) { SettingInfo* pThis = (SettingInfo*) malloc ( sizeof(SettingInfo) ); SettingInfo_ctor ( pThis ); return pThis; } -void SettingInfo_delete ( SettingInfo* pThis ) { +static void SettingInfo_delete ( SettingInfo* pThis ) { if ( NULL != pThis ) { SettingInfo_dtor ( pThis ); free ( pThis ); } } -int fsl_list_visitor_free ( void* obj, void* visitorState ) { - free ( obj ); +static int fsl_list_visitor_free ( void* obj, void* visitorState ) { + fsl_free ( obj ); return 0; } -SettingInfo* SettingInfo_createFromFile ( const char* pszDir, const char* pszName ) { +static SettingInfo* SettingInfo_createFromFile ( const char* pszDir, const SettingDesc* pdesc ) { /*Note to self; this impl was originally in C++, where it (maybe) made a*/ /*little more sense, however a tidier impl comes to mind:*/ /** read the whole file into buffer*/ /** characterise the styles from that buffer*/ /** strpbrk out the comma, cr, lf -> nul*/ /* list_append the ptr into the buffer of the start of the value text*/ /** finally, transform the value into csv*/ /*this would avoid a lot of buffer copies and dtor code*/ + /*YYY Note: fsl_buffer_fill_from_filename can help*/ + /*YYY Note: fsl_glob_list_parse/fsl_glob_list_clear can help*/ StyleValueSeparation esvsSep; StyleLineEnding esleNewline; fsl_buffer strPath; struct _stat buf; fsl_buffer strValue; SettingInfo* psi; strPath = fsl_buffer_empty; - fsl_buffer_appendf ( &strPath, "%s%c%s", pszDir, sg_chPathSep, pszName ); + fsl_buffer_appendf ( &strPath, "%s%c%s", pszDir, sg_chPathSep, pdesc->_pszName ); /*if the file does not exist, we can't try to depersist*/ if ( 0 != _stat ( fsl_buffer_cstr ( &strPath ), &buf ) ) { fsl_buffer_clear ( &strPath ); return NULL; @@ -1068,21 +1157,36 @@ } } fclose ( f ); } - + /*well, OK, if metadata says we're not multivalue, override any inferred sep style*/ + if ( ! pdesc->_bIsMultivalue ) { + esvsSep = SEPSTYLE_NONE; + } + /*now, open file, read lines, parse seps, build string list, then*/ /*anneal string list into csv*/ - strValue = fsl_buffer_empty; { - fsl_list lstrValues; - fsl_buffer strToken; - char ch; - size_t nRead; - bool bEOF; - FILE* f; - fsl_size_t nIdx; + strValue = fsl_buffer_empty; + if ( SEPSTYLE_NONE == esvsSep ) { + if ( FSL_RC_OK != fsl_buffer_fill_from_filename ( &strValue, + fsl_buffer_cstr ( &strPath ) ) ) { + /*absurd; we just opened it a moment ago*/ + fsl_buffer_clear ( &strValue ); + fsl_buffer_clear ( &strPath ); + return NULL; + } + } + else { + /*YYY reimplement this using the fsl_glob_list_parse()*/ + fsl_list lstrValues; + fsl_buffer strToken; + char ch; + size_t nRead; + bool bEOF; + FILE* f; + fsl_size_t nIdx; lstrValues = fsl_list_empty; strToken = fsl_buffer_empty; f = fopen ( fsl_buffer_cstr ( &strPath ), "rb" ); @@ -1092,28 +1196,27 @@ fsl_list_reserve ( &lstrValues, 0 ); fsl_buffer_clear ( &strValue ); fsl_buffer_clear ( &strPath ); return NULL; } - + while ( nRead = fread ( &ch, sizeof(ch), 1, f ), bEOF = ( sizeof(ch) != nRead ), ! bEOF - ) - { + ) { if ( ',' == ch || '\r' == ch || '\n' == ch ) { if ( 0 != fsl_buffer_size ( &strToken ) ) { /*push token*/ - fsl_list_append ( &lstrValues, strdup ( fsl_buffer_cstr ( &strToken ) ) ); + fsl_list_append ( &lstrValues, fsl_strdup ( fsl_buffer_cstr ( &strToken ) ) ); fsl_buffer_reset ( &strToken ); } } else /*build token*/ fsl_buffer_append ( &strToken, &ch, sizeof(ch) ); } - if ( 0 != fsl_buffer_size ( &strToken ) ) { /*a last one? (like if fil ended without final sep)*/ - fsl_list_append ( &lstrValues, strdup ( fsl_buffer_cstr ( &strToken ) ) ); + if ( 0 != fsl_buffer_size ( &strToken ) ) { /*a last one? (like if file ended without final sep)*/ + fsl_list_append ( &lstrValues, fsl_strdup ( fsl_buffer_cstr ( &strToken ) ) ); fsl_buffer_reset ( &strToken ); } fclose ( f ); @@ -1120,21 +1223,21 @@ /*normalize into CSV*/ for ( nIdx = 0; nIdx < lstrValues.used; ++nIdx ) { char chComma = ','; if ( 0 != fsl_buffer_size ( &strValue ) ) fsl_buffer_append ( &strValue, &chComma, sizeof(chComma) ); - fsl_buffer_append ( &strValue, lstrValues.list[nIdx], strlen ( lstrValues.list[nIdx] ) ); + fsl_buffer_append ( &strValue, lstrValues.list[nIdx], fsl_strlen ( lstrValues.list[nIdx] ) ); } /*dtor*/ fsl_buffer_clear ( &strToken ); fsl_list_clear ( &lstrValues, fsl_list_visitor_free, NULL ); } /*OK, now we can get down to business...*/ psi = SettingInfo_new(); - fsl_buffer_append ( &psi->_strName, pszName, strlen ( pszName ) ); + fsl_buffer_append ( &psi->_strName, pdesc->_pszName, fsl_strlen ( pdesc->_pszName ) ); fsl_buffer_append ( &psi->_strOriginalPath, fsl_buffer_cstr ( &strPath ), fsl_buffer_size ( &strPath ) ); psi->_esvsSep = esvsSep; psi->_esleNewline = esleNewline; fsl_buffer_append ( &psi->_strValue, fsl_buffer_cstr ( &strValue ), fsl_buffer_size ( &strValue ) ); psi->_nRowId = 0; /*(parent will set this up)*/ @@ -1145,11 +1248,11 @@ return psi; } -SettingInfo* SettingInfo_createFromParams ( const char* pszDir, const char* pszName, +static SettingInfo* SettingInfo_createFromParams ( const char* pszDir, const char* pszName, const char* pszValue, StyleValueSeparation esvsSep, StyleLineEnding esleNewline ) { fsl_buffer strPath; SettingInfo* psi; strPath = fsl_buffer_empty; @@ -1169,55 +1272,64 @@ return psi; } -bool SettingInfo_saveToFile ( const SettingInfo* pThis ) { - FILE* f; - size_t nIdx; - - f = fopen ( fsl_buffer_cstr ( &pThis->_strOriginalPath ), "wb" ); - if ( NULL == f ) { - return false; - } - - /*emit characters in the string until we hit a ',' which we will*/ - /*translate into separators/newline according to style.*/ - for ( nIdx = 0; nIdx < fsl_buffer_size ( &pThis->_strValue ); ++nIdx ) { - if ( ',' == pThis->_strValue.mem[nIdx] ) { - if ( SEPSTYLE_COMMA == pThis->_esvsSep ) { - char ch = ','; - fwrite ( &ch, sizeof(ch), 1, f ); - } - else { - if ( NLSTYLE_CRLF == pThis->_esleNewline ) { - char ach[2] = { '\r', '\n' }; - fwrite ( ach, sizeof(ach), 1, f ); - } - else if ( NLSTYLE_CR == pThis->_esleNewline ) { - char ch = '\r'; - fwrite ( &ch, sizeof(ch), 1, f ); - } - else { /*NLSTYLE_LF*/ - char ch = '\n'; - fwrite ( &ch, sizeof(ch), 1, f ); - } - } - } - else { - fwrite ( &pThis->_strValue.mem[nIdx], sizeof(pThis->_strValue.mem[nIdx]), 1, f ); - } - } - - fclose ( f ); +static bool SettingInfo_saveToFile ( const SettingInfo* pThis ) { + + if ( SEPSTYLE_NONE == pThis->_esvsSep ) { + /*this is not a multivar; just emit the data verbatim*/ + if ( FSL_RC_OK != fsl_buffer_to_filename( &pThis->_strValue, + fsl_buffer_cstr ( &pThis->_strOriginalPath ) ) ) { + return false; + } + } + else { + FILE* f; + size_t nIdx; + + f = fopen ( fsl_buffer_cstr ( &pThis->_strOriginalPath ), "wb" ); + if ( NULL == f ) { + return false; + } + /*emit characters in the string until we hit a ',' which we will*/ + /*translate into separators/newline according to style.*/ + for ( nIdx = 0; nIdx < fsl_buffer_size ( &pThis->_strValue ); ++nIdx ) { + if ( ',' == pThis->_strValue.mem[nIdx] ) { + if ( SEPSTYLE_COMMA == pThis->_esvsSep ) { + char ch = ','; + fwrite ( &ch, sizeof(ch), 1, f ); + } + else { + if ( NLSTYLE_CRLF == pThis->_esleNewline ) { + char ach[2] = { '\r', '\n' }; + fwrite ( ach, sizeof(ach), 1, f ); + } + else if ( NLSTYLE_CR == pThis->_esleNewline ) { + char ch = '\r'; + fwrite ( &ch, sizeof(ch), 1, f ); + } + else { /*NLSTYLE_LF*/ + char ch = '\n'; + fwrite ( &ch, sizeof(ch), 1, f ); + } + } + } + else { + fwrite ( &pThis->_strValue.mem[nIdx], sizeof(pThis->_strValue.mem[nIdx]), 1, f ); + } + } + + fclose ( f ); + } return true; } -bool SettingInfo_removeFile ( const SettingInfo* pThis ) { +static bool SettingInfo_removeFile ( const SettingInfo* pThis ) { return 0 == _unlink ( fsl_buffer_cstr ( &pThis->_strOriginalPath ) ); } @@ -1228,11 +1340,11 @@ sqlite_int64 _nRowId; SettingInfo* _psi; }; typedef struct tagSettingCollTuple SettingCollTuple; -int fsl_list_visitor_settingtuple_free ( void* obj, void* visitorState ) { +static int fsl_list_visitor_settingtuple_free ( void* obj, void* visitorState ) { SettingCollTuple* ptpl = (SettingCollTuple*)obj; /*dtor on the tuple members*/ /*ptpl->_nRowId*/ SettingInfo_delete ( ptpl->_psi ); /*delete the tuple*/ @@ -1263,11 +1375,11 @@ int _nCursorsOpen; /*count of open cursors; some updating cannot happen with > 1*/ }; typedef struct tagvtblVerSet_table vtblVerSet_table; -void vtblVerSet_table_ctor ( vtblVerSet_table* pThis, sqlite3* db, const char* pszVerSettingsPath ) { +static void vtblVerSet_table_ctor ( vtblVerSet_table* pThis, sqlite3* db, const char* pszVerSettingsPath ) { /*YYY (BBB docs say lib will set these up, but it doesn't. it does setup pModule, however)*/ pThis->_base.nRef = 0; /*unused, but we clear it anyway*/ pThis->_base.zErrMsg = NULL; /*ensure null lest there be crashes when lib tries to free*/ pThis->_db = db; /*we need this from time-to-time*/ /*platform-specific defaults for multi-value and newline styles. These*/ @@ -1278,45 +1390,45 @@ pThis->_esleNewlinePlat = NLSTYLE_CRLF; /*use windowsian CRLF*/ #else pThis->_esleNewlinePlat = NLSTYLE_LF; /*fall back to unixian LF*/ #endif pThis->_strVerSettingsPath = fsl_buffer_empty; - fsl_buffer_append ( &pThis->_strVerSettingsPath, pszVerSettingsPath, strlen ( pszVerSettingsPath ) ); + fsl_buffer_append ( &pThis->_strVerSettingsPath, pszVerSettingsPath, fsl_strlen ( pszVerSettingsPath ) ); pThis->_nLastRowId = 0; pThis->_settings = fsl_list_empty; pThis->_nCursorsOpen = 0; } -void vtblVerSet_table_dtor ( vtblVerSet_table* pThis ) { +static void vtblVerSet_table_dtor ( vtblVerSet_table* pThis ) { fsl_list_clear ( &pThis->_settings, fsl_list_visitor_settingtuple_free, NULL ); fsl_buffer_clear( &pThis->_strVerSettingsPath ); } -vtblVerSet_table* vtblVerSet_table_new ( sqlite3* db, const char* pszVerSettingsPath ) { +static vtblVerSet_table* vtblVerSet_table_new ( sqlite3* db, const char* pszVerSettingsPath ) { vtblVerSet_table* pThis = (vtblVerSet_table*) malloc ( sizeof(vtblVerSet_table) ); vtblVerSet_table_ctor ( pThis, db, pszVerSettingsPath ); return pThis; } -void vtblVerSet_table_delete ( vtblVerSet_table* pThis ) { +static void vtblVerSet_table_delete ( vtblVerSet_table* pThis ) { if ( NULL != pThis ) { vtblVerSet_table_dtor ( pThis ); free ( pThis ); } } -bool vtblVerSet_table_addExistingSetting ( vtblVerSet_table* pThis, const char* pszName ) { +static bool vtblVerSet_table_addExistingSetting ( vtblVerSet_table* pThis, const SettingDesc* pdesc ) { /*factory idiom*/ - SettingInfo* psi = SettingInfo_createFromFile ( fsl_buffer_cstr ( &pThis->_strVerSettingsPath ), pszName ); + SettingInfo* psi = SettingInfo_createFromFile ( fsl_buffer_cstr ( &pThis->_strVerSettingsPath ), pdesc ); if ( NULL != psi ) { SettingCollTuple* psct; if ( psi->_esvsSep == SEPSTYLE_UNKNOWN ) psi->_esvsSep = pThis->_esvsSepPlat; if ( psi->_esleNewline == NLSTYLE_UNKNOWN ) @@ -1331,15 +1443,15 @@ return NULL != psi; } -bool vtblVerSet_table_populate ( vtblVerSet_table* pThis ) { +static bool vtblVerSet_table_populate ( vtblVerSet_table* pThis ) { size_t nIdx; for ( nIdx = 0; nIdx < g_asdSettingsDescriptorsCount; ++nIdx ) { if ( g_asdSettingsDescriptors[nIdx]._bIsVersionable ) - vtblVerSet_table_addExistingSetting ( pThis, g_asdSettingsDescriptors[nIdx]._pszName ); + vtblVerSet_table_addExistingSetting ( pThis, &g_asdSettingsDescriptors[nIdx] ); } return true; } @@ -1347,11 +1459,11 @@ /*since there are only a few well-known versionable settings, I am simply going*/ /*to do a linear search for 'find' operations. The 'find' operation will*/ /*return the index into the _settings array (er, 'list'). You may use this*/ /*index to get at the setting object, or use it to erase the setting (and*/ /*manage the list correctly). Non-found items will return an index of -1.*/ -int vtblVerSet_table_find_setting_idx_by_rowid ( vtblVerSet_table* pThis, sqlite3_int64 nRowId ) { +static int vtblVerSet_table_find_setting_idx_by_rowid ( vtblVerSet_table* pThis, sqlite3_int64 nRowId ) { int nIdx; for ( nIdx = 0; nIdx < (int) pThis->_settings.used; ++nIdx ) { if ( ((SettingCollTuple*)(pThis->_settings.list[nIdx]))->_nRowId == nRowId ) return nIdx; } @@ -1358,11 +1470,11 @@ return -1; } -int vtblVerSet_table_find_setting_idx_by_name ( vtblVerSet_table* pThis, const char* pszName ) +static int vtblVerSet_table_find_setting_idx_by_name ( vtblVerSet_table* pThis, const char* pszName ) { int nIdx; for ( nIdx = 0; nIdx < (int) pThis->_settings.used; ++nIdx ) { if ( 0 == _stricmp ( fsl_buffer_cstr ( &((SettingCollTuple*)(pThis->_settings.list[nIdx]))->_psi->_strName ), @@ -1372,13 +1484,13 @@ return -1; } -void vtblVerSet_table_erase_setting_by_idx ( vtblVerSet_table* pThis, int nIdx ) { +static void vtblVerSet_table_erase_setting_by_idx ( vtblVerSet_table* pThis, int nIdx ) { SettingCollTuple* ptpl; - if ( nIdx < 0 || nIdx >= pThis->_settings.used ) + if ( nIdx < 0 || nIdx >= (int) pThis->_settings.used ) return; /*bogus; assert-worthy*/ /*delete the tuple*/ ptpl = (SettingCollTuple*)(pThis->_settings.list[nIdx]); fsl_list_visitor_settingtuple_free ( ptpl, NULL ); /*scootch memory and update counts*/ @@ -1401,33 +1513,33 @@ }; typedef struct tagvtblVerSet_cursor vtblVerSet_cursor; -void vtblVerSet_cursor_ctor ( vtblVerSet_cursor* pThis, vtblVerSet_table* pvst ) { +static void vtblVerSet_cursor_ctor ( vtblVerSet_cursor* pThis, vtblVerSet_table* pvst ) { pThis->_nIter = 0; pThis->_pvst = pvst; ++(pThis->_pvst->_nCursorsOpen); } -void vtblVerSet_cursor_dtor ( vtblVerSet_cursor* pThis ) { +static void vtblVerSet_cursor_dtor ( vtblVerSet_cursor* pThis ) { --(pThis->_pvst->_nCursorsOpen); } -vtblVerSet_cursor* vtblVerSet_cursor_new ( vtblVerSet_table* pvst ) { +static vtblVerSet_cursor* vtblVerSet_cursor_new ( vtblVerSet_table* pvst ) { vtblVerSet_cursor* pThis = (vtblVerSet_cursor*) malloc ( sizeof(vtblVerSet_cursor) ); vtblVerSet_cursor_ctor ( pThis, pvst ); return pThis; } -void vtblVerSet_cursor_delete ( vtblVerSet_cursor* pThis ) { +static void vtblVerSet_cursor_delete ( vtblVerSet_cursor* pThis ) { if ( NULL != pThis ) { vtblVerSet_cursor_dtor ( pThis ); free ( pThis ); } } @@ -1439,11 +1551,11 @@ /*'table' stuff*/ /*xCreate -- 'create virtual table' called; make a sqlite3_vtab*/ -int vtblVerSet_Create ( sqlite3* db, void* pAux, int argc, const char*const* argv, sqlite3_vtab** ppVTab, char** pzErr ) { +static int vtblVerSet_Create ( sqlite3* db, void* pAux, int argc, const char*const* argv, sqlite3_vtab** ppVTab, char** pzErr ) { int nLen; char* pszValue; fsl_buffer strVerSettingsPath; char ch; bool bMkdirOK; @@ -1463,11 +1575,11 @@ /*first arg is the fully-qualified path to the .fossil-settings directory*/ strVerSettingsPath = fsl_buffer_empty; /*knock off the SQL quotes*/ pszValue = (char*) argv[3]; - nLen = strlen ( pszValue ); + nLen = fsl_strlen ( pszValue ); if ( 0 != nLen && '\'' == pszValue[0] ) { ++pszValue; --nLen; } if ( 0 != nLen && '\'' == pszValue[nLen-1] ) { @@ -1565,39 +1677,39 @@ /*xDestroy -- drop virtual table; the last one, so global cleanup can be done*/ /*last connection to this table is going away*/ -int vtblVerSet_Destroy ( sqlite3_vtab* pVTab ) { +static int vtblVerSet_Destroy ( sqlite3_vtab* pVTab ) { vtblVerSet_table* pThis = DOWNCAST(vtblVerSet_table,_base,pVTab); vtblVerSet_table_delete ( pThis ); return SQLITE_OK; } /*xConnect -- connect to an existing vtable; relevant for dealing with idempotently instantiated state, otherwise the same as xCreate*/ /*new connection to existing table; we just treat it the same as 'create'*/ -int vtblVerSet_Connect ( sqlite3* db, void* pAux, int argc, const char*const* argv, sqlite3_vtab** ppVTab, char** pzErr ) { +static int vtblVerSet_Connect ( sqlite3* db, void* pAux, int argc, const char*const* argv, sqlite3_vtab** ppVTab, char** pzErr ) { return vtblVerSet_Create ( db, pAux, argc, argv, ppVTab, pzErr ); } /*xDisconnect -- drop virtual table, but not the last one*/ /*table is going away, but not last one*/ /*(BBB actually I have seen the last one go away via this method in the*/ /*case of database closure when there was not an explicit drop table)*/ -int vtblVerSet_Disconnect ( sqlite3_vtab* pVTab ) { +static int vtblVerSet_Disconnect ( sqlite3_vtab* pVTab ) { return vtblVerSet_Destroy ( pVTab ); } /*xBestIndex -- prepare for query, indicate what filtering we can do efficiently ourselves*/ /*think about what query is being done, and indicate what possible internal*/ /*quasi-index we might have*/ -int vtblVerSet_BestIndex ( sqlite3_vtab* pVTab, sqlite3_index_info* pIdxInfo ) { +static int vtblVerSet_BestIndex ( sqlite3_vtab* pVTab, sqlite3_index_info* pIdxInfo ) { /*vtblVerSet_table* pThis = DOWNCAST(vtblVerSet_table,_base,pVTab); int nIdx; / *Inputs* / pIdxInfo->nConstraint; / *Number of entries in aConstraint* / pIdxInfo->aConstraint[0].iColumn; / *Column on left-hand side of constraint* / @@ -1638,27 +1750,27 @@ /*'cursor' stuff*/ /*xOpen -- open a cursor*/ -int vtblVerSet_cursor_Open ( sqlite3_vtab* pVTab, sqlite3_vtab_cursor** ppCursor ) { +static int vtblVerSet_cursor_Open ( sqlite3_vtab* pVTab, sqlite3_vtab_cursor** ppCursor ) { vtblVerSet_table* pThis = DOWNCAST(vtblVerSet_table,_base,pVTab); *ppCursor = &vtblVerSet_cursor_new ( pThis )->_base; return SQLITE_OK; } /*xClose -- close a cursor*/ -int vtblVerSet_cursor_Close ( sqlite3_vtab_cursor* pCur ) { +static int vtblVerSet_cursor_Close ( sqlite3_vtab_cursor* pCur ) { vtblVerSet_cursor* pThis = DOWNCAST(vtblVerSet_cursor,_base,pCur); vtblVerSet_cursor_delete ( pThis ); return SQLITE_OK; } -int vtblVerSet_cursor_Filter ( sqlite3_vtab_cursor* pCur, int idxNum, const char* idxStr, int argc, sqlite3_value** argv ) { +static int vtblVerSet_cursor_Filter ( sqlite3_vtab_cursor* pCur, int idxNum, const char* idxStr, int argc, sqlite3_value** argv ) { vtblVerSet_cursor* pThis = DOWNCAST(vtblVerSet_cursor,_base,pCur); /*position cursor to location based on value arguments and parameters that ultimately came from xBestIndex*/ /*this will 'rewind' the cursor to the beginning*/ pThis->_nIter = 0; return SQLITE_OK; @@ -1665,11 +1777,11 @@ } /*xNext -- advance a cursor*/ -int vtblVerSet_cursor_Next ( sqlite3_vtab_cursor* pCur ) { +static int vtblVerSet_cursor_Next ( sqlite3_vtab_cursor* pCur ) { vtblVerSet_cursor* pThis = DOWNCAST(vtblVerSet_cursor,_base,pCur); /*advance a cursor*/ /*we iterate through the list*/ if ( pThis->_nIter < pThis->_pvst->_settings.used ) { /*still some left*/ ++pThis->_nIter; /*next*/ @@ -1680,20 +1792,20 @@ } /*xEof -- check for end of scan*/ -int vtblVerSet_cursor_Eof ( sqlite3_vtab_cursor* pCur ) { +static int vtblVerSet_cursor_Eof ( sqlite3_vtab_cursor* pCur ) { vtblVerSet_cursor* pThis = DOWNCAST(vtblVerSet_cursor,_base,pCur); /*check for end of iteration*/ return ( pThis->_nIter >= pThis->_pvst->_settings.used ) ? 1 : 0; } /*xColumn -- read data*/ -int vtblVerSet_cursor_Column ( sqlite3_vtab_cursor* pCur, sqlite3_context* pCtx, int nCol ) { +static int vtblVerSet_cursor_Column ( sqlite3_vtab_cursor* pCur, sqlite3_context* pCtx, int nCol ) { const SettingInfo* psi; vtblVerSet_cursor* pThis = DOWNCAST(vtblVerSet_cursor,_base,pCur); /*read data*/ psi = ((SettingCollTuple*)pThis->_pvst->_settings.list[pThis->_nIter])->_psi; @@ -1731,11 +1843,11 @@ } /*xRowid -- read data*/ -int vtblVerSet_cursor_Rowid ( sqlite3_vtab_cursor* pCur, sqlite3_int64* pRowid ) { +static int vtblVerSet_cursor_Rowid ( sqlite3_vtab_cursor* pCur, sqlite3_int64* pRowid ) { vtblVerSet_cursor* pThis = DOWNCAST(vtblVerSet_cursor,_base,pCur); /*read data*/ *pRowid = ((SettingCollTuple*)pThis->_pvst->_settings.list[pThis->_nIter])->_nRowId; return SQLITE_OK; } @@ -1757,11 +1869,11 @@ return false; } -int vtblVerSet_Update ( sqlite3_vtab* pVTab, int argc, sqlite3_value** argv, sqlite3_int64* pRowid ) { +static int vtblVerSet_Update ( sqlite3_vtab* pVTab, int argc, sqlite3_value** argv, sqlite3_int64* pRowid ) { vtblVerSet_table* pThis = DOWNCAST(vtblVerSet_table,_base,pVTab); /*All changes to a virtual table are made using the xUpdate method.*/ /*This one method can be used to insert, delete, or update.*/ @@ -1845,14 +1957,14 @@ pszName = (const char*) sqlite3_value_text ( argv[2] ); /*"VALUE TEXT," / *setting value, if any*/ pszValue = (const char*) sqlite3_value_text ( argv[3] ); /*"VALUE_SEPARATOR INTEGER HIDDEN," / *style of value separation*/ nsvs = SQLITE_NULL == sqlite3_value_type ( argv[4] ) ? - pThis->_esvsSepPlat : sqlite3_value_int ( argv[4] ); + (int) pThis->_esvsSepPlat : sqlite3_value_int ( argv[4] ); /*"LINE_ENDINGS INTEGER HIDDEN," / *style of line endings*/ nsle = SQLITE_NULL == sqlite3_value_type ( argv[5] ) ? - pThis->_esleNewlinePlat : sqlite3_value_int ( argv[5] ); + (int) pThis->_esleNewlinePlat : sqlite3_value_int ( argv[5] ); /*"ORIGINAL_PATH TEXT HIDDEN" / *path where this setting came from*/ /*you're never going to be able to set this, but we'll just ignore it if you try*/ /*argv[6]*/ /*first, some sanity checks on the data*/ @@ -1978,19 +2090,19 @@ pThis->_base.zErrMsg = sqlite3_mprintf ( "NAME cannot change" ); return SQLITE_ERROR; } /*"VALUE_SEPARATOR INTEGER HIDDEN," / *style of value separation*/ nsvs = SQLITE_NULL == sqlite3_value_type ( argv[4] ) ? - pThis->_esvsSepPlat : sqlite3_value_int ( argv[4] ); + (int) pThis->_esvsSepPlat : sqlite3_value_int ( argv[4] ); /*range check: nsvs must be a value enum value*/ if ( nsvs < SEPSTYLE_NEWLINE || nsvs > SEPSTYLE_COMMA ) { pThis->_base.zErrMsg = sqlite3_mprintf ( "VALUE_SEPARATOR must be a valid value" ); return SQLITE_CONSTRAINT; } /*"LINE_ENDINGS INTEGER HIDDEN," / *style of line endings*/ nsle = SQLITE_NULL == sqlite3_value_type ( argv[5] ) ? - pThis->_esvsSepPlat : sqlite3_value_int ( argv[5] ); + (int) pThis->_esvsSepPlat : sqlite3_value_int ( argv[5] ); /*range check: nsle must be a value enum value*/ if ( nsle < NLSTYLE_CRLF || nsvs > NLSTYLE_LF ) { pThis->_base.zErrMsg = sqlite3_mprintf ( "LINE_ENDINGS must be a valid value" ); return SQLITE_CONSTRAINT; } @@ -2062,10 +2174,90 @@ /*end a transaction on a virtual table*/ /*int vtblVerSet_Commit ( sqlite3_vtab* pVTab );*/ /*int vtblVerSet_Rollback ( sqlite3_vtab* pVTab );*/ + +/*here, we preprocess a regex for our 'extended' escapes (which must be +at the start of the regex; we just have a couple, and set bools to indicate +whether they were detected. we return a pointer to the 'rest' of the +regex, which can be processed as per usual.*/ +static const char* _implPreprocessRegex ( const char* pszRegex, + bool* pbInsensitive, bool* pbMultivar, bool* pbEmptyOK ) { + const char* pszRest = pszRegex; + *pbInsensitive = false; /*init defaults for params we are parsing*/ + *pbMultivar = false; + while ( '\0' != *pszRest ) { + /*must start with our special escape sequence*/ + if ( '\\' != pszRest[0] || '$' != pszRest[1] ) { + break; + } + /*must be one of our known instructions*/ + if ( 'i' == pszRest[2] ) { + *pbInsensitive = true; + } + else if ( 'm' == pszRest[2] ) { + *pbMultivar = true; + } + else if ( 'e' == pszRest[2] ) { + *pbEmptyOK = true; + } + else + break; + /*consume*/ + pszRest += 3; + } + return pszRest; +} + + +/*context data stored in 'aux data'*/ +typedef struct tagMatchOrFailFxnCtx MatchOrFailFxnCtx; +struct tagMatchOrFailFxnCtx +{ + sqlite3_ReCompiled* pRe; /* Compiled regular expression */ + bool bInsensitive; /*and rude*/ + bool bMultivar; /*must we crack it into several items?*/ + bool bEmptyOK; /*is empty input text considered a match?*/ +}; + +void MatchOrFailFxnCtx_delete ( MatchOrFailFxnCtx* pThis ) { + sqlite3re_free ( pThis->pRe ); + free ( pThis ); +} + +bool MatchOrFailFxnCtx_match ( MatchOrFailFxnCtx* pThis, const char* pszStr ) { + if ( '\0' == pszStr[0] && pThis->bEmptyOK ) { + return true; + } + else if ( ! pThis->bMultivar ) { + return sqlite3re_match ( pThis->pRe, (const unsigned char*)pszStr, -1 ); + } + else { + /*this is a 'multivar' value, so crack it, and test them all individually*/ + fsl_list lstrValues = fsl_list_empty; + fsl_size_t nIdx; + bool bResult; + /*while it is called a 'glob_list', in fact, it is just a list of strings*/ + if ( 0 != fsl_glob_list_parse ( &lstrValues, pszStr ) ) { + /*horror*/ + return false; + } + /*for each; match. return logical 'and' over all tests*/ + bResult = true; + for ( nIdx = 0; bResult && nIdx < lstrValues.used; ++nIdx ) { + if ( ! sqlite3re_match ( pThis->pRe, + (const unsigned char*)lstrValues.list[nIdx], -1 ) ) { + bResult = false; + } + } + fsl_glob_list_clear ( &lstrValues ); + return bResult; + } +} + + /*This is a sqlite3 extension function that either matches a text to a regex pattern, or fails (with a SQLITE_CONSTRAINT error. This is intended to be used for value validation for INSERTs or UPDATE, and was created for the fossil settings vtable, but it may be of general use, since it doesn't have @@ -2075,16 +2267,13 @@ This implementation uses the sqlite3 sample regexp engine, but it is simple enough that you could easily use another, more powerful, engine, like PCRE. argv[0] = text to match argv[1] = regex */ -void implMatchOrFail ( sqlite3_context* pctx, int argc, sqlite3_value** argv ) { - - ReCompiled* pRe; /* Compiled regular expression */ - const char* zPattern; /* The regular expression */ +static void implMatchOrFail ( sqlite3_context* pctx, int argc, sqlite3_value** argv ) { + MatchOrFailFxnCtx* pCtx; const char* zStr; /* String being searched */ - const char* zErr; /* Compile error message */ /*we registered this method explicitly as taking two parameters, so we don't need to check that assuption*/ /*we definitely need the text to test, and do some pre-checks*/ zStr = (const char*) sqlite3_value_text ( argv[0] ); @@ -2091,28 +2280,39 @@ if ( NULL == zStr ) { sqlite3_result_null ( pctx ); return; } - pRe = (ReCompiled*)sqlite3_get_auxdata ( pctx, 0 ); /*precompiled?*/ - if ( NULL == pRe ) { /*no, must compile it now*/ + /*see if we precomputed this, and compute if needed*/ + pCtx = (MatchOrFailFxnCtx*)sqlite3_get_auxdata ( pctx, 0 ); /*precomputed?*/ + if ( NULL == pCtx ) { /*no, must compile it now*/ + const char* zErr; /* Compile error message */ + sqlite3_ReCompiled* pRe; /* Compiled regular expression */ + const char* zPattern; /*The regular expression*/ + const char* pszProcessedPattern; /*the pattern after custom escapes*/ + bool bInsensitive; /*and rude*/ + bool bMultivar; /*must we crack it into several items?*/ + bool bEmptyOK; /*is the empty string OK?*/ + zPattern = (const char*) sqlite3_value_text ( argv[1] ); /*null pattern == null return*/ if ( NULL == zPattern ) { sqlite3_result_null ( pctx ); return; } - /*empty pattern == always match*/ + /*empty pattern == never match*/ if ( '\0' == zPattern[0] ) { - sqlite3_result_text ( pctx, zStr, -1, SQLITE_TRANSIENT ); + sqlite3_result_error_code ( pctx, SQLITE_CONSTRAINT ); return; } - /*XXX maybe empty pattern means 'never match, because .* can always match and ^$ doesn't compile*/ - /*XXX add two new metachars for insensitive and multivar; need to figure out what those can be, because I will have to emulate that here since it's not part of the regex engine*/ + /*parse off our special metachars, if any*/ + pszProcessedPattern = _implPreprocessRegex ( zPattern, + &bInsensitive, &bMultivar, &bEmptyOK ); - zErr = sqlite3re_compile ( &pRe, zPattern, 0 ); + /*compile regex machine*/ + zErr = sqlite3re_compile ( &pRe, pszProcessedPattern, bInsensitive ); if ( NULL != zErr ) { sqlite3re_free ( pRe ); sqlite3_result_error ( pctx, zErr, -1 ); return; } @@ -2119,15 +2319,21 @@ if ( NULL == pRe ) { sqlite3_result_error_nomem ( pctx ); return; } - sqlite3_set_auxdata ( pctx, 0, pRe, (void(*)(void*))sqlite3re_free ); + /*remember...*/ + pCtx = (MatchOrFailFxnCtx*) malloc ( sizeof(MatchOrFailFxnCtx) ); + pCtx->pRe = pRe; + pCtx->bInsensitive = bInsensitive; + pCtx->bMultivar = bMultivar; + pCtx->bEmptyOK = bEmptyOK; + sqlite3_set_auxdata ( pctx, 0, pCtx, (void(*)(void*))MatchOrFailFxnCtx_delete ); } - + /*OK, now, finally we can test*/ - if ( sqlite3re_match ( pRe, (const unsigned char*)zStr, -1 ) ) { + if ( MatchOrFailFxnCtx_match ( pCtx, zStr ) ) { sqlite3_result_text ( pctx, zStr, -1, SQLITE_TRANSIENT ); } else { sqlite3_result_error_code ( pctx, SQLITE_CONSTRAINT ); } @@ -2134,22 +2340,22 @@ } /*'overloading' functions*/ -int vtblVerSet_FindFunction ( sqlite3_vtab* pVtab, int nArg, const char* zName, +static int vtblVerSet_FindFunction ( sqlite3_vtab* pVtab, int nArg, const char* zName, void (**pxFunc) ( sqlite3_context*, int, sqlite3_value** ), void** ppArg ) { return 0; /*do not override*/ } -int vtblVerSet_Rename ( sqlite3_vtab* pVtab, const char* zNew ) { +static int vtblVerSet_Rename ( sqlite3_vtab* pVtab, const char* zNew ) { return SQLITE_OK; /*we don't care about being renamed*/ } -const sqlite3_module sg_pvtblVersionedSettings = { +static const sqlite3_module sg_pvtblVersionedSettings = { 2, /*iVersion -- version of this structure, so sqlite doesn't access past end*/ vtblVerSet_Create, /*xCreate -- 'create virtual table' called; make a sqlite3_vtab*/ vtblVerSet_Connect, /*xConnect -- connect to an existing vtable; relevant for dealing with idempotently instantiated state, otherwise the same as xCreate*/ vtblVerSet_BestIndex, /*xBestIndex -- prepare for query, indicate what filtering we can do efficiently ourselves*/ vtblVerSet_Disconnect, /*xDisconnect -- drop virtual table, but not the last one*/ @@ -2187,22 +2393,22 @@ int sqlite3_vtbl_fossilsettings_init ( sqlite3* db, char** pzErrMsg, const struct sqlite3_api_routines* pApi ) { - int nRet; + /* int nRet; */ #ifndef SQLITE_CORE SQLITE_EXTENSION_INIT2(pApi) /*sets up the 'vtable' to the host exe sqlite3 impl*/ #else #endif #ifndef SQLITE_OMIT_VIRTUALTABLE - nRet = sqlite3_create_module_v2 ( db, "SETTINGS_METADATA", &sg_pvtblSettingsMetadata, NULL, NULL ); - nRet = sqlite3_create_module_v2 ( db, "VERSIONED_SETTINGS", &sg_pvtblVersionedSettings, NULL, NULL ); + /* nRet = */sqlite3_create_module_v2 ( db, "SETTINGS_METADATA", &sg_pvtblSettingsMetadata, NULL, NULL ); + /* nRet = */sqlite3_create_module_v2 ( db, "VERSIONED_SETTINGS", &sg_pvtblVersionedSettings, NULL, NULL ); #endif - nRet = sqlite3_create_function ( db, "MATCH_OR_FAIL", 2, SQLITE_UTF8, 0, implMatchOrFail, 0, 0 ); + /* nRet = */sqlite3_create_function ( db, "MATCH_OR_FAIL", 2, SQLITE_UTF8, 0, implMatchOrFail, 0, 0 ); return SQLITE_OK; } Index: src/sqlite3.c ================================================================== --- src/sqlite3.c +++ src/sqlite3.c @@ -231,11 +231,11 @@ ** [sqlite3_libversion_number()], [sqlite3_sourceid()], ** [sqlite_version()] and [sqlite_source_id()]. */ #define SQLITE_VERSION "3.8.7" #define SQLITE_VERSION_NUMBER 3008007 -#define SQLITE_SOURCE_ID "2014-10-01 13:17:34 07c89940c49a5dca3205a4b6fa8290f23bcb6e10" +#define SQLITE_SOURCE_ID "2014-10-17 11:24:17 e4ab094f8afce0817f4074e823fabe59fc29ebb4" /* ** CAPI3REF: Run-Time Library Version Numbers ** KEYWORDS: sqlite3_version, sqlite3_sourceid ** @@ -2791,13 +2791,13 @@ ** [SQLITE_OK] is returned. Otherwise an [error code] is returned.)^ ^The ** [sqlite3_errmsg()] or [sqlite3_errmsg16()] routines can be used to obtain ** an English language description of the error following a failure of any ** of the sqlite3_open() routines. ** -** ^The default encoding for the database will be UTF-8 if -** sqlite3_open() or sqlite3_open_v2() is called and -** UTF-16 in the native byte order if sqlite3_open16() is used. +** ^The default encoding will be UTF-8 for databases created using +** sqlite3_open() or sqlite3_open_v2(). ^The default encoding for databases +** created using sqlite3_open16() will be UTF-16 in the native byte order. ** ** Whether or not an error occurs when it is opened, resources ** associated with the [database connection] handle should be released by ** passing it to [sqlite3_close()] when it is no longer required. ** @@ -2881,17 +2881,18 @@ ** ^SQLite uses the path component of the URI as the name of the disk file ** which contains the database. ^If the path begins with a '/' character, ** then it is interpreted as an absolute path. ^If the path does not begin ** with a '/' (meaning that the authority section is omitted from the URI) ** then the path is interpreted as a relative path. -** ^On windows, the first component of an absolute path -** is a drive specification (e.g. "C:"). +** ^(On windows, the first component of an absolute path +** is a drive specification (e.g. "C:").)^ ** ** [[core URI query parameters]] ** The query component of a URI may contain parameters that are interpreted ** either by SQLite itself, or by a [VFS | custom VFS implementation]. -** SQLite interprets the following three query parameters: +** SQLite and its built-in [VFSes] interpret the +** following query parameters: ** **
    **
  • vfs: ^The "vfs" parameter may be used to specify the name of ** a VFS object that provides the operating system interface that should ** be used to access the database file on disk. ^If this option is set to @@ -2922,15 +2923,13 @@ ** equivalent to setting the SQLITE_OPEN_PRIVATECACHE bit. ** ^If sqlite3_open_v2() is used and the "cache" parameter is present in ** a URI filename, its value overrides any behavior requested by setting ** SQLITE_OPEN_PRIVATECACHE or SQLITE_OPEN_SHAREDCACHE flag. ** -**
  • psow: ^The psow parameter may be "true" (or "on" or "yes" or -** "1") or "false" (or "off" or "no" or "0") to indicate that the +**
  • psow: ^The psow parameter indicates whether or not the ** [powersafe overwrite] property does or does not apply to the -** storage media on which the database file resides. ^The psow query -** parameter only works for the built-in unix and Windows VFSes. +** storage media on which the database file resides. ** **
  • nolock: ^The nolock parameter is a boolean query parameter ** which if set disables file locking in rollback journal modes. This ** is useful for accessing a database on a filesystem that does not ** support locking. Caution: Database corruption might result if two @@ -3521,15 +3520,14 @@ ** terminated. If any NUL characters occur at byte offsets less than ** the value of the fourth parameter then the resulting string value will ** contain embedded NULs. The result of expressions involving strings ** with embedded NULs is undefined. ** -** ^The fifth argument to sqlite3_bind_blob(), sqlite3_bind_text(), and -** sqlite3_bind_text16() is a destructor used to dispose of the BLOB or +** ^The fifth argument to the BLOB and string binding interfaces +** is a destructor used to dispose of the BLOB or ** string after SQLite has finished with it. ^The destructor is called -** to dispose of the BLOB or string even if the call to sqlite3_bind_blob(), -** sqlite3_bind_text(), or sqlite3_bind_text16() fails. +** to dispose of the BLOB or string even if the call to bind API fails. ** ^If the fifth argument is ** the special value [SQLITE_STATIC], then SQLite assumes that the ** information is in static, unmanaged space and does not need to be freed. ** ^If the fifth argument has the value [SQLITE_TRANSIENT], then ** SQLite makes its own private copy of the data immediately, before @@ -3536,11 +3534,11 @@ ** the sqlite3_bind_*() routine returns. ** ** ^The sixth argument to sqlite3_bind_text64() must be one of ** [SQLITE_UTF8], [SQLITE_UTF16], [SQLITE_UTF16BE], or [SQLITE_UTF16LE] ** to specify the encoding of the text in the third parameter. If -** the sixth argument to sqlite3_bind_text64() is not how of the +** the sixth argument to sqlite3_bind_text64() is not one of the ** allowed values shown above, or if the text encoding is different ** from the encoding specified by the sixth parameter, then the behavior ** is undefined. ** ** ^The sqlite3_bind_zeroblob() routine binds a BLOB of length N that @@ -4572,11 +4570,11 @@ ** ** ^The sqlite3_result_null() interface sets the return value ** of the application-defined function to be NULL. ** ** ^The sqlite3_result_text(), sqlite3_result_text16(), -** sqlite3_result_text16le(), and sqlite3_result_text16be() +** sqlite3_result_text16le(), and sqlite3_result_text16be() interfaces ** set the return value of the application-defined function to be ** a text string which is represented as UTF-8, UTF-16 native byte order, ** UTF-16 little endian, or UTF-16 big endian, respectively. ** ^The sqlite3_result_text64() interface sets the return value of an ** application-defined function to be a text string in an encoding @@ -7946,11 +7944,11 @@ ** A macro to hint to the compiler that a function should not be ** inlined. */ #if defined(__GNUC__) # define SQLITE_NOINLINE __attribute__((noinline)) -#elif defined(_MSC_VER) +#elif defined(_MSC_VER) && _MSC_VER>=1310 # define SQLITE_NOINLINE __declspec(noinline) #else # define SQLITE_NOINLINE #endif @@ -11324,10 +11322,11 @@ #ifdef SQLITE_ENABLE_STAT3_OR_STAT4 int nSample; /* Number of elements in aSample[] */ int nSampleCol; /* Size of IndexSample.anEq[] and so on */ tRowcnt *aAvgEq; /* Average nEq values for keys not in aSample */ IndexSample *aSample; /* Samples of the left-most key */ + tRowcnt *aiRowEst; /* Non-logarithmic stat1 data for this table */ #endif }; /* ** Allowed values for Index.idxType @@ -12188,11 +12187,10 @@ #define OPFLAG_EPHEM 0x01 /* OP_Column: Ephemeral output is ok */ #define OPFLAG_LASTROWID 0x02 /* Set to update db->lastRowid */ #define OPFLAG_ISUPDATE 0x04 /* This OP_Insert is an sql UPDATE */ #define OPFLAG_APPEND 0x08 /* This is likely to be an append */ #define OPFLAG_USESEEKRESULT 0x10 /* Try to avoid a seek in BtreeInsert() */ -#define OPFLAG_CLEARCACHE 0x20 /* Clear pseudo-table cache in OP_Column */ #define OPFLAG_LENGTHARG 0x40 /* OP_Column only used for length() */ #define OPFLAG_TYPEOFARG 0x80 /* OP_Column only used for typeof() */ #define OPFLAG_BULKCSR 0x01 /* OP_Open** used to open bulk cursor */ #define OPFLAG_P2ISREG 0x02 /* P2 to OP_Open** is a register number */ #define OPFLAG_PERMUTE 0x01 /* OP_Compare: use the permutation */ @@ -13322,14 +13320,13 @@ # define sqlite3MemdebugSetType(X,Y) /* no-op */ # define sqlite3MemdebugHasType(X,Y) 1 # define sqlite3MemdebugNoType(X,Y) 1 #endif #define MEMTYPE_HEAP 0x01 /* General heap allocations */ -#define MEMTYPE_LOOKASIDE 0x02 /* Might have been lookaside memory */ +#define MEMTYPE_LOOKASIDE 0x02 /* Heap that might have been lookaside */ #define MEMTYPE_SCRATCH 0x04 /* Scratch allocations */ #define MEMTYPE_PCACHE 0x08 /* Page cache allocations */ -#define MEMTYPE_DB 0x10 /* Uses sqlite3DbMalloc, not sqlite_malloc */ /* ** Threading interface */ #if SQLITE_MAX_WORKER_THREADS>0 @@ -13469,10 +13466,17 @@ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* f0..f7 ........ */ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40 /* f8..ff ........ */ }; #endif +/* EVIDENCE-OF: R-02982-34736 In order to maintain full backwards +** compatibility for legacy applications, the URI filename capability is +** disabled by default. +** +** EVIDENCE-OF: R-38799-08373 URI filenames can be enabled or disabled +** using the SQLITE_USE_URI=1 or SQLITE_USE_URI=0 compile-time options. +*/ #ifndef SQLITE_USE_URI # define SQLITE_USE_URI 0 #endif #ifndef SQLITE_ALLOW_COVERING_INDEX_SCAN @@ -14090,21 +14094,19 @@ #ifdef SQLITE_DEBUG u8 seekOp; /* Most recent seek operation on this cursor */ #endif i8 iDb; /* Index of cursor database in db->aDb[] (or -1) */ u8 nullRow; /* True if pointing to a row with no data */ - u8 rowidIsValid; /* True if lastRowid is valid */ u8 deferredMoveto; /* A call to sqlite3BtreeMoveto() is needed */ Bool isEphemeral:1; /* True for an ephemeral table */ Bool useRandomRowid:1;/* Generate new record numbers semi-randomly */ Bool isTable:1; /* True if a table requiring integer keys */ Bool isOrdered:1; /* True if the underlying table is BTREE_UNORDERED */ Pgno pgnoRoot; /* Root page of the open btree cursor */ sqlite3_vtab_cursor *pVtabCursor; /* The cursor for a virtual table */ i64 seqCount; /* Sequence counter */ i64 movetoTarget; /* Argument to the deferred sqlite3BtreeMoveto() */ - i64 lastRowid; /* Rowid being deleted by OP_Delete */ VdbeSorter *pSorter; /* Sorter object for OP_SorterOpen cursors */ /* Cached information about the header for the data record that the ** cursor is currently pointing to. Only valid if cacheStatus matches ** Vdbe.cacheCtr. Vdbe.cacheCtr will never take on the value of @@ -14117,10 +14119,11 @@ u32 cacheStatus; /* Cache is valid if this matches Vdbe.cacheCtr */ u32 payloadSize; /* Total number of bytes in the record */ u32 szRow; /* Byte available in aRow */ u32 iHdrOffset; /* Offset to next unparsed byte of the header */ const u8 *aRow; /* Data for the current row, if all on one page */ + u32 *aOffset; /* Pointer to aType[nField] */ u32 aType[1]; /* Type values for all entries in the record */ /* 2*nField extra array elements allocated for aType[], beyond the one ** static element declared in the structure. nField total array slots for ** aType[] and nField+1 array slots for aOffset[] */ }; @@ -14193,11 +14196,11 @@ int n; /* Number of characters in string value, excluding '\0' */ char *z; /* String or BLOB value */ /* ShallowCopy only needs to copy the information above */ char *zMalloc; /* Space to hold MEM_Str or MEM_Blob if szMalloc>0 */ int szMalloc; /* Size of the zMalloc allocation */ - int iPadding1; /* Padding for 8-byte alignment */ + u32 uTemp; /* Transient storage for serial_type in OP_MakeRecord */ sqlite3 *db; /* The associated database connection */ void (*xDel)(void*);/* Destructor for Mem.z - only valid if MEM_Dyn */ #ifdef SQLITE_DEBUG Mem *pScopyFrom; /* This Mem is a shallow copy of pScopyFrom */ void *pFiller; /* So that sizeof(Mem) is a multiple of 8 */ @@ -14401,10 +14404,11 @@ ** Function prototypes */ SQLITE_PRIVATE void sqlite3VdbeFreeCursor(Vdbe *, VdbeCursor*); void sqliteVdbePopStack(Vdbe*,int); SQLITE_PRIVATE int sqlite3VdbeCursorMoveto(VdbeCursor*); +SQLITE_PRIVATE int sqlite3VdbeCursorRestore(VdbeCursor*); #if defined(SQLITE_DEBUG) || defined(VDBE_PROFILE) SQLITE_PRIVATE void sqlite3VdbePrintOp(FILE*, int, Op*); #endif SQLITE_PRIVATE u32 sqlite3VdbeSerialTypeLen(u32); SQLITE_PRIVATE u32 sqlite3VdbeSerialType(Mem*, int); @@ -14700,11 +14704,11 @@ sqlite3VdbeClearObject(db, pVdbe); sqlite3DbFree(db, pVdbe); } db->pnBytesFreed = 0; - *pHighwater = 0; + *pHighwater = 0; /* IMP: R-64479-57858 */ *pCurrent = nByte; break; } @@ -14725,21 +14729,23 @@ if( db->aDb[i].pBt ){ Pager *pPager = sqlite3BtreePager(db->aDb[i].pBt); sqlite3PagerCacheStat(pPager, op, resetFlag, &nRet); } } - *pHighwater = 0; + *pHighwater = 0; /* IMP: R-42420-56072 */ + /* IMP: R-54100-20147 */ + /* IMP: R-29431-39229 */ *pCurrent = nRet; break; } /* Set *pCurrent to non-zero if there are unresolved deferred foreign ** key constraints. Set *pCurrent to zero if all foreign key constraints ** have been satisfied. The *pHighwater is always set to zero. */ case SQLITE_DBSTATUS_DEFERRED_FKS: { - *pHighwater = 0; + *pHighwater = 0; /* IMP: R-11967-56545 */ *pCurrent = db->nDeferredImmCons>0 || db->nDeferredCons>0; break; } default: { @@ -17123,11 +17129,11 @@ ** allocation p. Also return true if p==NULL. ** ** This routine is designed for use within an assert() statement, to ** verify the type of an allocation. For example: ** -** assert( sqlite3MemdebugHasType(p, MEMTYPE_DB) ); +** assert( sqlite3MemdebugHasType(p, MEMTYPE_HEAP) ); */ SQLITE_PRIVATE int sqlite3MemdebugHasType(void *p, u8 eType){ int rc = 1; if( p && sqlite3GlobalConfig.m.xMalloc==sqlite3MemMalloc ){ struct MemBlockHdr *pHdr; @@ -17145,11 +17151,11 @@ ** allocation p. Also return true if p==NULL. ** ** This routine is designed for use within an assert() statement, to ** verify the type of an allocation. For example: ** -** assert( sqlite3MemdebugNoType(p, MEMTYPE_DB) ); +** assert( sqlite3MemdebugNoType(p, MEMTYPE_LOOKASIDE) ); */ SQLITE_PRIVATE int sqlite3MemdebugNoType(void *p, u8 eType){ int rc = 1; if( p && sqlite3GlobalConfig.m.xMalloc==sqlite3MemMalloc ){ struct MemBlockHdr *pHdr; @@ -20220,11 +20226,11 @@ mallocWithAlarm((int)n, &p); sqlite3_mutex_leave(mem0.mutex); }else{ p = sqlite3GlobalConfig.m.xMalloc((int)n); } - assert( EIGHT_BYTE_ALIGNMENT(p) ); /* IMP: R-04675-44850 */ + assert( EIGHT_BYTE_ALIGNMENT(p) ); /* IMP: R-11148-40995 */ return p; } /* ** This version of the memory allocation is for use by the application. @@ -20357,39 +20363,41 @@ ** Return the size of a memory allocation previously obtained from ** sqlite3Malloc() or sqlite3_malloc(). */ SQLITE_PRIVATE int sqlite3MallocSize(void *p){ assert( sqlite3MemdebugHasType(p, MEMTYPE_HEAP) ); - assert( sqlite3MemdebugNoType(p, MEMTYPE_DB) ); return sqlite3GlobalConfig.m.xSize(p); } SQLITE_PRIVATE int sqlite3DbMallocSize(sqlite3 *db, void *p){ if( db==0 ){ + assert( sqlite3MemdebugNoType(p, ~MEMTYPE_HEAP) ); + assert( sqlite3MemdebugHasType(p, MEMTYPE_HEAP) ); return sqlite3MallocSize(p); }else{ assert( sqlite3_mutex_held(db->mutex) ); if( isLookaside(db, p) ){ return db->lookaside.sz; }else{ - assert( sqlite3MemdebugHasType(p, MEMTYPE_DB) ); - assert( sqlite3MemdebugHasType(p, MEMTYPE_LOOKASIDE|MEMTYPE_HEAP) ); - assert( db!=0 || sqlite3MemdebugNoType(p, MEMTYPE_LOOKASIDE) ); + assert( sqlite3MemdebugHasType(p, (MEMTYPE_LOOKASIDE|MEMTYPE_HEAP)) ); + assert( sqlite3MemdebugNoType(p, ~(MEMTYPE_LOOKASIDE|MEMTYPE_HEAP)) ); return sqlite3GlobalConfig.m.xSize(p); } } } SQLITE_API sqlite3_uint64 sqlite3_msize(void *p){ + assert( sqlite3MemdebugNoType(p, ~MEMTYPE_HEAP) ); + assert( sqlite3MemdebugHasType(p, MEMTYPE_HEAP) ); return (sqlite3_uint64)sqlite3GlobalConfig.m.xSize(p); } /* ** Free memory previously obtained from sqlite3Malloc(). */ SQLITE_API void sqlite3_free(void *p){ if( p==0 ) return; /* IMP: R-49053-54554 */ - assert( sqlite3MemdebugNoType(p, MEMTYPE_DB) ); assert( sqlite3MemdebugHasType(p, MEMTYPE_HEAP) ); + assert( sqlite3MemdebugNoType(p, ~MEMTYPE_HEAP) ); if( sqlite3GlobalConfig.bMemstat ){ sqlite3_mutex_enter(mem0.mutex); sqlite3StatusAdd(SQLITE_STATUS_MEMORY_USED, -sqlite3MallocSize(p)); sqlite3StatusAdd(SQLITE_STATUS_MALLOC_COUNT, -1); sqlite3GlobalConfig.m.xFree(p); @@ -20429,12 +20437,12 @@ db->lookaside.pFree = pBuf; db->lookaside.nOut--; return; } } - assert( sqlite3MemdebugHasType(p, MEMTYPE_DB) ); - assert( sqlite3MemdebugHasType(p, MEMTYPE_LOOKASIDE|MEMTYPE_HEAP) ); + assert( sqlite3MemdebugHasType(p, (MEMTYPE_LOOKASIDE|MEMTYPE_HEAP)) ); + assert( sqlite3MemdebugNoType(p, ~(MEMTYPE_LOOKASIDE|MEMTYPE_HEAP)) ); assert( db!=0 || sqlite3MemdebugNoType(p, MEMTYPE_LOOKASIDE) ); sqlite3MemdebugSetType(p, MEMTYPE_HEAP); sqlite3_free(p); } @@ -20442,15 +20450,17 @@ ** Change the size of an existing memory allocation */ SQLITE_PRIVATE void *sqlite3Realloc(void *pOld, u64 nBytes){ int nOld, nNew, nDiff; void *pNew; + assert( sqlite3MemdebugHasType(pOld, MEMTYPE_HEAP) ); + assert( sqlite3MemdebugNoType(pOld, ~MEMTYPE_HEAP) ); if( pOld==0 ){ - return sqlite3Malloc(nBytes); /* IMP: R-28354-25769 */ + return sqlite3Malloc(nBytes); /* IMP: R-04300-56712 */ } if( nBytes==0 ){ - sqlite3_free(pOld); /* IMP: R-31593-10574 */ + sqlite3_free(pOld); /* IMP: R-26507-47431 */ return 0; } if( nBytes>=0x7fffff00 ){ /* The 0x7ffff00 limit term is explained in comments on sqlite3Malloc() */ return 0; @@ -20468,12 +20478,10 @@ nDiff = nNew - nOld; if( sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED) >= mem0.alarmThreshold-nDiff ){ sqlite3MallocAlarm(nDiff); } - assert( sqlite3MemdebugHasType(pOld, MEMTYPE_HEAP) ); - assert( sqlite3MemdebugNoType(pOld, ~MEMTYPE_HEAP) ); pNew = sqlite3GlobalConfig.m.xRealloc(pOld, nNew); if( pNew==0 && mem0.alarmCallback ){ sqlite3MallocAlarm((int)nBytes); pNew = sqlite3GlobalConfig.m.xRealloc(pOld, nNew); } @@ -20483,11 +20491,11 @@ } sqlite3_mutex_leave(mem0.mutex); }else{ pNew = sqlite3GlobalConfig.m.xRealloc(pOld, nNew); } - assert( EIGHT_BYTE_ALIGNMENT(pNew) ); /* IMP: R-04675-44850 */ + assert( EIGHT_BYTE_ALIGNMENT(pNew) ); /* IMP: R-11148-40995 */ return pNew; } /* ** The public interface to sqlite3Realloc. Make sure that the memory @@ -20495,11 +20503,11 @@ */ SQLITE_API void *sqlite3_realloc(void *pOld, int n){ #ifndef SQLITE_OMIT_AUTOINIT if( sqlite3_initialize() ) return 0; #endif - if( n<0 ) n = 0; + if( n<0 ) n = 0; /* IMP: R-26507-47431 */ return sqlite3Realloc(pOld, n); } SQLITE_API void *sqlite3_realloc64(void *pOld, sqlite3_uint64 n){ #ifndef SQLITE_OMIT_AUTOINIT if( sqlite3_initialize() ) return 0; @@ -20582,12 +20590,12 @@ #endif p = sqlite3Malloc(n); if( !p && db ){ db->mallocFailed = 1; } - sqlite3MemdebugSetType(p, MEMTYPE_DB | - ((db && db->lookaside.bEnabled) ? MEMTYPE_LOOKASIDE : MEMTYPE_HEAP)); + sqlite3MemdebugSetType(p, + (db && db->lookaside.bEnabled) ? MEMTYPE_LOOKASIDE : MEMTYPE_HEAP); return p; } /* ** Resize the block of memory pointed to by p to n bytes. If the @@ -20609,19 +20617,18 @@ if( pNew ){ memcpy(pNew, p, db->lookaside.sz); sqlite3DbFree(db, p); } }else{ - assert( sqlite3MemdebugHasType(p, MEMTYPE_DB) ); - assert( sqlite3MemdebugHasType(p, MEMTYPE_LOOKASIDE|MEMTYPE_HEAP) ); + assert( sqlite3MemdebugHasType(p, (MEMTYPE_LOOKASIDE|MEMTYPE_HEAP)) ); + assert( sqlite3MemdebugNoType(p, ~(MEMTYPE_LOOKASIDE|MEMTYPE_HEAP)) ); sqlite3MemdebugSetType(p, MEMTYPE_HEAP); pNew = sqlite3_realloc64(p, n); if( !pNew ){ - sqlite3MemdebugSetType(p, MEMTYPE_DB|MEMTYPE_HEAP); db->mallocFailed = 1; } - sqlite3MemdebugSetType(pNew, MEMTYPE_DB | + sqlite3MemdebugSetType(pNew, (db->lookaside.bEnabled ? MEMTYPE_LOOKASIDE : MEMTYPE_HEAP)); } } return pNew; } @@ -22084,18 +22091,18 @@ #endif /* SQLITE_OS_UNIX && defined(SQLITE_MUTEX_PTHREADS) */ /******************************** End Unix Pthreads *************************/ /********************************* Win32 Threads ****************************/ -#if SQLITE_OS_WIN && !SQLITE_OS_WINRT && SQLITE_THREADSAFE>0 +#if SQLITE_OS_WIN && !SQLITE_OS_WINCE && !SQLITE_OS_WINRT && SQLITE_THREADSAFE>0 #define SQLITE_THREADS_IMPLEMENTED 1 /* Prevent the single-thread code below */ #include /* A running thread */ struct SQLiteThread { - uintptr_t tid; /* The thread handle */ + void *tid; /* The thread handle */ unsigned id; /* The thread identifier */ void *(*xTask)(void*); /* The routine to run as a thread */ void *pIn; /* Argument to xTask */ void *pResult; /* Result of xTask */ }; @@ -22139,11 +22146,11 @@ if( sqlite3GlobalConfig.bCoreMutex==0 ){ memset(p, 0, sizeof(*p)); }else{ p->xTask = xTask; p->pIn = pIn; - p->tid = _beginthreadex(0, 0, sqlite3ThreadProc, p, 0, &p->id); + p->tid = (void*)_beginthreadex(0, 0, sqlite3ThreadProc, p, 0, &p->id); if( p->tid==0 ){ memset(p, 0, sizeof(*p)); } } if( p->xTask==0 ){ @@ -22177,11 +22184,11 @@ if( rc==WAIT_OBJECT_0 ) *ppOut = p->pResult; sqlite3_free(p); return (rc==WAIT_OBJECT_0) ? SQLITE_OK : SQLITE_ERROR; } -#endif /* SQLITE_OS_WIN && !SQLITE_OS_WINRT */ +#endif /* SQLITE_OS_WIN && !SQLITE_OS_WINCE && !SQLITE_OS_WINRT */ /******************************** End Win32 Threads *************************/ /********************************* Single-Threaded **************************/ #ifndef SQLITE_THREADS_IMPLEMENTED @@ -33480,11 +33487,15 @@ #endif #define osWaitForSingleObject ((DWORD(WINAPI*)(HANDLE, \ DWORD))aSyscall[63].pCurrent) +#if !SQLITE_OS_WINCE { "WaitForSingleObjectEx", (SYSCALL)WaitForSingleObjectEx, 0 }, +#else + { "WaitForSingleObjectEx", (SYSCALL)0, 0 }, +#endif #define osWaitForSingleObjectEx ((DWORD(WINAPI*)(HANDLE,DWORD, \ BOOL))aSyscall[64].pCurrent) #if SQLITE_OS_WINRT @@ -33823,11 +33834,12 @@ #else osSleep(milliseconds); #endif } -#if SQLITE_MAX_WORKER_THREADS>0 && !SQLITE_OS_WINRT && SQLITE_THREADSAFE>0 +#if SQLITE_MAX_WORKER_THREADS>0 && !SQLITE_OS_WINCE && !SQLITE_OS_WINRT && \ + SQLITE_THREADSAFE>0 SQLITE_PRIVATE DWORD sqlite3Win32Wait(HANDLE hObject){ DWORD rc; while( (rc = osWaitForSingleObjectEx(hObject, INFINITE, TRUE))==WAIT_IO_COMPLETION ){} return rc; @@ -37839,35 +37851,35 @@ UNUSED_PARAMETER(pVfs); #if defined(SQLITE_TEST) n = nBuf; memset(zBuf, 0, nBuf); #else - if( (int)sizeof(SYSTEMTIME)<=nBuf-n ){ + if( sizeof(SYSTEMTIME)<=nBuf-n ){ SYSTEMTIME x; osGetSystemTime(&x); memcpy(&zBuf[n], &x, sizeof(x)); n += sizeof(x); } - if( (int)sizeof(DWORD)<=nBuf-n ){ + if( sizeof(DWORD)<=nBuf-n ){ DWORD pid = osGetCurrentProcessId(); memcpy(&zBuf[n], &pid, sizeof(pid)); n += sizeof(pid); } #if SQLITE_OS_WINRT - if( (int)sizeof(ULONGLONG)<=nBuf-n ){ + if( sizeof(ULONGLONG)<=nBuf-n ){ ULONGLONG cnt = osGetTickCount64(); memcpy(&zBuf[n], &cnt, sizeof(cnt)); n += sizeof(cnt); } #else - if( (int)sizeof(DWORD)<=nBuf-n ){ + if( sizeof(DWORD)<=nBuf-n ){ DWORD cnt = osGetTickCount(); memcpy(&zBuf[n], &cnt, sizeof(cnt)); n += sizeof(cnt); } #endif - if( (int)sizeof(LARGE_INTEGER)<=nBuf-n ){ + if( sizeof(LARGE_INTEGER)<=nBuf-n ){ LARGE_INTEGER i; osQueryPerformanceCounter(&i); memcpy(&zBuf[n], &i, sizeof(i)); n += sizeof(i); } @@ -39848,11 +39860,11 @@ assert( pGroup->mxPinned == pGroup->nMaxPage + 10 - pGroup->nMinPage ); assert( pCache->n90pct == pCache->nMax*9/10 ); if( createFlag==1 && ( nPinned>=pGroup->mxPinned || nPinned>=pCache->n90pct - || pcache1UnderMemoryPressure(pCache) + || (pcache1UnderMemoryPressure(pCache) && pCache->nRecyclablenPage>=pCache->nHash ) pcache1ResizeHash(pCache); @@ -44469,17 +44481,19 @@ if( !pNew ) rc = SQLITE_NOMEM; } if( rc==SQLITE_OK ){ pager_reset(pPager); - sqlite3PageFree(pPager->pTmpSpace); - pPager->pTmpSpace = pNew; rc = sqlite3PcacheSetPageSize(pPager->pPCache, pageSize); } if( rc==SQLITE_OK ){ + sqlite3PageFree(pPager->pTmpSpace); + pPager->pTmpSpace = pNew; pPager->dbSize = (Pgno)((nByte+pageSize-1)/pageSize); pPager->pageSize = pageSize; + }else{ + sqlite3PageFree(pNew); } } *pPageSize = pPager->pageSize; if( rc==SQLITE_OK ){ @@ -51642,11 +51656,11 @@ int nRef; /* Number of references to this structure */ BtShared *pNext; /* Next on a list of sharable BtShared structs */ BtLock *pLock; /* List of locks held on this shared-btree struct */ Btree *pWriter; /* Btree with currently open write transaction */ #endif - u8 *pTmpSpace; /* BtShared.pageSize bytes of space for tmp use */ + u8 *pTmpSpace; /* Temp space sufficient to hold a single cell */ }; /* ** Allowed values for BtShared.btsFlags */ @@ -52940,11 +52954,11 @@ ** ** Use the separate sqlite3BtreeCursorRestore() routine to restore a cursor ** back to where it ought to be if this routine returns true. */ SQLITE_PRIVATE int sqlite3BtreeCursorHasMoved(BtCursor *pCur){ - return pCur && pCur->eState!=CURSOR_VALID; + return pCur->eState!=CURSOR_VALID; } /* ** This routine restores a cursor back to its original position after it ** has been moved by some outside activity (such as a btree rebalance or @@ -54272,11 +54286,12 @@ #endif } /* ** Make sure pBt->pTmpSpace points to an allocation of -** MX_CELL_SIZE(pBt) bytes. +** MX_CELL_SIZE(pBt) bytes with a 4-byte prefix for a left-child +** pointer. */ static void allocateTempSpace(BtShared *pBt){ if( !pBt->pTmpSpace ){ pBt->pTmpSpace = sqlite3PageMalloc( pBt->pageSize ); @@ -54287,21 +54302,32 @@ ** can mean that fillInCell() only initializes the first 2 or 3 ** bytes of pTmpSpace, but that the first 4 bytes are copied from ** it into a database page. This is not actually a problem, but it ** does cause a valgrind error when the 1 or 2 bytes of unitialized ** data is passed to system call write(). So to avoid this error, - ** zero the first 4 bytes of temp space here. */ - if( pBt->pTmpSpace ) memset(pBt->pTmpSpace, 0, 4); + ** zero the first 4 bytes of temp space here. + ** + ** Also: Provide four bytes of initialized space before the + ** beginning of pTmpSpace as an area available to prepend the + ** left-child pointer to the beginning of a cell. + */ + if( pBt->pTmpSpace ){ + memset(pBt->pTmpSpace, 0, 8); + pBt->pTmpSpace += 4; + } } } /* ** Free the pBt->pTmpSpace allocation */ static void freeTempSpace(BtShared *pBt){ - sqlite3PageFree( pBt->pTmpSpace); - pBt->pTmpSpace = 0; + if( pBt->pTmpSpace ){ + pBt->pTmpSpace -= 4; + sqlite3PageFree(pBt->pTmpSpace); + pBt->pTmpSpace = 0; + } } /* ** Close an open database and invalidate all cursors. */ @@ -58009,15 +58035,10 @@ ** pTemp is not null. Regardless of pTemp, allocate a new entry ** in pPage->apOvfl[] and make it point to the cell content (either ** in pTemp or the original pCell) and also record its index. ** Allocating a new entry in pPage->aCell[] implies that ** pPage->nOverflow is incremented. -** -** If nSkip is non-zero, then do not copy the first nSkip bytes of the -** cell. The caller will overwrite them after this function returns. If -** nSkip is non-zero, then pCell may not point to an invalid memory location -** (but pCell+nSkip is always valid). */ static void insertCell( MemPage *pPage, /* Page into which we are copying */ int i, /* New cell becomes the i-th cell of the page */ u8 *pCell, /* Content of the new cell */ @@ -58030,11 +58051,10 @@ int j; /* Loop counter */ int end; /* First byte past the last cell pointer in data[] */ int ins; /* Index in data[] where new cell pointer is inserted */ int cellOffset; /* Address of first cell pointer in data[] */ u8 *data; /* The content of the whole page */ - int nSkip = (iChild ? 4 : 0); if( *pRC ) return; assert( i>=0 && i<=pPage->nCell+pPage->nOverflow ); assert( MX_CELL(pPage->pBt)<=10921 ); @@ -58048,11 +58068,11 @@ ** might be less than 8 (leaf-size + pointer) on the interior node. Hence ** the term after the || in the following assert(). */ assert( sz==cellSizePtr(pPage, pCell) || (sz==8 && iChild>0) ); if( pPage->nOverflow || sz+2>pPage->nFree ){ if( pTemp ){ - memcpy(pTemp+nSkip, pCell+nSkip, sz-nSkip); + memcpy(pTemp, pCell, sz); pCell = pTemp; } if( iChild ){ put4byte(pCell, iChild); } @@ -58077,11 +58097,11 @@ ** if it returns success */ assert( idx >= end+2 ); assert( idx+sz <= (int)pPage->pBt->usableSize ); pPage->nCell++; pPage->nFree -= (u16)(2 + sz); - memcpy(&data[idx+nSkip], pCell+nSkip, sz-nSkip); + memcpy(&data[idx], pCell, sz); if( iChild ){ put4byte(&data[idx], iChild); } memmove(&data[ins+2], &data[ins], end-ins); put2byte(&data[ins], idx); @@ -61623,11 +61643,14 @@ /* If MEM_Dyn is set then Mem.xDel!=0. ** Mem.xDel is might not be initialized if MEM_Dyn is clear. */ assert( (p->flags & MEM_Dyn)==0 || p->xDel!=0 ); - /* MEM_Dyn may only be set if Mem.szMalloc==0 */ + /* MEM_Dyn may only be set if Mem.szMalloc==0. In this way we + ** ensure that if Mem.szMalloc>0 then it is safe to do + ** Mem.z = Mem.zMalloc without having to check Mem.flags&MEM_Dyn. + ** That saves a few cycles in inner loops. */ assert( (p->flags & MEM_Dyn)==0 || p->szMalloc==0 ); /* Cannot be both MEM_Int and MEM_Real at the same time */ assert( (p->flags & (MEM_Int|MEM_Real))!=(MEM_Int|MEM_Real) ); @@ -61732,11 +61755,11 @@ }else{ pMem->szMalloc = sqlite3DbMallocSize(pMem->db, pMem->zMalloc); } } - if( pMem->z && bPreserve && pMem->z!=pMem->zMalloc ){ + if( bPreserve && pMem->z && pMem->z!=pMem->zMalloc ){ memcpy(pMem->zMalloc, pMem->z, pMem->n); } if( (pMem->flags&MEM_Dyn)!=0 ){ assert( pMem->xDel!=0 && pMem->xDel!=SQLITE_DYNAMIC ); pMem->xDel((void *)(pMem->z)); @@ -61759,11 +61782,12 @@ ** ** Return SQLITE_OK on success or an error code (probably SQLITE_NOMEM) ** if unable to complete the resizing. */ SQLITE_PRIVATE int sqlite3VdbeMemClearAndResize(Mem *pMem, int szNew){ - assert( szNew>=0 ); + assert( szNew>0 ); + assert( (pMem->flags & MEM_Dyn)==0 || pMem->szMalloc==0 ); if( pMem->szMallocflags & MEM_Dyn)==0 ); pMem->z = pMem->zMalloc; @@ -62483,11 +62507,14 @@ nAlloc += (enc==SQLITE_UTF8?1:2); } if( nByte>iLimit ){ return SQLITE_TOOBIG; } - if( sqlite3VdbeMemClearAndResize(pMem, nAlloc) ){ + testcase( nAlloc==0 ); + testcase( nAlloc==31 ); + testcase( nAlloc==32 ); + if( sqlite3VdbeMemClearAndResize(pMem, MAX(nAlloc,32)) ){ return SQLITE_NOMEM; } memcpy(pMem->z, z, nAlloc); }else if( xDel==SQLITE_DYNAMIC ){ sqlite3VdbeMemRelease(pMem); @@ -62586,11 +62613,11 @@ /* ** The pVal argument is known to be a value other than NULL. ** Convert it into a string with encoding enc and return a pointer ** to a zero-terminated version of that string. */ -SQLITE_NOINLINE const void *valueToText(sqlite3_value* pVal, u8 enc){ +static SQLITE_NOINLINE const void *valueToText(sqlite3_value* pVal, u8 enc){ assert( pVal!=0 ); assert( pVal->db==0 || sqlite3_mutex_held(pVal->db->mutex) ); assert( (enc&3)==(enc&~SQLITE_UTF16_ALIGNED) ); assert( (pVal->flags & MEM_RowSet)==0 ); assert( (pVal->flags & (MEM_Null))==0 ); @@ -64906,11 +64933,11 @@ ** the call above. */ }else if( pCx->pCursor ){ sqlite3BtreeCloseCursor(pCx->pCursor); } #ifndef SQLITE_OMIT_VIRTUALTABLE - if( pCx->pVtabCursor ){ + else if( pCx->pVtabCursor ){ sqlite3_vtab_cursor *pVtabCursor = pCx->pVtabCursor; const sqlite3_module *pModule = pVtabCursor->pVtab->pModule; p->inVtabMethod = 1; pModule->xClose(pVtabCursor); p->inVtabMethod = 0; @@ -64949,13 +64976,14 @@ static void closeAllCursors(Vdbe *p){ if( p->pFrame ){ VdbeFrame *pFrame; for(pFrame=p->pFrame; pFrame->pParent; pFrame=pFrame->pParent); sqlite3VdbeFrameRestore(pFrame); + p->pFrame = 0; + p->nFrame = 0; } - p->pFrame = 0; - p->nFrame = 0; + assert( p->nFrame==0 ); if( p->apCsr ){ int i; for(i=0; inCursor; i++){ VdbeCursor *pC = p->apCsr[i]; @@ -64973,11 +65001,11 @@ p->pDelFrame = pDel->pParent; sqlite3VdbeFrameDelete(pDel); } /* Delete any auxdata allocations made by the VM */ - sqlite3VdbeDeleteAuxData(p, -1, 0); + if( p->pAuxData ) sqlite3VdbeDeleteAuxData(p, -1, 0); assert( p->pAuxData==0 ); } /* ** Clean up the VM after a single run. @@ -65879,13 +65907,11 @@ #endif assert( p->deferredMoveto ); assert( p->isTable ); rc = sqlite3BtreeMovetoUnpacked(p->pCursor, 0, p->movetoTarget, 0, &res); if( rc ) return rc; - p->lastRowid = p->movetoTarget; if( res!=0 ) return SQLITE_CORRUPT_BKPT; - p->rowidIsValid = 1; #ifdef SQLITE_TEST sqlite3_search_count++; #endif p->deferredMoveto = 0; p->cacheStatus = CACHE_STALE; @@ -65906,10 +65932,21 @@ rc = sqlite3BtreeCursorRestore(p->pCursor, &isDifferentRow); p->cacheStatus = CACHE_STALE; if( isDifferentRow ) p->nullRow = 1; return rc; } + +/* +** Check to ensure that the cursor is valid. Restore the cursor +** if need be. Return any I/O error from the restore operation. +*/ +SQLITE_PRIVATE int sqlite3VdbeCursorRestore(VdbeCursor *p){ + if( sqlite3BtreeCursorHasMoved(p->pCursor) ){ + return handleMovedCursor(p); + } + return SQLITE_OK; +} /* ** Make sure the cursor p is ready to read or write the row to which it ** was last positioned. Return an error code if an OOM fault or I/O error ** prevents us from positioning the cursor to its correct position. @@ -65924,11 +65961,11 @@ */ SQLITE_PRIVATE int sqlite3VdbeCursorMoveto(VdbeCursor *p){ if( p->deferredMoveto ){ return handleDeferredMoveto(p); } - if( sqlite3BtreeCursorHasMoved(p->pCursor) ){ + if( p->pCursor && sqlite3BtreeCursorHasMoved(p->pCursor) ){ return handleMovedCursor(p); } return SQLITE_OK; } @@ -67549,10 +67586,11 @@ void (*xDel)(void *), unsigned char enc ){ assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) ); assert( xDel!=SQLITE_DYNAMIC ); + if( enc==SQLITE_UTF16 ) enc = SQLITE_UTF16NATIVE; if( n>0x7fffffff ){ (void)invokeValueDestructor(z, xDel, pCtx); }else{ setResultStrOrError(pCtx, z, (int)n, enc, xDel); } @@ -69087,10 +69125,11 @@ if( SQLITE_OK==sqlite3VdbeMemClearAndResize(pMem, nByte) ){ p->apCsr[iCur] = pCx = (VdbeCursor*)pMem->z; memset(pCx, 0, sizeof(VdbeCursor)); pCx->iDb = iDb; pCx->nField = nField; + pCx->aOffset = &pCx->aType[nField]; if( isBtreeCursor ){ pCx->pCursor = (BtCursor*) &pMem->z[ROUND8(sizeof(VdbeCursor))+2*sizeof(u32)*nField]; sqlite3BtreeCursorZero(pCx->pCursor); } @@ -70520,11 +70559,11 @@ ctx.pFunc = pOp->p4.pFunc; ctx.iOp = pc; ctx.pVdbe = p; MemSetTypeFlag(ctx.pOut, MEM_Null); ctx.fErrorOrAux = 0; - assert( db->lastRowid==lastRowid ); + db->lastRowid = lastRowid; (*ctx.pFunc->xFunc)(&ctx, n, apVal); /* IMP: R-24505-23230 */ lastRowid = db->lastRowid; /* Remember rowid changes made by xFunc */ /* If the function returned an error, throw an exception */ if( ctx.fErrorOrAux ){ @@ -71238,11 +71277,11 @@ memAboutToChange(p, pDest); assert( pOp->p1>=0 && pOp->p1nCursor ); pC = p->apCsr[pOp->p1]; assert( pC!=0 ); assert( p2nField ); - aOffset = pC->aType + pC->nField; + aOffset = pC->aOffset; #ifndef SQLITE_OMIT_VIRTUALTABLE assert( pC->pVtabCursor==0 ); /* OP_Column never called on virtual table */ #endif pCrsr = pC->pCursor; assert( pCrsr!=0 || pC->pseudoTableReg>0 ); /* pCrsr NULL on PseudoTables */ @@ -71249,11 +71288,11 @@ assert( pCrsr!=0 || pC->nullRow ); /* pC->nullRow on PseudoTables */ /* If the cursor cache is stale, bring it up-to-date */ rc = sqlite3VdbeCursorMoveto(pC); if( rc ) goto abort_due_to_error; - if( pC->cacheStatus!=p->cacheCtr || (pOp->p5&OPFLAG_CLEARCACHE)!=0 ){ + if( pC->cacheStatus!=p->cacheCtr ){ if( pC->nullRow ){ if( pCrsr==0 ){ assert( pC->pseudoTableReg>0 ); pReg = &aMem[pC->pseudoTableReg]; assert( pReg->flags & MEM_Blob ); @@ -71294,18 +71333,10 @@ } pC->cacheStatus = p->cacheCtr; pC->iHdrOffset = getVarint32(pC->aRow, offset); pC->nHdrParsed = 0; aOffset[0] = offset; - if( availaRow does not have to hold the entire row, but it does at least - ** need to cover the header of the record. If pC->aRow does not contain - ** the complete header, then set it to zero, forcing the header to be - ** dynamically allocated. */ - pC->aRow = 0; - pC->szRow = 0; - } /* Make sure a corrupt database has not given us an oversize header. ** Do this now to avoid an oversize memory allocation. ** ** Type entries can be between 1 and 5 bytes each. But 4 and 5 byte @@ -71316,19 +71347,36 @@ */ if( offset > 98307 || offset > pC->payloadSize ){ rc = SQLITE_CORRUPT_BKPT; goto op_column_error; } + + if( availaRow does not have to hold the entire row, but it does at least + ** need to cover the header of the record. If pC->aRow does not contain + ** the complete header, then set it to zero, forcing the header to be + ** dynamically allocated. */ + pC->aRow = 0; + pC->szRow = 0; + } + + /* The following goto is an optimization. It can be omitted and + ** everything will still work. But OP_Column is measurably faster + ** by skipping the subsequent conditional, which is always true. + */ + assert( pC->nHdrParsed<=p2 ); /* Conditional skipped */ + goto op_column_read_header; } /* Make sure at least the first p2+1 entries of the header have been ** parsed and valid information is in aOffset[] and pC->aType[]. */ if( pC->nHdrParsed<=p2 ){ /* If there is more header available for parsing in the record, try ** to extract additional fields up through the p2+1-th field */ + op_column_read_header: if( pC->iHdrOffsetaRow==0 ){ memset(&sMem, 0, sizeof(sMem)); rc = sqlite3VdbeMemFromBtree(pCrsr, 0, aOffset[0], @@ -71369,19 +71417,20 @@ if( pC->aRow==0 ){ sqlite3VdbeMemRelease(&sMem); sMem.flags = MEM_Null; } - /* If we have read more header data than was contained in the header, - ** or if the end of the last field appears to be past the end of the - ** record, or if the end of the last field appears to be before the end - ** of the record (when all fields present), then we must be dealing - ** with a corrupt database. + /* The record is corrupt if any of the following are true: + ** (1) the bytes of the header extend past the declared header size + ** (zHdr>zEndHdr) + ** (2) the entire header was used but not all data was used + ** (zHdr==zEndHdr && offset!=pC->payloadSize) + ** (3) the end of the data extends beyond the end of the record. + ** (offset > pC->payloadSize) */ - if( (zHdr > zEndHdr) + if( (zHdr>=zEndHdr && (zHdr>zEndHdr || offset!=pC->payloadSize)) || (offset > pC->payloadSize) - || (zHdr==zEndHdr && offset!=pC->payloadSize) ){ rc = SQLITE_CORRUPT_BKPT; goto op_column_error; } } @@ -71568,11 +71617,11 @@ ** out how much space is required for the new record. */ pRec = pLast; do{ assert( memIsValid(pRec) ); - serial_type = sqlite3VdbeSerialType(pRec, file_format); + pRec->uTemp = serial_type = sqlite3VdbeSerialType(pRec, file_format); len = sqlite3VdbeSerialTypeLen(serial_type); if( pRec->flags & MEM_Zero ){ if( nData ){ sqlite3VdbeMemExpandBlob(pRec); }else{ @@ -71617,11 +71666,11 @@ i = putVarint32(zNewRecord, nHdr); j = nHdr; assert( pData0<=pLast ); pRec = pData0; do{ - serial_type = sqlite3VdbeSerialType(pRec, file_format); + serial_type = pRec->uTemp; i += putVarint32(&zNewRecord[i], serial_type); /* serial type */ j += sqlite3VdbeSerialPut(&zNewRecord[j], pRec, serial_type); /* content */ }while( (++pRec)<=pLast ); assert( i==nHdr ); assert( j==nByte ); @@ -72516,11 +72565,10 @@ pIn3 = &aMem[pOp->p3]; if( (pIn3->flags & (MEM_Int|MEM_Real|MEM_Str))==MEM_Str ){ applyNumericAffinity(pIn3, 0); } iKey = sqlite3VdbeIntValue(pIn3); - pC->rowidIsValid = 0; /* If the P3 value could not be converted into an integer without ** loss of information, then special processing is required... */ if( (pIn3->flags & MEM_Int)==0 ){ if( (pIn3->flags & MEM_Real)==0 ){ @@ -72552,17 +72600,14 @@ assert( (OP_SeekLT & 0x0001)==(OP_SeekGE & 0x0001) ); if( (oc & 0x0001)==(OP_SeekLT & 0x0001) ) oc++; } } rc = sqlite3BtreeMovetoUnpacked(pC->pCursor, 0, (u64)iKey, 0, &res); + pC->movetoTarget = iKey; /* Used by OP_Delete */ if( rc!=SQLITE_OK ){ goto abort_due_to_error; } - if( res==0 ){ - pC->rowidIsValid = 1; - pC->lastRowid = iKey; - } }else{ nField = pOp->p4.i; assert( pOp->p4type==P4_INT32 ); assert( nField>0 ); r.pKeyInfo = pC->pKeyInfo; @@ -72588,11 +72633,10 @@ ExpandBlob(r.aMem); rc = sqlite3BtreeMovetoUnpacked(pC->pCursor, &r, 0, 0, &res); if( rc!=SQLITE_OK ){ goto abort_due_to_error; } - pC->rowidIsValid = 0; } pC->deferredMoveto = 0; pC->cacheStatus = CACHE_STALE; #ifdef SQLITE_TEST sqlite3_search_count++; @@ -72600,21 +72644,19 @@ if( oc>=OP_SeekGE ){ assert( oc==OP_SeekGE || oc==OP_SeekGT ); if( res<0 || (res==0 && oc==OP_SeekGT) ){ res = 0; rc = sqlite3BtreeNext(pC->pCursor, &res); if( rc!=SQLITE_OK ) goto abort_due_to_error; - pC->rowidIsValid = 0; }else{ res = 0; } }else{ assert( oc==OP_SeekLT || oc==OP_SeekLE ); if( res>0 || (res==0 && oc==OP_SeekLT) ){ res = 0; rc = sqlite3BtreePrevious(pC->pCursor, &res); if( rc!=SQLITE_OK ) goto abort_due_to_error; - pC->rowidIsValid = 0; }else{ /* res might be negative because the table is empty. Check to ** see if this is the case. */ res = sqlite3BtreeEof(pC->pCursor); @@ -72647,11 +72689,10 @@ assert( pC->pCursor!=0 ); assert( pC->isTable ); pC->nullRow = 0; pIn2 = &aMem[pOp->p2]; pC->movetoTarget = sqlite3VdbeIntValue(pIn2); - pC->rowidIsValid = 0; pC->deferredMoveto = 1; break; } @@ -72833,19 +72874,17 @@ pCrsr = pC->pCursor; assert( pCrsr!=0 ); res = 0; iKey = pIn3->u.i; rc = sqlite3BtreeMovetoUnpacked(pCrsr, 0, iKey, 0, &res); - pC->lastRowid = pIn3->u.i; - pC->rowidIsValid = res==0 ?1:0; + pC->movetoTarget = iKey; /* Used by OP_Delete */ pC->nullRow = 0; pC->cacheStatus = CACHE_STALE; pC->deferredMoveto = 0; VdbeBranchTaken(res!=0,2); if( res!=0 ){ pc = pOp->p2 - 1; - assert( pC->rowidIsValid==0 ); } pC->seekResult = res; break; } @@ -72989,11 +73028,10 @@ rc = SQLITE_FULL; /* IMP: R-38219-53002 */ goto abort_due_to_error; } assert( v>0 ); /* EV: R-40812-03570 */ } - pC->rowidIsValid = 0; pC->deferredMoveto = 0; pC->cacheStatus = CACHE_STALE; } pOut->u.i = v; break; @@ -73094,11 +73132,10 @@ } rc = sqlite3BtreeInsert(pC->pCursor, 0, iKey, pData->z, pData->n, nZero, (pOp->p5 & OPFLAG_APPEND)!=0, seekResult ); - pC->rowidIsValid = 0; pC->deferredMoveto = 0; pC->cacheStatus = CACHE_STALE; /* Invoke the update-hook if required. */ if( rc==SQLITE_OK && db->xUpdateCallback && pOp->p4.z ){ @@ -73131,37 +73168,36 @@ ** pointing to. The update hook will be invoked, if it exists. ** If P4 is not NULL then the P1 cursor must have been positioned ** using OP_NotFound prior to invoking this opcode. */ case OP_Delete: { - i64 iKey; VdbeCursor *pC; assert( pOp->p1>=0 && pOp->p1nCursor ); pC = p->apCsr[pOp->p1]; assert( pC!=0 ); assert( pC->pCursor!=0 ); /* Only valid for real tables, no pseudotables */ - iKey = pC->lastRowid; /* Only used for the update hook */ - - /* The OP_Delete opcode always follows an OP_NotExists or OP_Last or - ** OP_Column on the same table without any intervening operations that - ** might move or invalidate the cursor. Hence cursor pC is always pointing - ** to the row to be deleted and the sqlite3VdbeCursorMoveto() operation - ** below is always a no-op and cannot fail. We will run it anyhow, though, - ** to guard against future changes to the code generator. - **/ assert( pC->deferredMoveto==0 ); - rc = sqlite3VdbeCursorMoveto(pC); - if( NEVER(rc!=SQLITE_OK) ) goto abort_due_to_error; +#ifdef SQLITE_DEBUG + /* The seek operation that positioned the cursor prior to OP_Delete will + ** have also set the pC->movetoTarget field to the rowid of the row that + ** is being deleted */ + if( pOp->p4.z && pC->isTable ){ + i64 iKey = 0; + sqlite3BtreeKeySize(pC->pCursor, &iKey); + assert( pC->movetoTarget==iKey ); + } +#endif + rc = sqlite3BtreeDelete(pC->pCursor); pC->cacheStatus = CACHE_STALE; /* Invoke the update-hook if required. */ if( rc==SQLITE_OK && db->xUpdateCallback && pOp->p4.z && pC->isTable ){ db->xUpdateCallback(db->pUpdateArg, SQLITE_DELETE, - db->aDb[pC->iDb].zName, pOp->p4.z, iKey); + db->aDb[pC->iDb].zName, pOp->p4.z, pC->movetoTarget); assert( pC->iDb>=0 ); } if( pOp->p2 & OPFLAG_NCHANGE ) p->nChange++; break; } @@ -73210,23 +73246,32 @@ pc = pOp->p2-1; } break; }; -/* Opcode: SorterData P1 P2 * * * +/* Opcode: SorterData P1 P2 P3 * * ** Synopsis: r[P2]=data ** ** Write into register P2 the current sorter data for sorter cursor P1. +** Then clear the column header cache on cursor P3. +** +** This opcode is normally use to move a record out of the sorter and into +** a register that is the source for a pseudo-table cursor created using +** OpenPseudo. That pseudo-table cursor is the one that is identified by +** parameter P3. Clearing the P3 column cache as part of this opcode saves +** us from having to issue a separate NullRow instruction to clear that cache. */ case OP_SorterData: { VdbeCursor *pC; pOut = &aMem[pOp->p2]; pC = p->apCsr[pOp->p1]; assert( isSorter(pC) ); rc = sqlite3VdbeSorterRowkey(pC, pOut); assert( rc!=SQLITE_OK || (pOut->flags & MEM_Blob) ); + assert( pOp->p1>=0 && pOp->p1nCursor ); + p->apCsr[pOp->p3]->cacheStatus = CACHE_STALE; break; } /* Opcode: RowData P1 P2 * * * ** Synopsis: r[P2]=data @@ -73269,20 +73314,24 @@ assert( pC!=0 ); assert( pC->nullRow==0 ); assert( pC->pseudoTableReg==0 ); assert( pC->pCursor!=0 ); pCrsr = pC->pCursor; - assert( sqlite3BtreeCursorIsValid(pCrsr) ); /* The OP_RowKey and OP_RowData opcodes always follow OP_NotExists or ** OP_Rewind/Op_Next with no intervening instructions that might invalidate - ** the cursor. Hence the following sqlite3VdbeCursorMoveto() call is always - ** a no-op and can never fail. But we leave it in place as a safety. + ** the cursor. If this where not the case, on of the following assert()s + ** would fail. Should this ever change (because of changes in the code + ** generator) then the fix would be to insert a call to + ** sqlite3VdbeCursorMoveto(). */ assert( pC->deferredMoveto==0 ); + assert( sqlite3BtreeCursorIsValid(pCrsr) ); +#if 0 /* Not required due to the previous to assert() statements */ rc = sqlite3VdbeCursorMoveto(pC); - if( NEVER(rc!=SQLITE_OK) ) goto abort_due_to_error; + if( rc!=SQLITE_OK ) goto abort_due_to_error; +#endif if( pC->isTable==0 ){ assert( !pC->isTable ); VVA_ONLY(rc =) sqlite3BtreeKeySize(pCrsr, &n64); assert( rc==SQLITE_OK ); /* True because of CursorMoveto() call above */ @@ -73295,11 +73344,12 @@ assert( rc==SQLITE_OK ); /* DataSize() cannot fail */ if( n>(u32)db->aLimit[SQLITE_LIMIT_LENGTH] ){ goto too_big; } } - if( sqlite3VdbeMemClearAndResize(pOut, n) ){ + testcase( n==0 ); + if( sqlite3VdbeMemClearAndResize(pOut, MAX(n,32)) ){ goto no_mem; } pOut->n = n; MemSetTypeFlag(pOut, MEM_Blob); if( pC->isTable==0 ){ @@ -73346,18 +73396,14 @@ rc = pModule->xRowid(pC->pVtabCursor, &v); sqlite3VtabImportErrmsg(p, pVtab); #endif /* SQLITE_OMIT_VIRTUALTABLE */ }else{ assert( pC->pCursor!=0 ); - rc = sqlite3VdbeCursorMoveto(pC); + rc = sqlite3VdbeCursorRestore(pC); if( rc ) goto abort_due_to_error; - if( pC->rowidIsValid ){ - v = pC->lastRowid; - }else{ - rc = sqlite3BtreeKeySize(pC->pCursor, &v); - assert( rc==SQLITE_OK ); /* Always so because of CursorMoveto() above */ - } + rc = sqlite3BtreeKeySize(pC->pCursor, &v); + assert( rc==SQLITE_OK ); /* Always so because of CursorRestore() above */ } pOut->u.i = v; break; } @@ -73372,11 +73418,10 @@ assert( pOp->p1>=0 && pOp->p1nCursor ); pC = p->apCsr[pOp->p1]; assert( pC!=0 ); pC->nullRow = 1; - pC->rowidIsValid = 0; pC->cacheStatus = CACHE_STALE; if( pC->pCursor ){ sqlite3BtreeClearCursor(pC->pCursor); } break; @@ -73406,11 +73451,10 @@ res = 0; assert( pCrsr!=0 ); rc = sqlite3BtreeLast(pCrsr, &res); pC->nullRow = (u8)res; pC->deferredMoveto = 0; - pC->rowidIsValid = 0; pC->cacheStatus = CACHE_STALE; #ifdef SQLITE_DEBUG pC->seekOp = OP_Last; #endif if( pOp->p2>0 ){ @@ -73473,11 +73517,10 @@ pCrsr = pC->pCursor; assert( pCrsr ); rc = sqlite3BtreeFirst(pCrsr, &res); pC->deferredMoveto = 0; pC->cacheStatus = CACHE_STALE; - pC->rowidIsValid = 0; } pC->nullRow = (u8)res; assert( pOp->p2>0 && pOp->p2nOp ); VdbeBranchTaken(res!=0,2); if( res ){ @@ -73599,11 +73642,10 @@ sqlite3_search_count++; #endif }else{ pC->nullRow = 1; } - pC->rowidIsValid = 0; goto check_for_interrupt; } /* Opcode: IdxInsert P1 P2 P3 * P5 ** Synopsis: key=r[P2] @@ -73715,14 +73757,20 @@ pC = p->apCsr[pOp->p1]; assert( pC!=0 ); pCrsr = pC->pCursor; assert( pCrsr!=0 ); pOut->flags = MEM_Null; - rc = sqlite3VdbeCursorMoveto(pC); - if( NEVER(rc) ) goto abort_due_to_error; + assert( pC->isTable==0 ); assert( pC->deferredMoveto==0 ); - assert( pC->isTable==0 ); + + /* sqlite3VbeCursorRestore() can only fail if the record has been deleted + ** out from under the cursor. That will never happend for an IdxRowid + ** opcode, hence the NEVER() arround the check of the return value. + */ + rc = sqlite3VdbeCursorRestore(pC); + if( NEVER(rc!=SQLITE_OK) ) goto abort_due_to_error; + if( !pC->nullRow ){ rowid = 0; /* Not needed. Only used to silence a warning. */ rc = sqlite3VdbeIdxRowid(db, pCrsr, &rowid); if( rc!=SQLITE_OK ){ goto abort_due_to_error; @@ -78179,11 +78227,11 @@ if( rc==SQLITE_OK ){ #if SQLITE_MAX_WORKER_THREADS assert( pSorter->bUseThreads==0 || pSorter->nTask>1 ); if( pSorter->bUseThreads ){ int iTask; - PmaReader *pReadr; + PmaReader *pReadr = 0; SortSubtask *pLast = &pSorter->aTask[pSorter->nTask-1]; rc = vdbeSortAllocUnpacked(pLast); if( rc==SQLITE_OK ){ pReadr = (PmaReader*)sqlite3DbMallocZero(db, sizeof(PmaReader)); pSorter->pReader = pReadr; @@ -87160,29 +87208,27 @@ tRowcnt v; #ifdef SQLITE_ENABLE_STAT3_OR_STAT4 if( z==0 ) z = ""; #else - if( NEVER(z==0) ) z = ""; + assert( z!=0 ); #endif for(i=0; *z && i='0' && c<='9' ){ v = v*10 + c - '0'; z++; } #ifdef SQLITE_ENABLE_STAT3_OR_STAT4 - if( aOut ){ - aOut[i] = v; - }else + if( aOut ) aOut[i] = v; + if( aLog ) aLog[i] = sqlite3LogEst(v); #else assert( aOut==0 ); UNUSED_PARAMETER(aOut); + assert( aLog!=0 ); + aLog[i] = sqlite3LogEst(v); #endif - { - aLog[i] = sqlite3LogEst(v); - } if( *z==' ' ) z++; } #ifndef SQLITE_ENABLE_STAT3_OR_STAT4 assert( pIndex!=0 ); #else @@ -87239,12 +87285,21 @@ pIndex = sqlite3FindIndex(pInfo->db, argv[1], pInfo->zDatabase); } z = argv[2]; if( pIndex ){ + int nCol = pIndex->nKeyCol+1; +#ifdef SQLITE_ENABLE_STAT3_OR_STAT4 + tRowcnt * const aiRowEst = pIndex->aiRowEst = (tRowcnt*)sqlite3MallocZero( + sizeof(tRowcnt) * nCol + ); + if( aiRowEst==0 ) pInfo->db->mallocFailed = 1; +#else + tRowcnt * const aiRowEst = 0; +#endif pIndex->bUnordered = 0; - decodeIntArray((char*)z, pIndex->nKeyCol+1, 0, pIndex->aiRowLogEst, pIndex); + decodeIntArray((char*)z, nCol, aiRowEst, pIndex->aiRowLogEst, pIndex); if( pIndex->pPartIdxWhere==0 ) pTable->nRowLogEst = pIndex->aiRowLogEst[0]; }else{ Index fakeIdx; fakeIdx.szIdxRow = pTable->szTabRow; #ifdef SQLITE_ENABLE_COSTMULT @@ -87299,29 +87354,42 @@ ** unique. */ nCol = pIdx->nSampleCol-1; pIdx->aAvgEq[nCol] = 1; } for(iCol=0; iColnSample; int i; /* Used to iterate through samples */ tRowcnt sumEq = 0; /* Sum of the nEq values */ - tRowcnt nSum = 0; /* Number of terms contributing to sumEq */ tRowcnt avgEq = 0; - tRowcnt nDLt = pFinal->anDLt[iCol]; + tRowcnt nRow; /* Number of rows in index */ + i64 nSum100 = 0; /* Number of terms contributing to sumEq */ + i64 nDist100; /* Number of distinct values in index */ + + if( pIdx->aiRowEst==0 || pIdx->aiRowEst[iCol+1]==0 ){ + nRow = pFinal->anLt[iCol]; + nDist100 = (i64)100 * pFinal->anDLt[iCol]; + nSample--; + }else{ + nRow = pIdx->aiRowEst[0]; + nDist100 = ((i64)100 * pIdx->aiRowEst[0]) / pIdx->aiRowEst[iCol+1]; + } /* Set nSum to the number of distinct (iCol+1) field prefixes that - ** occur in the stat4 table for this index before pFinal. Set - ** sumEq to the sum of the nEq values for column iCol for the same - ** set (adding the value only once where there exist duplicate - ** prefixes). */ - for(i=0; i<(pIdx->nSample-1); i++){ - if( aSample[i].anDLt[iCol]!=aSample[i+1].anDLt[iCol] ){ + ** occur in the stat4 table for this index. Set sumEq to the sum of + ** the nEq values for column iCol for the same set (adding the value + ** only once where there exist duplicate prefixes). */ + for(i=0; inSample-1) + || aSample[i].anDLt[iCol]!=aSample[i+1].anDLt[iCol] + ){ sumEq += aSample[i].anEq[iCol]; - nSum++; + nSum100 += 100; } } - if( nDLt>nSum ){ - avgEq = (pFinal->anLt[iCol] - sumEq)/(nDLt - nSum); + + if( nDist100>nSum100 ){ + avgEq = ((i64)100 * (nRow - sumEq))/(nDist100 - nSum100); } if( avgEq==0 ) avgEq = 1; pIdx->aAvgEq[iCol] = avgEq; } } @@ -87568,10 +87636,15 @@ if( rc==SQLITE_OK ){ int lookasideEnabled = db->lookaside.bEnabled; db->lookaside.bEnabled = 0; rc = loadStat4(db, sInfo.zDatabase); db->lookaside.bEnabled = lookasideEnabled; + } + for(i=sqliteHashFirst(&db->aDb[iDb].pSchema->idxHash);i;i=sqliteHashNext(i)){ + Index *pIdx = sqliteHashData(i); + sqlite3_free(pIdx->aiRowEst); + pIdx->aiRowEst = 0; } #endif if( rc==SQLITE_NOMEM ){ db->mallocFailed = 1; @@ -88862,10 +88935,13 @@ #endif if( db==0 || db->pnBytesFreed==0 ) sqlite3KeyInfoUnref(p->pKeyInfo); sqlite3ExprDelete(db, p->pPartIdxWhere); sqlite3DbFree(db, p->zColAff); if( p->isResized ) sqlite3DbFree(db, p->azColl); +#ifdef SQLITE_ENABLE_STAT3_OR_STAT4 + sqlite3_free(p->aiRowEst); +#endif sqlite3DbFree(db, p); } /* ** For the index called zIdxName which is found in the database iDb, @@ -91171,11 +91247,11 @@ pIndex->nKeyCol); VdbeCoverage(v); sqlite3UniqueConstraint(pParse, OE_Abort, pIndex); }else{ addr2 = sqlite3VdbeCurrentAddr(v); } - sqlite3VdbeAddOp2(v, OP_SorterData, iSorter, regRecord); + sqlite3VdbeAddOp3(v, OP_SorterData, iSorter, regRecord, iIdx); sqlite3VdbeAddOp3(v, OP_IdxInsert, iIdx, regRecord, 1); sqlite3VdbeChangeP5(v, OPFLAG_USESEEKRESULT); sqlite3ReleaseTempReg(pParse, regRecord); sqlite3VdbeAddOp2(v, OP_SorterNext, iSorter, addr2); VdbeCoverage(v); sqlite3VdbeJumpHere(v, addr1); @@ -105114,11 +105190,10 @@ int regRow; int regRowid; int nKey; int iSortTab; /* Sorter cursor to read from */ int nSortData; /* Trailing values to read from sorter */ - u8 p5; /* p5 parameter for 1st OP_Column */ int i; int bSeq; /* True if sorter record includes seq. no. */ #ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS struct ExprList_item *aOutEx = p->pEList->a; #endif @@ -105148,23 +105223,20 @@ sqlite3VdbeAddOp3(v, OP_OpenPseudo, iSortTab, regSortOut, nKey+1+nSortData); if( addrOnce ) sqlite3VdbeJumpHere(v, addrOnce); addr = 1 + sqlite3VdbeAddOp2(v, OP_SorterSort, iTab, addrBreak); VdbeCoverage(v); codeOffset(v, p->iOffset, addrContinue); - sqlite3VdbeAddOp2(v, OP_SorterData, iTab, regSortOut); - p5 = OPFLAG_CLEARCACHE; + sqlite3VdbeAddOp3(v, OP_SorterData, iTab, regSortOut, iSortTab); bSeq = 0; }else{ addr = 1 + sqlite3VdbeAddOp2(v, OP_Sort, iTab, addrBreak); VdbeCoverage(v); codeOffset(v, p->iOffset, addrContinue); iSortTab = iTab; - p5 = 0; bSeq = 1; } for(i=0; inExpr; j++){ if( groupBySort ){ sqlite3VdbeAddOp3(v, OP_Column, sortPTab, j, iBMem+j); - if( j==0 ) sqlite3VdbeChangeP5(v, OPFLAG_CLEARCACHE); }else{ sAggInfo.directMode = 1; sqlite3ExprCode(pParse, pGroupBy->a[j].pExpr, iBMem+j); } } @@ -112442,10 +112513,11 @@ } sqlite3DbFree(db, pVTable); }else if( ALWAYS(pVTable->pVtab) ){ /* Justification of ALWAYS(): A correct vtab constructor must allocate ** the sqlite3_vtab object if successful. */ + memset(pVTable->pVtab, 0, sizeof(pVTable->pVtab[0])); pVTable->pVtab->pModule = pMod->pModule; pVTable->nRef = 1; if( sCtx.pTab ){ const char *zFormat = "vtable constructor did not declare schema: %s"; *pzErr = sqlite3MPrintf(db, zFormat, pTab->zName); @@ -115692,21 +115764,28 @@ ** have been requested when testing key $P in whereEqualScanEst(). */ whereKeyStats(pParse, p, pRec, 0, a); iLower = a[0]; iUpper = a[0] + a[1]; } + + assert( pLower==0 || (pLower->eOperator & (WO_GT|WO_GE))!=0 ); + assert( pUpper==0 || (pUpper->eOperator & (WO_LT|WO_LE))!=0 ); + assert( p->aSortOrder!=0 ); + if( p->aSortOrder[nEq] ){ + /* The roles of pLower and pUpper are swapped for a DESC index */ + SWAP(WhereTerm*, pLower, pUpper); + } /* If possible, improve on the iLower estimate using ($P:$L). */ if( pLower ){ int bOk; /* True if value is extracted from pExpr */ Expr *pExpr = pLower->pExpr->pRight; - assert( (pLower->eOperator & (WO_GT|WO_GE))!=0 ); rc = sqlite3Stat4ProbeSetValue(pParse, p, &pRec, pExpr, aff, nEq, &bOk); if( rc==SQLITE_OK && bOk ){ tRowcnt iNew; whereKeyStats(pParse, p, pRec, 0, a); - iNew = a[0] + ((pLower->eOperator & WO_GT) ? a[1] : 0); + iNew = a[0] + ((pLower->eOperator & (WO_GT|WO_LE)) ? a[1] : 0); if( iNew>iLower ) iLower = iNew; nOut--; pLower = 0; } } @@ -115713,16 +115792,15 @@ /* If possible, improve on the iUpper estimate using ($P:$U). */ if( pUpper ){ int bOk; /* True if value is extracted from pExpr */ Expr *pExpr = pUpper->pExpr->pRight; - assert( (pUpper->eOperator & (WO_LT|WO_LE))!=0 ); rc = sqlite3Stat4ProbeSetValue(pParse, p, &pRec, pExpr, aff, nEq, &bOk); if( rc==SQLITE_OK && bOk ){ tRowcnt iNew; whereKeyStats(pParse, p, pRec, 1, a); - iNew = a[0] + ((pUpper->eOperator & WO_LE) ? a[1] : 0); + iNew = a[0] + ((pUpper->eOperator & (WO_GT|WO_LE)) ? a[1] : 0); if( iNew2; ** ** is run and there is an index on (a, b), then this function returns a ** string similar to: ** ** "a=? AND b>?" -** -** The returned pointer points to memory obtained from sqlite3DbMalloc(). -** It is the responsibility of the caller to free the buffer when it is -** no longer required. */ -static char *explainIndexRange(sqlite3 *db, WhereLoop *pLoop, Table *pTab){ +static void explainIndexRange(StrAccum *pStr, WhereLoop *pLoop, Table *pTab){ Index *pIndex = pLoop->u.btree.pIndex; u16 nEq = pLoop->u.btree.nEq; u16 nSkip = pLoop->u.btree.nSkip; int i, j; Column *aCol = pTab->aCol; i16 *aiColumn = pIndex->aiColumn; - StrAccum txt; - - if( nEq==0 && (pLoop->wsFlags & (WHERE_BTM_LIMIT|WHERE_TOP_LIMIT))==0 ){ - return 0; - } - sqlite3StrAccumInit(&txt, 0, 0, SQLITE_MAX_LENGTH); - txt.db = db; - sqlite3StrAccumAppend(&txt, " (", 2); + + if( nEq==0 && (pLoop->wsFlags&(WHERE_BTM_LIMIT|WHERE_TOP_LIMIT))==0 ) return; + sqlite3StrAccumAppend(pStr, " (", 2); for(i=0; i=nSkip ){ - explainAppendTerm(&txt, i, z, "="); + explainAppendTerm(pStr, i, z, "="); }else{ - if( i ) sqlite3StrAccumAppend(&txt, " AND ", 5); - sqlite3StrAccumAppend(&txt, "ANY(", 4); - sqlite3StrAccumAppendAll(&txt, z); - sqlite3StrAccumAppend(&txt, ")", 1); + if( i ) sqlite3StrAccumAppend(pStr, " AND ", 5); + sqlite3XPrintf(pStr, 0, "ANY(%s)", z); } } j = i; if( pLoop->wsFlags&WHERE_BTM_LIMIT ){ char *z = aiColumn[j] < 0 ? "rowid" : aCol[aiColumn[j]].zName; - explainAppendTerm(&txt, i++, z, ">"); + explainAppendTerm(pStr, i++, z, ">"); } if( pLoop->wsFlags&WHERE_TOP_LIMIT ){ char *z = aiColumn[j] < 0 ? "rowid" : aCol[aiColumn[j]].zName; - explainAppendTerm(&txt, i, z, "<"); + explainAppendTerm(pStr, i, z, "<"); } - sqlite3StrAccumAppend(&txt, ")", 1); - return sqlite3StrAccumFinish(&txt); + sqlite3StrAccumAppend(pStr, ")", 1); } /* ** This function is a no-op unless currently processing an EXPLAIN QUERY PLAN ** command. If the query being compiled is an EXPLAIN QUERY PLAN, a single @@ -116295,72 +116360,90 @@ #endif { struct SrcList_item *pItem = &pTabList->a[pLevel->iFrom]; Vdbe *v = pParse->pVdbe; /* VM being constructed */ sqlite3 *db = pParse->db; /* Database handle */ - char *zMsg; /* Text to add to EQP output */ int iId = pParse->iSelectId; /* Select id (left-most output column) */ int isSearch; /* True for a SEARCH. False for SCAN. */ WhereLoop *pLoop; /* The controlling WhereLoop object */ u32 flags; /* Flags that describe this loop */ + char *zMsg; /* Text to add to EQP output */ + StrAccum str; /* EQP output string */ + char zBuf[100]; /* Initial space for EQP output string */ pLoop = pLevel->pWLoop; flags = pLoop->wsFlags; if( (flags&WHERE_MULTI_OR) || (wctrlFlags&WHERE_ONETABLE_ONLY) ) return; isSearch = (flags&(WHERE_BTM_LIMIT|WHERE_TOP_LIMIT))!=0 || ((flags&WHERE_VIRTUALTABLE)==0 && (pLoop->u.btree.nEq>0)) || (wctrlFlags&(WHERE_ORDERBY_MIN|WHERE_ORDERBY_MAX)); - zMsg = sqlite3MPrintf(db, "%s", isSearch?"SEARCH":"SCAN"); + sqlite3StrAccumInit(&str, zBuf, sizeof(zBuf), SQLITE_MAX_LENGTH); + str.db = db; + sqlite3StrAccumAppendAll(&str, isSearch ? "SEARCH" : "SCAN"); if( pItem->pSelect ){ - zMsg = sqlite3MAppendf(db, zMsg, "%s SUBQUERY %d", zMsg,pItem->iSelectId); + sqlite3XPrintf(&str, 0, " SUBQUERY %d", pItem->iSelectId); }else{ - zMsg = sqlite3MAppendf(db, zMsg, "%s TABLE %s", zMsg, pItem->zName); + sqlite3XPrintf(&str, 0, " TABLE %s", pItem->zName); } if( pItem->zAlias ){ - zMsg = sqlite3MAppendf(db, zMsg, "%s AS %s", zMsg, pItem->zAlias); - } - if( (flags & (WHERE_IPK|WHERE_VIRTUALTABLE))==0 - && ALWAYS(pLoop->u.btree.pIndex!=0) - ){ - const char *zFmt; - Index *pIdx = pLoop->u.btree.pIndex; - char *zWhere = explainIndexRange(db, pLoop, pItem->pTab); + sqlite3XPrintf(&str, 0, " AS %s", pItem->zAlias); + } + if( (flags & (WHERE_IPK|WHERE_VIRTUALTABLE))==0 ){ + const char *zFmt = 0; + Index *pIdx; + + assert( pLoop->u.btree.pIndex!=0 ); + pIdx = pLoop->u.btree.pIndex; assert( !(flags&WHERE_AUTO_INDEX) || (flags&WHERE_IDX_ONLY) ); if( !HasRowid(pItem->pTab) && IsPrimaryKeyIndex(pIdx) ){ - zFmt = zWhere ? "%s USING PRIMARY KEY%.0s%s" : "%s%.0s%s"; + if( isSearch ){ + zFmt = "PRIMARY KEY"; + } }else if( flags & WHERE_AUTO_INDEX ){ - zFmt = "%s USING AUTOMATIC COVERING INDEX%.0s%s"; + zFmt = "AUTOMATIC COVERING INDEX"; }else if( flags & WHERE_IDX_ONLY ){ - zFmt = "%s USING COVERING INDEX %s%s"; + zFmt = "COVERING INDEX %s"; }else{ - zFmt = "%s USING INDEX %s%s"; + zFmt = "INDEX %s"; } - zMsg = sqlite3MAppendf(db, zMsg, zFmt, zMsg, pIdx->zName, zWhere); - sqlite3DbFree(db, zWhere); + if( zFmt ){ + sqlite3StrAccumAppend(&str, " USING ", 7); + sqlite3XPrintf(&str, 0, zFmt, pIdx->zName); + explainIndexRange(&str, pLoop, pItem->pTab); + } }else if( (flags & WHERE_IPK)!=0 && (flags & WHERE_CONSTRAINT)!=0 ){ - zMsg = sqlite3MAppendf(db, zMsg, "%s USING INTEGER PRIMARY KEY", zMsg); - + const char *zRange; if( flags&(WHERE_COLUMN_EQ|WHERE_COLUMN_IN) ){ - zMsg = sqlite3MAppendf(db, zMsg, "%s (rowid=?)", zMsg); + zRange = "(rowid=?)"; }else if( (flags&WHERE_BOTH_LIMIT)==WHERE_BOTH_LIMIT ){ - zMsg = sqlite3MAppendf(db, zMsg, "%s (rowid>? AND rowid? AND rowid?)", zMsg); - }else if( ALWAYS(flags&WHERE_TOP_LIMIT) ){ - zMsg = sqlite3MAppendf(db, zMsg, "%s (rowid?)"; + }else{ + assert( flags&WHERE_TOP_LIMIT); + zRange = "(rowidu.vtab.idxNum, pLoop->u.vtab.idxStr); } #endif - zMsg = sqlite3MAppendf(db, zMsg, "%s", zMsg); +#ifdef SQLITE_EXPLAIN_ESTIMATED_ROWS + if( pLoop->nOut>=10 ){ + sqlite3XPrintf(&str, 0, " (~%llu rows)", sqlite3LogEstToInt(pLoop->nOut)); + }else{ + sqlite3StrAccumAppend(&str, " (~1 row)", 9); + } +#endif + zMsg = sqlite3StrAccumFinish(&str); sqlite3VdbeAddOp4(v, OP_Explain, iId, iLevel, iFrom, zMsg, P4_DYNAMIC); } } #else # define explainOneScan(u,v,w,x,y,z) @@ -117625,11 +117708,11 @@ if( ppPrev==0 ){ /* There already exists a WhereLoop on the list that is better ** than pTemplate, so just ignore pTemplate */ #if WHERETRACE_ENABLED /* 0x8 */ if( sqlite3WhereTrace & 0x8 ){ - sqlite3DebugPrintf("ins-noop: "); + sqlite3DebugPrintf(" skip: "); whereLoopPrint(pTemplate, pBuilder->pWC); } #endif return SQLITE_OK; }else{ @@ -117641,14 +117724,14 @@ ** WhereLoop and insert it. */ #if WHERETRACE_ENABLED /* 0x8 */ if( sqlite3WhereTrace & 0x8 ){ if( p!=0 ){ - sqlite3DebugPrintf("ins-del: "); + sqlite3DebugPrintf("replace: "); whereLoopPrint(p, pBuilder->pWC); } - sqlite3DebugPrintf("ins-new: "); + sqlite3DebugPrintf(" add: "); whereLoopPrint(pTemplate, pBuilder->pWC); } #endif if( p==0 ){ /* Allocate a new WhereLoop to add to the end of the list */ @@ -117668,11 +117751,11 @@ pToDel = *ppTail; if( pToDel==0 ) break; *ppTail = pToDel->pNextLoop; #if WHERETRACE_ENABLED /* 0x8 */ if( sqlite3WhereTrace & 0x8 ){ - sqlite3DebugPrintf("ins-del: "); + sqlite3DebugPrintf(" delete: "); whereLoopPrint(pToDel, pBuilder->pWC); } #endif whereLoopDelete(db, pToDel); } @@ -118835,11 +118918,11 @@ if( sqlite3StrICmp(pColl->zName, pIndex->azColl[j])!=0 ) continue; } isMatch = 1; break; } - if( isMatch && (pWInfo->wctrlFlags & WHERE_GROUPBY)==0 ){ + if( isMatch && (wctrlFlags & WHERE_GROUPBY)==0 ){ /* Make sure the sort order is compatible in an ORDER BY clause. ** Sort order is irrelevant for a GROUP BY clause. */ if( revSet ){ if( (rev ^ revIdx)!=pOrderBy->a[i].sortOrder ) isMatch = 0; }else{ @@ -119300,16 +119383,19 @@ pWInfo->revMask = pFrom->revLoop; } if( (pWInfo->wctrlFlags & WHERE_SORTBYGROUP) && pWInfo->nOBSat==pWInfo->pOrderBy->nExpr ){ - Bitmask notUsed = 0; + Bitmask revMask = 0; int nOrder = wherePathSatisfiesOrderBy(pWInfo, pWInfo->pOrderBy, - pFrom, 0, nLoop-1, pFrom->aLoop[nLoop-1], ¬Used + pFrom, 0, nLoop-1, pFrom->aLoop[nLoop-1], &revMask ); assert( pWInfo->sorted==0 ); - pWInfo->sorted = (nOrder==pWInfo->pOrderBy->nExpr); + if( nOrder==pWInfo->pOrderBy->nExpr ){ + pWInfo->sorted = 1; + pWInfo->revMask = revMask; + } } } pWInfo->nRowOut = pFrom->nRow; @@ -120194,11 +120280,11 @@ /* A routine to convert a binary TK_IS or TK_ISNOT expression into a ** unary TK_ISNULL or TK_NOTNULL expression. */ static void binaryToUnaryIfNull(Parse *pParse, Expr *pY, Expr *pA, int op){ sqlite3 *db = pParse->db; - if( db->mallocFailed==0 && pY->op==TK_NULL ){ + if( pY && pA && pY->op==TK_NULL ){ pA->op = (u8)op; sqlite3ExprDelete(db, pA->pRight); pA->pRight = 0; } } @@ -125295,10 +125381,15 @@ sqlite3GlobalConfig.xLog = va_arg(ap, LOGFUNC_t); sqlite3GlobalConfig.pLogArg = va_arg(ap, void*); break; } + /* EVIDENCE-OF: R-55548-33817 The compile-time setting for URI filenames + ** can be changed at start-time using the + ** sqlite3_config(SQLITE_CONFIG_URI,1) or + ** sqlite3_config(SQLITE_CONFIG_URI,0) configuration calls. + */ case SQLITE_CONFIG_URI: { sqlite3GlobalConfig.bOpenUri = va_arg(ap, int); break; } @@ -127032,11 +127123,11 @@ int nUri = sqlite3Strlen30(zUri); assert( *pzErrMsg==0 ); if( ((flags & SQLITE_OPEN_URI) || sqlite3GlobalConfig.bOpenUri) - && nUri>=5 && memcmp(zUri, "file:", 5)==0 + && nUri>=5 && memcmp(zUri, "file:", 5)==0 /* IMP: R-57884-37496 */ ){ char *zOpt; int eState; /* Parser state when parsing URI */ int iIn; /* Input character index */ int iOut = 0; /* Output character index */ @@ -127262,11 +127353,13 @@ assert( SQLITE_OPEN_READWRITE == 0x02 ); assert( SQLITE_OPEN_CREATE == 0x04 ); testcase( (1<<(flags&7))==0x02 ); /* READONLY */ testcase( (1<<(flags&7))==0x04 ); /* READWRITE */ testcase( (1<<(flags&7))==0x40 ); /* READWRITE | CREATE */ - if( ((1<<(flags&7)) & 0x46)==0 ) return SQLITE_MISUSE_BKPT; + if( ((1<<(flags&7)) & 0x46)==0 ){ + return SQLITE_MISUSE_BKPT; /* IMP: R-65497-44594 */ + } if( sqlite3GlobalConfig.bCoreMutex==0 ){ isThreadsafe = 0; }else if( flags & SQLITE_OPEN_NOMUTEX ){ isThreadsafe = 0; @@ -132605,10 +132698,11 @@ assert( iIdx==nVal ); /* In case the cursor has been used before, clear it now. */ sqlite3_finalize(pCsr->pStmt); sqlite3_free(pCsr->aDoclist); + sqlite3_free(pCsr->aMatchinfo); sqlite3Fts3ExprFree(pCsr->pExpr); memset(&pCursor[1], 0, sizeof(Fts3Cursor)-sizeof(sqlite3_vtab_cursor)); /* Set the lower and upper bounds on docids to return */ pCsr->iMinDocid = fts3DocidRange(pDocidGe, SMALLEST_INT64); @@ -133915,11 +134009,11 @@ if( a[i].bIgnore==0 && (bMaxSet==0 || DOCID_CMP(iMax, a[i].iDocid)<0) ){ iMax = a[i].iDocid; bMaxSet = 1; } } - assert( rc!=SQLITE_OK || a[p->nToken-1].bIgnore==0 ); + assert( rc!=SQLITE_OK || (p->nToken>=1 && a[p->nToken-1].bIgnore==0) ); assert( rc!=SQLITE_OK || bMaxSet ); /* Keep advancing iterators until they all point to the same document */ for(i=0; inToken; i++){ while( rc==SQLITE_OK && bEof==0 @@ -136032,11 +136126,11 @@ int i = 0; /* Set variable i to the maximum number of bytes of input to tokenize. */ for(i=0; iiLangid, z, i, &pCursor); if( rc==SQLITE_OK ){ Index: src/sqlite3.h ================================================================== --- src/sqlite3.h +++ src/sqlite3.h @@ -107,11 +107,11 @@ ** [sqlite3_libversion_number()], [sqlite3_sourceid()], ** [sqlite_version()] and [sqlite_source_id()]. */ #define SQLITE_VERSION "3.8.7" #define SQLITE_VERSION_NUMBER 3008007 -#define SQLITE_SOURCE_ID "2014-10-01 13:17:34 07c89940c49a5dca3205a4b6fa8290f23bcb6e10" +#define SQLITE_SOURCE_ID "2014-10-17 11:24:17 e4ab094f8afce0817f4074e823fabe59fc29ebb4" /* ** CAPI3REF: Run-Time Library Version Numbers ** KEYWORDS: sqlite3_version, sqlite3_sourceid ** @@ -2667,13 +2667,13 @@ ** [SQLITE_OK] is returned. Otherwise an [error code] is returned.)^ ^The ** [sqlite3_errmsg()] or [sqlite3_errmsg16()] routines can be used to obtain ** an English language description of the error following a failure of any ** of the sqlite3_open() routines. ** -** ^The default encoding for the database will be UTF-8 if -** sqlite3_open() or sqlite3_open_v2() is called and -** UTF-16 in the native byte order if sqlite3_open16() is used. +** ^The default encoding will be UTF-8 for databases created using +** sqlite3_open() or sqlite3_open_v2(). ^The default encoding for databases +** created using sqlite3_open16() will be UTF-16 in the native byte order. ** ** Whether or not an error occurs when it is opened, resources ** associated with the [database connection] handle should be released by ** passing it to [sqlite3_close()] when it is no longer required. ** @@ -2757,17 +2757,18 @@ ** ^SQLite uses the path component of the URI as the name of the disk file ** which contains the database. ^If the path begins with a '/' character, ** then it is interpreted as an absolute path. ^If the path does not begin ** with a '/' (meaning that the authority section is omitted from the URI) ** then the path is interpreted as a relative path. -** ^On windows, the first component of an absolute path -** is a drive specification (e.g. "C:"). +** ^(On windows, the first component of an absolute path +** is a drive specification (e.g. "C:").)^ ** ** [[core URI query parameters]] ** The query component of a URI may contain parameters that are interpreted ** either by SQLite itself, or by a [VFS | custom VFS implementation]. -** SQLite interprets the following three query parameters: +** SQLite and its built-in [VFSes] interpret the +** following query parameters: ** **
      **
    • vfs: ^The "vfs" parameter may be used to specify the name of ** a VFS object that provides the operating system interface that should ** be used to access the database file on disk. ^If this option is set to @@ -2798,15 +2799,13 @@ ** equivalent to setting the SQLITE_OPEN_PRIVATECACHE bit. ** ^If sqlite3_open_v2() is used and the "cache" parameter is present in ** a URI filename, its value overrides any behavior requested by setting ** SQLITE_OPEN_PRIVATECACHE or SQLITE_OPEN_SHAREDCACHE flag. ** -**
    • psow: ^The psow parameter may be "true" (or "on" or "yes" or -** "1") or "false" (or "off" or "no" or "0") to indicate that the +**
    • psow: ^The psow parameter indicates whether or not the ** [powersafe overwrite] property does or does not apply to the -** storage media on which the database file resides. ^The psow query -** parameter only works for the built-in unix and Windows VFSes. +** storage media on which the database file resides. ** **
    • nolock: ^The nolock parameter is a boolean query parameter ** which if set disables file locking in rollback journal modes. This ** is useful for accessing a database on a filesystem that does not ** support locking. Caution: Database corruption might result if two @@ -3397,15 +3396,14 @@ ** terminated. If any NUL characters occur at byte offsets less than ** the value of the fourth parameter then the resulting string value will ** contain embedded NULs. The result of expressions involving strings ** with embedded NULs is undefined. ** -** ^The fifth argument to sqlite3_bind_blob(), sqlite3_bind_text(), and -** sqlite3_bind_text16() is a destructor used to dispose of the BLOB or +** ^The fifth argument to the BLOB and string binding interfaces +** is a destructor used to dispose of the BLOB or ** string after SQLite has finished with it. ^The destructor is called -** to dispose of the BLOB or string even if the call to sqlite3_bind_blob(), -** sqlite3_bind_text(), or sqlite3_bind_text16() fails. +** to dispose of the BLOB or string even if the call to bind API fails. ** ^If the fifth argument is ** the special value [SQLITE_STATIC], then SQLite assumes that the ** information is in static, unmanaged space and does not need to be freed. ** ^If the fifth argument has the value [SQLITE_TRANSIENT], then ** SQLite makes its own private copy of the data immediately, before @@ -3412,11 +3410,11 @@ ** the sqlite3_bind_*() routine returns. ** ** ^The sixth argument to sqlite3_bind_text64() must be one of ** [SQLITE_UTF8], [SQLITE_UTF16], [SQLITE_UTF16BE], or [SQLITE_UTF16LE] ** to specify the encoding of the text in the third parameter. If -** the sixth argument to sqlite3_bind_text64() is not how of the +** the sixth argument to sqlite3_bind_text64() is not one of the ** allowed values shown above, or if the text encoding is different ** from the encoding specified by the sixth parameter, then the behavior ** is undefined. ** ** ^The sqlite3_bind_zeroblob() routine binds a BLOB of length N that @@ -4448,11 +4446,11 @@ ** ** ^The sqlite3_result_null() interface sets the return value ** of the application-defined function to be NULL. ** ** ^The sqlite3_result_text(), sqlite3_result_text16(), -** sqlite3_result_text16le(), and sqlite3_result_text16be() +** sqlite3_result_text16le(), and sqlite3_result_text16be() interfaces ** set the return value of the application-defined function to be ** a text string which is represented as UTF-8, UTF-16 native byte order, ** UTF-16 little endian, or UTF-16 big endian, respectively. ** ^The sqlite3_result_text64() interface sets the return value of an ** application-defined function to be a text string in an encoding ADDED src/sqlite3ext.h Index: src/sqlite3ext.h ================================================================== --- /dev/null +++ src/sqlite3ext.h @@ -0,0 +1,517 @@ +/* +** 2006 June 7 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +************************************************************************* +** This header file defines the SQLite interface for use by +** shared libraries that want to be imported as extensions into +** an SQLite instance. Shared libraries that intend to be loaded +** as extensions by SQLite should #include this file instead of +** sqlite3.h. +*/ +#ifndef _SQLITE3EXT_H_ +#define _SQLITE3EXT_H_ +#include "sqlite3.h" + +typedef struct sqlite3_api_routines sqlite3_api_routines; + +/* +** The following structure holds pointers to all of the SQLite API +** routines. +** +** WARNING: In order to maintain backwards compatibility, add new +** interfaces to the end of this structure only. If you insert new +** interfaces in the middle of this structure, then older different +** versions of SQLite will not be able to load each other's shared +** libraries! +*/ +struct sqlite3_api_routines { + void * (*aggregate_context)(sqlite3_context*,int nBytes); + int (*aggregate_count)(sqlite3_context*); + int (*bind_blob)(sqlite3_stmt*,int,const void*,int n,void(*)(void*)); + int (*bind_double)(sqlite3_stmt*,int,double); + int (*bind_int)(sqlite3_stmt*,int,int); + int (*bind_int64)(sqlite3_stmt*,int,sqlite_int64); + int (*bind_null)(sqlite3_stmt*,int); + int (*bind_parameter_count)(sqlite3_stmt*); + int (*bind_parameter_index)(sqlite3_stmt*,const char*zName); + const char * (*bind_parameter_name)(sqlite3_stmt*,int); + int (*bind_text)(sqlite3_stmt*,int,const char*,int n,void(*)(void*)); + int (*bind_text16)(sqlite3_stmt*,int,const void*,int,void(*)(void*)); + int (*bind_value)(sqlite3_stmt*,int,const sqlite3_value*); + int (*busy_handler)(sqlite3*,int(*)(void*,int),void*); + int (*busy_timeout)(sqlite3*,int ms); + int (*changes)(sqlite3*); + int (*close)(sqlite3*); + int (*collation_needed)(sqlite3*,void*,void(*)(void*,sqlite3*, + int eTextRep,const char*)); + int (*collation_needed16)(sqlite3*,void*,void(*)(void*,sqlite3*, + int eTextRep,const void*)); + const void * (*column_blob)(sqlite3_stmt*,int iCol); + int (*column_bytes)(sqlite3_stmt*,int iCol); + int (*column_bytes16)(sqlite3_stmt*,int iCol); + int (*column_count)(sqlite3_stmt*pStmt); + const char * (*column_database_name)(sqlite3_stmt*,int); + const void * (*column_database_name16)(sqlite3_stmt*,int); + const char * (*column_decltype)(sqlite3_stmt*,int i); + const void * (*column_decltype16)(sqlite3_stmt*,int); + double (*column_double)(sqlite3_stmt*,int iCol); + int (*column_int)(sqlite3_stmt*,int iCol); + sqlite_int64 (*column_int64)(sqlite3_stmt*,int iCol); + const char * (*column_name)(sqlite3_stmt*,int); + const void * (*column_name16)(sqlite3_stmt*,int); + const char * (*column_origin_name)(sqlite3_stmt*,int); + const void * (*column_origin_name16)(sqlite3_stmt*,int); + const char * (*column_table_name)(sqlite3_stmt*,int); + const void * (*column_table_name16)(sqlite3_stmt*,int); + const unsigned char * (*column_text)(sqlite3_stmt*,int iCol); + const void * (*column_text16)(sqlite3_stmt*,int iCol); + int (*column_type)(sqlite3_stmt*,int iCol); + sqlite3_value* (*column_value)(sqlite3_stmt*,int iCol); + void * (*commit_hook)(sqlite3*,int(*)(void*),void*); + int (*complete)(const char*sql); + int (*complete16)(const void*sql); + int (*create_collation)(sqlite3*,const char*,int,void*, + int(*)(void*,int,const void*,int,const void*)); + int (*create_collation16)(sqlite3*,const void*,int,void*, + int(*)(void*,int,const void*,int,const void*)); + int (*create_function)(sqlite3*,const char*,int,int,void*, + void (*xFunc)(sqlite3_context*,int,sqlite3_value**), + void (*xStep)(sqlite3_context*,int,sqlite3_value**), + void (*xFinal)(sqlite3_context*)); + int (*create_function16)(sqlite3*,const void*,int,int,void*, + void (*xFunc)(sqlite3_context*,int,sqlite3_value**), + void (*xStep)(sqlite3_context*,int,sqlite3_value**), + void (*xFinal)(sqlite3_context*)); + int (*create_module)(sqlite3*,const char*,const sqlite3_module*,void*); + int (*data_count)(sqlite3_stmt*pStmt); + sqlite3 * (*db_handle)(sqlite3_stmt*); + int (*declare_vtab)(sqlite3*,const char*); + int (*enable_shared_cache)(int); + int (*errcode)(sqlite3*db); + const char * (*errmsg)(sqlite3*); + const void * (*errmsg16)(sqlite3*); + int (*exec)(sqlite3*,const char*,sqlite3_callback,void*,char**); + int (*expired)(sqlite3_stmt*); + int (*finalize)(sqlite3_stmt*pStmt); + void (*free)(void*); + void (*free_table)(char**result); + int (*get_autocommit)(sqlite3*); + void * (*get_auxdata)(sqlite3_context*,int); + int (*get_table)(sqlite3*,const char*,char***,int*,int*,char**); + int (*global_recover)(void); + void (*interruptx)(sqlite3*); + sqlite_int64 (*last_insert_rowid)(sqlite3*); + const char * (*libversion)(void); + int (*libversion_number)(void); + void *(*malloc)(int); + char * (*mprintf)(const char*,...); + int (*open)(const char*,sqlite3**); + int (*open16)(const void*,sqlite3**); + int (*prepare)(sqlite3*,const char*,int,sqlite3_stmt**,const char**); + int (*prepare16)(sqlite3*,const void*,int,sqlite3_stmt**,const void**); + void * (*profile)(sqlite3*,void(*)(void*,const char*,sqlite_uint64),void*); + void (*progress_handler)(sqlite3*,int,int(*)(void*),void*); + void *(*realloc)(void*,int); + int (*reset)(sqlite3_stmt*pStmt); + void (*result_blob)(sqlite3_context*,const void*,int,void(*)(void*)); + void (*result_double)(sqlite3_context*,double); + void (*result_error)(sqlite3_context*,const char*,int); + void (*result_error16)(sqlite3_context*,const void*,int); + void (*result_int)(sqlite3_context*,int); + void (*result_int64)(sqlite3_context*,sqlite_int64); + void (*result_null)(sqlite3_context*); + void (*result_text)(sqlite3_context*,const char*,int,void(*)(void*)); + void (*result_text16)(sqlite3_context*,const void*,int,void(*)(void*)); + void (*result_text16be)(sqlite3_context*,const void*,int,void(*)(void*)); + void (*result_text16le)(sqlite3_context*,const void*,int,void(*)(void*)); + void (*result_value)(sqlite3_context*,sqlite3_value*); + void * (*rollback_hook)(sqlite3*,void(*)(void*),void*); + int (*set_authorizer)(sqlite3*,int(*)(void*,int,const char*,const char*, + const char*,const char*),void*); + void (*set_auxdata)(sqlite3_context*,int,void*,void (*)(void*)); + char * (*snprintf)(int,char*,const char*,...); + int (*step)(sqlite3_stmt*); + int (*table_column_metadata)(sqlite3*,const char*,const char*,const char*, + char const**,char const**,int*,int*,int*); + void (*thread_cleanup)(void); + int (*total_changes)(sqlite3*); + void * (*trace)(sqlite3*,void(*xTrace)(void*,const char*),void*); + int (*transfer_bindings)(sqlite3_stmt*,sqlite3_stmt*); + void * (*update_hook)(sqlite3*,void(*)(void*,int ,char const*,char const*, + sqlite_int64),void*); + void * (*user_data)(sqlite3_context*); + const void * (*value_blob)(sqlite3_value*); + int (*value_bytes)(sqlite3_value*); + int (*value_bytes16)(sqlite3_value*); + double (*value_double)(sqlite3_value*); + int (*value_int)(sqlite3_value*); + sqlite_int64 (*value_int64)(sqlite3_value*); + int (*value_numeric_type)(sqlite3_value*); + const unsigned char * (*value_text)(sqlite3_value*); + const void * (*value_text16)(sqlite3_value*); + const void * (*value_text16be)(sqlite3_value*); + const void * (*value_text16le)(sqlite3_value*); + int (*value_type)(sqlite3_value*); + char *(*vmprintf)(const char*,va_list); + /* Added ??? */ + int (*overload_function)(sqlite3*, const char *zFuncName, int nArg); + /* Added by 3.3.13 */ + int (*prepare_v2)(sqlite3*,const char*,int,sqlite3_stmt**,const char**); + int (*prepare16_v2)(sqlite3*,const void*,int,sqlite3_stmt**,const void**); + int (*clear_bindings)(sqlite3_stmt*); + /* Added by 3.4.1 */ + int (*create_module_v2)(sqlite3*,const char*,const sqlite3_module*,void*, + void (*xDestroy)(void *)); + /* Added by 3.5.0 */ + int (*bind_zeroblob)(sqlite3_stmt*,int,int); + int (*blob_bytes)(sqlite3_blob*); + int (*blob_close)(sqlite3_blob*); + int (*blob_open)(sqlite3*,const char*,const char*,const char*,sqlite3_int64, + int,sqlite3_blob**); + int (*blob_read)(sqlite3_blob*,void*,int,int); + int (*blob_write)(sqlite3_blob*,const void*,int,int); + int (*create_collation_v2)(sqlite3*,const char*,int,void*, + int(*)(void*,int,const void*,int,const void*), + void(*)(void*)); + int (*file_control)(sqlite3*,const char*,int,void*); + sqlite3_int64 (*memory_highwater)(int); + sqlite3_int64 (*memory_used)(void); + sqlite3_mutex *(*mutex_alloc)(int); + void (*mutex_enter)(sqlite3_mutex*); + void (*mutex_free)(sqlite3_mutex*); + void (*mutex_leave)(sqlite3_mutex*); + int (*mutex_try)(sqlite3_mutex*); + int (*open_v2)(const char*,sqlite3**,int,const char*); + int (*release_memory)(int); + void (*result_error_nomem)(sqlite3_context*); + void (*result_error_toobig)(sqlite3_context*); + int (*sleep)(int); + void (*soft_heap_limit)(int); + sqlite3_vfs *(*vfs_find)(const char*); + int (*vfs_register)(sqlite3_vfs*,int); + int (*vfs_unregister)(sqlite3_vfs*); + int (*xthreadsafe)(void); + void (*result_zeroblob)(sqlite3_context*,int); + void (*result_error_code)(sqlite3_context*,int); + int (*test_control)(int, ...); + void (*randomness)(int,void*); + sqlite3 *(*context_db_handle)(sqlite3_context*); + int (*extended_result_codes)(sqlite3*,int); + int (*limit)(sqlite3*,int,int); + sqlite3_stmt *(*next_stmt)(sqlite3*,sqlite3_stmt*); + const char *(*sql)(sqlite3_stmt*); + int (*status)(int,int*,int*,int); + int (*backup_finish)(sqlite3_backup*); + sqlite3_backup *(*backup_init)(sqlite3*,const char*,sqlite3*,const char*); + int (*backup_pagecount)(sqlite3_backup*); + int (*backup_remaining)(sqlite3_backup*); + int (*backup_step)(sqlite3_backup*,int); + const char *(*compileoption_get)(int); + int (*compileoption_used)(const char*); + int (*create_function_v2)(sqlite3*,const char*,int,int,void*, + void (*xFunc)(sqlite3_context*,int,sqlite3_value**), + void (*xStep)(sqlite3_context*,int,sqlite3_value**), + void (*xFinal)(sqlite3_context*), + void(*xDestroy)(void*)); + int (*db_config)(sqlite3*,int,...); + sqlite3_mutex *(*db_mutex)(sqlite3*); + int (*db_status)(sqlite3*,int,int*,int*,int); + int (*extended_errcode)(sqlite3*); + void (*log)(int,const char*,...); + sqlite3_int64 (*soft_heap_limit64)(sqlite3_int64); + const char *(*sourceid)(void); + int (*stmt_status)(sqlite3_stmt*,int,int); + int (*strnicmp)(const char*,const char*,int); + int (*unlock_notify)(sqlite3*,void(*)(void**,int),void*); + int (*wal_autocheckpoint)(sqlite3*,int); + int (*wal_checkpoint)(sqlite3*,const char*); + void *(*wal_hook)(sqlite3*,int(*)(void*,sqlite3*,const char*,int),void*); + int (*blob_reopen)(sqlite3_blob*,sqlite3_int64); + int (*vtab_config)(sqlite3*,int op,...); + int (*vtab_on_conflict)(sqlite3*); + /* Version 3.7.16 and later */ + int (*close_v2)(sqlite3*); + const char *(*db_filename)(sqlite3*,const char*); + int (*db_readonly)(sqlite3*,const char*); + int (*db_release_memory)(sqlite3*); + const char *(*errstr)(int); + int (*stmt_busy)(sqlite3_stmt*); + int (*stmt_readonly)(sqlite3_stmt*); + int (*stricmp)(const char*,const char*); + int (*uri_boolean)(const char*,const char*,int); + sqlite3_int64 (*uri_int64)(const char*,const char*,sqlite3_int64); + const char *(*uri_parameter)(const char*,const char*); + char *(*vsnprintf)(int,char*,const char*,va_list); + int (*wal_checkpoint_v2)(sqlite3*,const char*,int,int*,int*); + /* Version 3.8.7 and later */ + int (*auto_extension)(void(*)(void)); + int (*bind_blob64)(sqlite3_stmt*,int,const void*,sqlite3_uint64, + void(*)(void*)); + int (*bind_text64)(sqlite3_stmt*,int,const char*,sqlite3_uint64, + void(*)(void*),unsigned char); + int (*cancel_auto_extension)(void(*)(void)); + int (*load_extension)(sqlite3*,const char*,const char*,char**); + void *(*malloc64)(sqlite3_uint64); + sqlite3_uint64 (*msize)(void*); + void *(*realloc64)(void*,sqlite3_uint64); + void (*reset_auto_extension)(void); + void (*result_blob64)(sqlite3_context*,const void*,sqlite3_uint64, + void(*)(void*)); + void (*result_text64)(sqlite3_context*,const char*,sqlite3_uint64, + void(*)(void*), unsigned char); + int (*strglob)(const char*,const char*); +}; + +/* +** The following macros redefine the API routines so that they are +** redirected through the global sqlite3_api structure. +** +** This header file is also used by the loadext.c source file +** (part of the main SQLite library - not an extension) so that +** it can get access to the sqlite3_api_routines structure +** definition. But the main library does not want to redefine +** the API. So the redefinition macros are only valid if the +** SQLITE_CORE macros is undefined. +*/ +#ifndef SQLITE_CORE +#define sqlite3_aggregate_context sqlite3_api->aggregate_context +#ifndef SQLITE_OMIT_DEPRECATED +#define sqlite3_aggregate_count sqlite3_api->aggregate_count +#endif +#define sqlite3_bind_blob sqlite3_api->bind_blob +#define sqlite3_bind_double sqlite3_api->bind_double +#define sqlite3_bind_int sqlite3_api->bind_int +#define sqlite3_bind_int64 sqlite3_api->bind_int64 +#define sqlite3_bind_null sqlite3_api->bind_null +#define sqlite3_bind_parameter_count sqlite3_api->bind_parameter_count +#define sqlite3_bind_parameter_index sqlite3_api->bind_parameter_index +#define sqlite3_bind_parameter_name sqlite3_api->bind_parameter_name +#define sqlite3_bind_text sqlite3_api->bind_text +#define sqlite3_bind_text16 sqlite3_api->bind_text16 +#define sqlite3_bind_value sqlite3_api->bind_value +#define sqlite3_busy_handler sqlite3_api->busy_handler +#define sqlite3_busy_timeout sqlite3_api->busy_timeout +#define sqlite3_changes sqlite3_api->changes +#define sqlite3_close sqlite3_api->close +#define sqlite3_collation_needed sqlite3_api->collation_needed +#define sqlite3_collation_needed16 sqlite3_api->collation_needed16 +#define sqlite3_column_blob sqlite3_api->column_blob +#define sqlite3_column_bytes sqlite3_api->column_bytes +#define sqlite3_column_bytes16 sqlite3_api->column_bytes16 +#define sqlite3_column_count sqlite3_api->column_count +#define sqlite3_column_database_name sqlite3_api->column_database_name +#define sqlite3_column_database_name16 sqlite3_api->column_database_name16 +#define sqlite3_column_decltype sqlite3_api->column_decltype +#define sqlite3_column_decltype16 sqlite3_api->column_decltype16 +#define sqlite3_column_double sqlite3_api->column_double +#define sqlite3_column_int sqlite3_api->column_int +#define sqlite3_column_int64 sqlite3_api->column_int64 +#define sqlite3_column_name sqlite3_api->column_name +#define sqlite3_column_name16 sqlite3_api->column_name16 +#define sqlite3_column_origin_name sqlite3_api->column_origin_name +#define sqlite3_column_origin_name16 sqlite3_api->column_origin_name16 +#define sqlite3_column_table_name sqlite3_api->column_table_name +#define sqlite3_column_table_name16 sqlite3_api->column_table_name16 +#define sqlite3_column_text sqlite3_api->column_text +#define sqlite3_column_text16 sqlite3_api->column_text16 +#define sqlite3_column_type sqlite3_api->column_type +#define sqlite3_column_value sqlite3_api->column_value +#define sqlite3_commit_hook sqlite3_api->commit_hook +#define sqlite3_complete sqlite3_api->complete +#define sqlite3_complete16 sqlite3_api->complete16 +#define sqlite3_create_collation sqlite3_api->create_collation +#define sqlite3_create_collation16 sqlite3_api->create_collation16 +#define sqlite3_create_function sqlite3_api->create_function +#define sqlite3_create_function16 sqlite3_api->create_function16 +#define sqlite3_create_module sqlite3_api->create_module +#define sqlite3_create_module_v2 sqlite3_api->create_module_v2 +#define sqlite3_data_count sqlite3_api->data_count +#define sqlite3_db_handle sqlite3_api->db_handle +#define sqlite3_declare_vtab sqlite3_api->declare_vtab +#define sqlite3_enable_shared_cache sqlite3_api->enable_shared_cache +#define sqlite3_errcode sqlite3_api->errcode +#define sqlite3_errmsg sqlite3_api->errmsg +#define sqlite3_errmsg16 sqlite3_api->errmsg16 +#define sqlite3_exec sqlite3_api->exec +#ifndef SQLITE_OMIT_DEPRECATED +#define sqlite3_expired sqlite3_api->expired +#endif +#define sqlite3_finalize sqlite3_api->finalize +#define sqlite3_free sqlite3_api->free +#define sqlite3_free_table sqlite3_api->free_table +#define sqlite3_get_autocommit sqlite3_api->get_autocommit +#define sqlite3_get_auxdata sqlite3_api->get_auxdata +#define sqlite3_get_table sqlite3_api->get_table +#ifndef SQLITE_OMIT_DEPRECATED +#define sqlite3_global_recover sqlite3_api->global_recover +#endif +#define sqlite3_interrupt sqlite3_api->interruptx +#define sqlite3_last_insert_rowid sqlite3_api->last_insert_rowid +#define sqlite3_libversion sqlite3_api->libversion +#define sqlite3_libversion_number sqlite3_api->libversion_number +#define sqlite3_malloc sqlite3_api->malloc +#define sqlite3_mprintf sqlite3_api->mprintf +#define sqlite3_open sqlite3_api->open +#define sqlite3_open16 sqlite3_api->open16 +#define sqlite3_prepare sqlite3_api->prepare +#define sqlite3_prepare16 sqlite3_api->prepare16 +#define sqlite3_prepare_v2 sqlite3_api->prepare_v2 +#define sqlite3_prepare16_v2 sqlite3_api->prepare16_v2 +#define sqlite3_profile sqlite3_api->profile +#define sqlite3_progress_handler sqlite3_api->progress_handler +#define sqlite3_realloc sqlite3_api->realloc +#define sqlite3_reset sqlite3_api->reset +#define sqlite3_result_blob sqlite3_api->result_blob +#define sqlite3_result_double sqlite3_api->result_double +#define sqlite3_result_error sqlite3_api->result_error +#define sqlite3_result_error16 sqlite3_api->result_error16 +#define sqlite3_result_int sqlite3_api->result_int +#define sqlite3_result_int64 sqlite3_api->result_int64 +#define sqlite3_result_null sqlite3_api->result_null +#define sqlite3_result_text sqlite3_api->result_text +#define sqlite3_result_text16 sqlite3_api->result_text16 +#define sqlite3_result_text16be sqlite3_api->result_text16be +#define sqlite3_result_text16le sqlite3_api->result_text16le +#define sqlite3_result_value sqlite3_api->result_value +#define sqlite3_rollback_hook sqlite3_api->rollback_hook +#define sqlite3_set_authorizer sqlite3_api->set_authorizer +#define sqlite3_set_auxdata sqlite3_api->set_auxdata +#define sqlite3_snprintf sqlite3_api->snprintf +#define sqlite3_step sqlite3_api->step +#define sqlite3_table_column_metadata sqlite3_api->table_column_metadata +#define sqlite3_thread_cleanup sqlite3_api->thread_cleanup +#define sqlite3_total_changes sqlite3_api->total_changes +#define sqlite3_trace sqlite3_api->trace +#ifndef SQLITE_OMIT_DEPRECATED +#define sqlite3_transfer_bindings sqlite3_api->transfer_bindings +#endif +#define sqlite3_update_hook sqlite3_api->update_hook +#define sqlite3_user_data sqlite3_api->user_data +#define sqlite3_value_blob sqlite3_api->value_blob +#define sqlite3_value_bytes sqlite3_api->value_bytes +#define sqlite3_value_bytes16 sqlite3_api->value_bytes16 +#define sqlite3_value_double sqlite3_api->value_double +#define sqlite3_value_int sqlite3_api->value_int +#define sqlite3_value_int64 sqlite3_api->value_int64 +#define sqlite3_value_numeric_type sqlite3_api->value_numeric_type +#define sqlite3_value_text sqlite3_api->value_text +#define sqlite3_value_text16 sqlite3_api->value_text16 +#define sqlite3_value_text16be sqlite3_api->value_text16be +#define sqlite3_value_text16le sqlite3_api->value_text16le +#define sqlite3_value_type sqlite3_api->value_type +#define sqlite3_vmprintf sqlite3_api->vmprintf +#define sqlite3_overload_function sqlite3_api->overload_function +#define sqlite3_prepare_v2 sqlite3_api->prepare_v2 +#define sqlite3_prepare16_v2 sqlite3_api->prepare16_v2 +#define sqlite3_clear_bindings sqlite3_api->clear_bindings +#define sqlite3_bind_zeroblob sqlite3_api->bind_zeroblob +#define sqlite3_blob_bytes sqlite3_api->blob_bytes +#define sqlite3_blob_close sqlite3_api->blob_close +#define sqlite3_blob_open sqlite3_api->blob_open +#define sqlite3_blob_read sqlite3_api->blob_read +#define sqlite3_blob_write sqlite3_api->blob_write +#define sqlite3_create_collation_v2 sqlite3_api->create_collation_v2 +#define sqlite3_file_control sqlite3_api->file_control +#define sqlite3_memory_highwater sqlite3_api->memory_highwater +#define sqlite3_memory_used sqlite3_api->memory_used +#define sqlite3_mutex_alloc sqlite3_api->mutex_alloc +#define sqlite3_mutex_enter sqlite3_api->mutex_enter +#define sqlite3_mutex_free sqlite3_api->mutex_free +#define sqlite3_mutex_leave sqlite3_api->mutex_leave +#define sqlite3_mutex_try sqlite3_api->mutex_try +#define sqlite3_open_v2 sqlite3_api->open_v2 +#define sqlite3_release_memory sqlite3_api->release_memory +#define sqlite3_result_error_nomem sqlite3_api->result_error_nomem +#define sqlite3_result_error_toobig sqlite3_api->result_error_toobig +#define sqlite3_sleep sqlite3_api->sleep +#define sqlite3_soft_heap_limit sqlite3_api->soft_heap_limit +#define sqlite3_vfs_find sqlite3_api->vfs_find +#define sqlite3_vfs_register sqlite3_api->vfs_register +#define sqlite3_vfs_unregister sqlite3_api->vfs_unregister +#define sqlite3_threadsafe sqlite3_api->xthreadsafe +#define sqlite3_result_zeroblob sqlite3_api->result_zeroblob +#define sqlite3_result_error_code sqlite3_api->result_error_code +#define sqlite3_test_control sqlite3_api->test_control +#define sqlite3_randomness sqlite3_api->randomness +#define sqlite3_context_db_handle sqlite3_api->context_db_handle +#define sqlite3_extended_result_codes sqlite3_api->extended_result_codes +#define sqlite3_limit sqlite3_api->limit +#define sqlite3_next_stmt sqlite3_api->next_stmt +#define sqlite3_sql sqlite3_api->sql +#define sqlite3_status sqlite3_api->status +#define sqlite3_backup_finish sqlite3_api->backup_finish +#define sqlite3_backup_init sqlite3_api->backup_init +#define sqlite3_backup_pagecount sqlite3_api->backup_pagecount +#define sqlite3_backup_remaining sqlite3_api->backup_remaining +#define sqlite3_backup_step sqlite3_api->backup_step +#define sqlite3_compileoption_get sqlite3_api->compileoption_get +#define sqlite3_compileoption_used sqlite3_api->compileoption_used +#define sqlite3_create_function_v2 sqlite3_api->create_function_v2 +#define sqlite3_db_config sqlite3_api->db_config +#define sqlite3_db_mutex sqlite3_api->db_mutex +#define sqlite3_db_status sqlite3_api->db_status +#define sqlite3_extended_errcode sqlite3_api->extended_errcode +#define sqlite3_log sqlite3_api->log +#define sqlite3_soft_heap_limit64 sqlite3_api->soft_heap_limit64 +#define sqlite3_sourceid sqlite3_api->sourceid +#define sqlite3_stmt_status sqlite3_api->stmt_status +#define sqlite3_strnicmp sqlite3_api->strnicmp +#define sqlite3_unlock_notify sqlite3_api->unlock_notify +#define sqlite3_wal_autocheckpoint sqlite3_api->wal_autocheckpoint +#define sqlite3_wal_checkpoint sqlite3_api->wal_checkpoint +#define sqlite3_wal_hook sqlite3_api->wal_hook +#define sqlite3_blob_reopen sqlite3_api->blob_reopen +#define sqlite3_vtab_config sqlite3_api->vtab_config +#define sqlite3_vtab_on_conflict sqlite3_api->vtab_on_conflict +/* Version 3.7.16 and later */ +#define sqlite3_close_v2 sqlite3_api->close_v2 +#define sqlite3_db_filename sqlite3_api->db_filename +#define sqlite3_db_readonly sqlite3_api->db_readonly +#define sqlite3_db_release_memory sqlite3_api->db_release_memory +#define sqlite3_errstr sqlite3_api->errstr +#define sqlite3_stmt_busy sqlite3_api->stmt_busy +#define sqlite3_stmt_readonly sqlite3_api->stmt_readonly +#define sqlite3_stricmp sqlite3_api->stricmp +#define sqlite3_uri_boolean sqlite3_api->uri_boolean +#define sqlite3_uri_int64 sqlite3_api->uri_int64 +#define sqlite3_uri_parameter sqlite3_api->uri_parameter +#define sqlite3_uri_vsnprintf sqlite3_api->vsnprintf +#define sqlite3_wal_checkpoint_v2 sqlite3_api->wal_checkpoint_v2 +/* Version 3.8.7 and later */ +#define sqlite3_auto_extension sqlite3_api->auto_extension +#define sqlite3_bind_blob64 sqlite3_api->bind_blob64 +#define sqlite3_bind_text64 sqlite3_api->bind_text64 +#define sqlite3_cancel_auto_extension sqlite3_api->cancel_auto_extension +#define sqlite3_load_extension sqlite3_api->load_extension +#define sqlite3_malloc64 sqlite3_api->malloc64 +#define sqlite3_msize sqlite3_api->msize +#define sqlite3_realloc64 sqlite3_api->realloc64 +#define sqlite3_reset_auto_extension sqlite3_api->reset_auto_extension +#define sqlite3_result_blob64 sqlite3_api->result_blob64 +#define sqlite3_result_text64 sqlite3_api->result_text64 +#define sqlite3_strglob sqlite3_api->strglob +#endif /* SQLITE_CORE */ + +#ifndef SQLITE_CORE + /* This case when the file really is being compiled as a loadable + ** extension */ +# define SQLITE_EXTENSION_INIT1 const sqlite3_api_routines *sqlite3_api=0; +# define SQLITE_EXTENSION_INIT2(v) sqlite3_api=v; +# define SQLITE_EXTENSION_INIT3 \ + extern const sqlite3_api_routines *sqlite3_api; +#else + /* This case when the file is being statically linked into the + ** application */ +# define SQLITE_EXTENSION_INIT1 /*no-op*/ +# define SQLITE_EXTENSION_INIT2(v) (void)v; /* unused parameter */ +# define SQLITE_EXTENSION_INIT3 /*no-op*/ +#endif + +#endif /* _SQLITE3EXT_H_ */