summaryrefslogtreecommitdiff
path: root/ext/misc
diff options
context:
space:
mode:
Diffstat (limited to 'ext/misc')
-rw-r--r--ext/misc/amatch.c8
-rw-r--r--ext/misc/closure.c16
-rw-r--r--ext/misc/compress.c107
-rw-r--r--ext/misc/fileio.c100
-rw-r--r--ext/misc/fuzzer.c13
-rw-r--r--ext/misc/ieee754.c4
-rw-r--r--ext/misc/nextchar.c88
-rw-r--r--ext/misc/percentile.c219
-rw-r--r--ext/misc/regexp.c6
-rw-r--r--ext/misc/spellfix.c48
-rw-r--r--ext/misc/totype.c512
-rw-r--r--ext/misc/vfslog.c759
-rw-r--r--ext/misc/vtshim.c551
13 files changed, 2388 insertions, 43 deletions
diff --git a/ext/misc/amatch.c b/ext/misc/amatch.c
index b613080..d869dbd 100644
--- a/ext/misc/amatch.c
+++ b/ext/misc/amatch.c
@@ -786,6 +786,7 @@ static void amatchFree(amatch_vtab *p){
sqlite3_free(p->zVocabTab);
sqlite3_free(p->zVocabWord);
sqlite3_free(p->zVocabLang);
+ sqlite3_free(p->zSelf);
memset(p, 0, sizeof(*p));
sqlite3_free(p);
}
@@ -948,6 +949,9 @@ static void amatchClearCursor(amatch_cursor *pCur){
pCur->pAllWords = 0;
sqlite3_free(pCur->zInput);
pCur->zInput = 0;
+ sqlite3_free(pCur->zBuf);
+ pCur->zBuf = 0;
+ pCur->nBuf = 0;
pCur->pCost = 0;
pCur->pWord = 0;
pCur->pCurrent = 0;
@@ -1103,7 +1107,7 @@ static int amatchNext(sqlite3_vtab_cursor *cur){
char *zSql;
if( p->zVocabLang && p->zVocabLang[0] ){
zSql = sqlite3_mprintf(
- "SELECT \"%s\" FROM \"%s\"",
+ "SELECT \"%w\" FROM \"%w\"",
" WHERE \"%w\">=?1 AND \"%w\"=?2"
" ORDER BY 1",
p->zVocabWord, p->zVocabTab,
@@ -1111,7 +1115,7 @@ static int amatchNext(sqlite3_vtab_cursor *cur){
);
}else{
zSql = sqlite3_mprintf(
- "SELECT \"%s\" FROM \"%s\""
+ "SELECT \"%w\" FROM \"%w\""
" WHERE \"%w\">=?1"
" ORDER BY 1",
p->zVocabWord, p->zVocabTab,
diff --git a/ext/misc/closure.c b/ext/misc/closure.c
index 213b763..30c812d 100644
--- a/ext/misc/closure.c
+++ b/ext/misc/closure.c
@@ -496,7 +496,7 @@ static const char *closureValueOfKey(const char *zKey, const char *zStr){
/*
** xConnect/xCreate method for the closure module. Arguments are:
**
-** argv[0] -> module name ("approximate_match")
+** argv[0] -> module name ("transitive_closure")
** argv[1] -> database name
** argv[2] -> table name
** argv[3...] -> arguments
@@ -826,11 +826,17 @@ static int closureBestIndex(
int iPlan = 0;
int i;
int idx = 1;
+ int seenMatch = 0;
const struct sqlite3_index_constraint *pConstraint;
closure_vtab *pVtab = (closure_vtab*)pTab;
+ double rCost = 10000000.0;
pConstraint = pIdxInfo->aConstraint;
for(i=0; i<pIdxInfo->nConstraint; i++, pConstraint++){
+ if( pConstraint->iColumn==CLOSURE_COL_ROOT
+ && pConstraint->op==SQLITE_INDEX_CONSTRAINT_EQ ){
+ seenMatch = 1;
+ }
if( pConstraint->usable==0 ) continue;
if( (iPlan & 1)==0
&& pConstraint->iColumn==CLOSURE_COL_ROOT
@@ -839,6 +845,7 @@ static int closureBestIndex(
iPlan |= 1;
pIdxInfo->aConstraintUsage[i].argvIndex = 1;
pIdxInfo->aConstraintUsage[i].omit = 1;
+ rCost /= 100.0;
}
if( (iPlan & 0x0000f0)==0
&& pConstraint->iColumn==CLOSURE_COL_DEPTH
@@ -849,6 +856,7 @@ static int closureBestIndex(
iPlan |= idx<<4;
pIdxInfo->aConstraintUsage[i].argvIndex = ++idx;
if( pConstraint->op==SQLITE_INDEX_CONSTRAINT_LT ) iPlan |= 0x000002;
+ rCost /= 5.0;
}
if( (iPlan & 0x000f00)==0
&& pConstraint->iColumn==CLOSURE_COL_TABLENAME
@@ -857,6 +865,7 @@ static int closureBestIndex(
iPlan |= idx<<8;
pIdxInfo->aConstraintUsage[i].argvIndex = ++idx;
pIdxInfo->aConstraintUsage[i].omit = 1;
+ rCost /= 5.0;
}
if( (iPlan & 0x00f000)==0
&& pConstraint->iColumn==CLOSURE_COL_IDCOLUMN
@@ -891,13 +900,14 @@ static int closureBestIndex(
){
pIdxInfo->orderByConsumed = 1;
}
- pIdxInfo->estimatedCost = (double)10000;
+ if( seenMatch && (iPlan&1)==0 ) rCost *= 1e30;
+ pIdxInfo->estimatedCost = rCost;
return SQLITE_OK;
}
/*
-** A virtual table module that implements the "approximate_match".
+** A virtual table module that implements the "transitive_closure".
*/
static sqlite3_module closureModule = {
0, /* iVersion */
diff --git a/ext/misc/compress.c b/ext/misc/compress.c
new file mode 100644
index 0000000..a405911
--- /dev/null
+++ b/ext/misc/compress.c
@@ -0,0 +1,107 @@
+/*
+** 2014-06-13
+**
+** 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 SQLite extension implements SQL compression functions
+** compress() and uncompress() using ZLIB.
+*/
+#include "sqlite3ext.h"
+SQLITE_EXTENSION_INIT1
+#include <zlib.h>
+
+/*
+** Implementation of the "compress(X)" SQL function. The input X is
+** compressed using zLib and the output is returned.
+**
+** The output is a BLOB that begins with a variable-length integer that
+** is the input size in bytes (the size of X before compression). The
+** variable-length integer is implemented as 1 to 5 bytes. There are
+** seven bits per integer stored in the lower seven bits of each byte.
+** More significant bits occur first. The most significant bit (0x80)
+** is a flag to indicate the end of the integer.
+*/
+static void compressFunc(
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv
+){
+ const unsigned char *pIn;
+ unsigned char *pOut;
+ unsigned int nIn;
+ unsigned long int nOut;
+ unsigned char x[8];
+ int i, j;
+
+ pIn = sqlite3_value_blob(argv[0]);
+ nIn = sqlite3_value_bytes(argv[0]);
+ nOut = 13 + nIn + (nIn+999)/1000;
+ pOut = sqlite3_malloc( nOut+5 );
+ for(i=4; i>=0; i--){
+ x[i] = (nIn >> (7*(4-i)))&0x7f;
+ }
+ for(i=0; i<4 && x[i]==0; i++){}
+ for(j=0; i<=4; i++, j++) pOut[j] = x[i];
+ pOut[j-1] |= 0x80;
+ compress(&pOut[j], &nOut, pIn, nIn);
+ sqlite3_result_blob(context, pOut, nOut+j, sqlite3_free);
+}
+
+/*
+** Implementation of the "uncompress(X)" SQL function. The argument X
+** is a blob which was obtained from compress(Y). The output will be
+** the value Y.
+*/
+static void uncompressFunc(
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv
+){
+ const unsigned char *pIn;
+ unsigned char *pOut;
+ unsigned int nIn;
+ unsigned long int nOut;
+ int rc;
+ int i;
+
+ pIn = sqlite3_value_blob(argv[0]);
+ nIn = sqlite3_value_bytes(argv[0]);
+ nOut = 0;
+ for(i=0; i<nIn && i<5; i++){
+ nOut = (nOut<<7) | (pIn[i]&0x7f);
+ if( (pIn[i]&0x80)!=0 ){ i++; break; }
+ }
+ pOut = sqlite3_malloc( nOut+1 );
+ rc = uncompress(pOut, &nOut, &pIn[i], nIn-i);
+ if( rc==Z_OK ){
+ sqlite3_result_blob(context, pOut, nOut, sqlite3_free);
+ }
+}
+
+
+#ifdef _WIN32
+__declspec(dllexport)
+#endif
+int sqlite3_compress_init(
+ sqlite3 *db,
+ char **pzErrMsg,
+ const sqlite3_api_routines *pApi
+){
+ int rc = SQLITE_OK;
+ SQLITE_EXTENSION_INIT2(pApi);
+ (void)pzErrMsg; /* Unused parameter */
+ rc = sqlite3_create_function(db, "compress", 1, SQLITE_UTF8, 0,
+ compressFunc, 0, 0);
+ if( rc==SQLITE_OK ){
+ rc = sqlite3_create_function(db, "uncompress", 1, SQLITE_UTF8, 0,
+ uncompressFunc, 0, 0);
+ }
+ return rc;
+}
diff --git a/ext/misc/fileio.c b/ext/misc/fileio.c
new file mode 100644
index 0000000..fbe2d03
--- /dev/null
+++ b/ext/misc/fileio.c
@@ -0,0 +1,100 @@
+/*
+** 2014-06-13
+**
+** 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 SQLite extension implements SQL functions readfile() and
+** writefile().
+*/
+#include "sqlite3ext.h"
+SQLITE_EXTENSION_INIT1
+#include <stdio.h>
+
+/*
+** Implementation of the "readfile(X)" SQL function. The entire content
+** of the file named X is read and returned as a BLOB. NULL is returned
+** if the file does not exist or is unreadable.
+*/
+static void readfileFunc(
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv
+){
+ const char *zName;
+ FILE *in;
+ long nIn;
+ void *pBuf;
+
+ zName = (const char*)sqlite3_value_text(argv[0]);
+ if( zName==0 ) return;
+ in = fopen(zName, "rb");
+ if( in==0 ) return;
+ fseek(in, 0, SEEK_END);
+ nIn = ftell(in);
+ rewind(in);
+ pBuf = sqlite3_malloc( nIn );
+ if( pBuf && 1==fread(pBuf, nIn, 1, in) ){
+ sqlite3_result_blob(context, pBuf, nIn, sqlite3_free);
+ }else{
+ sqlite3_free(pBuf);
+ }
+ fclose(in);
+}
+
+/*
+** Implementation of the "writefile(X,Y)" SQL function. The argument Y
+** is written into file X. The number of bytes written is returned. Or
+** NULL is returned if something goes wrong, such as being unable to open
+** file X for writing.
+*/
+static void writefileFunc(
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv
+){
+ FILE *out;
+ const char *z;
+ sqlite3_int64 rc;
+ const char *zFile;
+
+ zFile = (const char*)sqlite3_value_text(argv[0]);
+ if( zFile==0 ) return;
+ out = fopen(zFile, "wb");
+ if( out==0 ) return;
+ z = (const char*)sqlite3_value_blob(argv[1]);
+ if( z==0 ){
+ rc = 0;
+ }else{
+ rc = fwrite(z, 1, sqlite3_value_bytes(argv[1]), out);
+ }
+ fclose(out);
+ sqlite3_result_int64(context, rc);
+}
+
+
+#ifdef _WIN32
+__declspec(dllexport)
+#endif
+int sqlite3_fileio_init(
+ sqlite3 *db,
+ char **pzErrMsg,
+ const sqlite3_api_routines *pApi
+){
+ int rc = SQLITE_OK;
+ SQLITE_EXTENSION_INIT2(pApi);
+ (void)pzErrMsg; /* Unused parameter */
+ rc = sqlite3_create_function(db, "readfile", 1, SQLITE_UTF8, 0,
+ readfileFunc, 0, 0);
+ if( rc==SQLITE_OK ){
+ rc = sqlite3_create_function(db, "writefile", 2, SQLITE_UTF8, 0,
+ writefileFunc, 0, 0);
+ }
+ return rc;
+}
diff --git a/ext/misc/fuzzer.c b/ext/misc/fuzzer.c
index 642b8f9..fe41cda 100644
--- a/ext/misc/fuzzer.c
+++ b/ext/misc/fuzzer.c
@@ -1077,9 +1077,16 @@ static int fuzzerBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
int iDistTerm = -1;
int iRulesetTerm = -1;
int i;
+ int seenMatch = 0;
const struct sqlite3_index_constraint *pConstraint;
+ double rCost = 1e12;
+
pConstraint = pIdxInfo->aConstraint;
for(i=0; i<pIdxInfo->nConstraint; i++, pConstraint++){
+ if( pConstraint->iColumn==0
+ && pConstraint->op==SQLITE_INDEX_CONSTRAINT_MATCH ){
+ seenMatch = 1;
+ }
if( pConstraint->usable==0 ) continue;
if( (iPlan & 1)==0
&& pConstraint->iColumn==0
@@ -1088,6 +1095,7 @@ static int fuzzerBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
iPlan |= 1;
pIdxInfo->aConstraintUsage[i].argvIndex = 1;
pIdxInfo->aConstraintUsage[i].omit = 1;
+ rCost /= 1e6;
}
if( (iPlan & 2)==0
&& pConstraint->iColumn==1
@@ -1096,6 +1104,7 @@ static int fuzzerBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
){
iPlan |= 2;
iDistTerm = i;
+ rCost /= 10.0;
}
if( (iPlan & 4)==0
&& pConstraint->iColumn==2
@@ -1104,6 +1113,7 @@ static int fuzzerBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
iPlan |= 4;
pIdxInfo->aConstraintUsage[i].omit = 1;
iRulesetTerm = i;
+ rCost /= 10.0;
}
}
if( iPlan & 2 ){
@@ -1122,7 +1132,8 @@ static int fuzzerBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
){
pIdxInfo->orderByConsumed = 1;
}
- pIdxInfo->estimatedCost = (double)10000;
+ if( seenMatch && (iPlan&1)==0 ) rCost = 1e99;
+ pIdxInfo->estimatedCost = rCost;
return SQLITE_OK;
}
diff --git a/ext/misc/ieee754.c b/ext/misc/ieee754.c
index 436b11e..f143893 100644
--- a/ext/misc/ieee754.c
+++ b/ext/misc/ieee754.c
@@ -18,11 +18,11 @@
**
** In the first form, the value X should be a floating-point number.
** The function will return a string of the form 'ieee754(Y,Z)' where
-** Y and Z are integers such that X==Y*pow(w.0,Z).
+** Y and Z are integers such that X==Y*pow(2,Z).
**
** In the second form, Y and Z are integers which are the mantissa and
** base-2 exponent of a new floating point number. The function returns
-** a floating-point value equal to Y*pow(2.0,Z).
+** a floating-point value equal to Y*pow(2,Z).
**
** Examples:
**
diff --git a/ext/misc/nextchar.c b/ext/misc/nextchar.c
index e063043..49dfd24 100644
--- a/ext/misc/nextchar.c
+++ b/ext/misc/nextchar.c
@@ -10,12 +10,22 @@
**
******************************************************************************
**
-** This file contains code to implement the next_char(A,T,F,W) SQL function.
+** This file contains code to implement the next_char(A,T,F,W,C) SQL function.
**
-** The next_char(A,T,F,H) function finds all valid "next" characters for
-** string A given the vocabulary in T.F. The T.F field should be indexed.
-** If the W value exists and is a non-empty string, then it is an SQL
-** expression that limits the entries in T.F that will be considered.
+** The next_char(A,T,F,W,C) function finds all valid "next" characters for
+** string A given the vocabulary in T.F. If the W value exists and is a
+** non-empty string, then it is an SQL expression that limits the entries
+** in T.F that will be considered. If C exists and is a non-empty string,
+** then it is the name of the collating sequence to use for comparison. If
+**
+** Only the first three arguments are required. If the C parameter is
+** omitted or is NULL or is an empty string, then the default collating
+** sequence of T.F is used for comparision. If the W parameter is omitted
+** or is NULL or is an empty string, then no filtering of the output is
+** done.
+**
+** The T.F column should be indexed using collation C or else this routine
+** will be quite slow.
**
** For example, suppose an application has a dictionary like this:
**
@@ -28,6 +38,19 @@
** out) run the following query:
**
** SELECT next_char('cha','dictionary','word');
+**
+** IMPLEMENTATION NOTES:
+**
+** The next_char function is implemented using recursive SQL that makes
+** use of the table name and column name as part of a query. If either
+** the table name or column name are keywords or contain special characters,
+** then they should be escaped. For example:
+**
+** SELECT next_char('cha','[dictionary]','[word]');
+**
+** This also means that the table name can be a subquery:
+**
+** SELECT next_char('cha','(SELECT word AS w FROM dictionary)','w');
*/
#include "sqlite3ext.h"
SQLITE_EXTENSION_INIT1
@@ -184,6 +207,9 @@ static void nextCharFunc(
const unsigned char *zTable = sqlite3_value_text(argv[1]);
const unsigned char *zField = sqlite3_value_text(argv[2]);
const unsigned char *zWhere;
+ const unsigned char *zCollName;
+ char *zWhereClause = 0;
+ char *zColl = 0;
char *zSql;
int rc;
@@ -192,25 +218,41 @@ static void nextCharFunc(
c.zPrefix = sqlite3_value_text(argv[0]);
c.nPrefix = sqlite3_value_bytes(argv[0]);
if( zTable==0 || zField==0 || c.zPrefix==0 ) return;
- if( argc<4
- || (zWhere = sqlite3_value_text(argv[3]))==0
- || zWhere[0]==0
+ if( argc>=4
+ && (zWhere = sqlite3_value_text(argv[3]))!=0
+ && zWhere[0]!=0
+ ){
+ zWhereClause = sqlite3_mprintf("AND (%s)", zWhere);
+ if( zWhereClause==0 ){
+ sqlite3_result_error_nomem(context);
+ return;
+ }
+ }else{
+ zWhereClause = "";
+ }
+ if( argc>=5
+ && (zCollName = sqlite3_value_text(argv[4]))!=0
+ && zCollName[0]!=0
){
- zSql = sqlite3_mprintf(
- "SELECT \"%w\" FROM \"%w\""
- " WHERE \"%w\">=(?1 || ?2)"
- " AND \"%w\"<=(?1 || char(1114111))" /* 1114111 == 0x10ffff */
- " ORDER BY 1 ASC LIMIT 1",
- zField, zTable, zField, zField);
+ zColl = sqlite3_mprintf("collate \"%w\"", zCollName);
+ if( zColl==0 ){
+ sqlite3_result_error_nomem(context);
+ if( zWhereClause[0] ) sqlite3_free(zWhereClause);
+ return;
+ }
}else{
- zSql = sqlite3_mprintf(
- "SELECT \"%w\" FROM \"%w\""
- " WHERE \"%w\">=(?1 || ?2)"
- " AND \"%w\"<=(?1 || char(1114111))" /* 1114111 == 0x10ffff */
- " AND (%s)"
- " ORDER BY 1 ASC LIMIT 1",
- zField, zTable, zField, zField, zWhere);
+ zColl = "";
}
+ zSql = sqlite3_mprintf(
+ "SELECT %s FROM %s"
+ " WHERE %s>=(?1 || ?2) %s"
+ " AND %s<=(?1 || char(1114111)) %s" /* 1114111 == 0x10ffff */
+ " %s"
+ " ORDER BY 1 %s ASC LIMIT 1",
+ zField, zTable, zField, zColl, zField, zColl, zWhereClause, zColl
+ );
+ if( zWhereClause[0] ) sqlite3_free(zWhereClause);
+ if( zColl[0] ) sqlite3_free(zColl);
if( zSql==0 ){
sqlite3_result_error_nomem(context);
return;
@@ -261,5 +303,9 @@ int sqlite3_nextchar_init(
rc = sqlite3_create_function(db, "next_char", 4, SQLITE_UTF8, 0,
nextCharFunc, 0, 0);
}
+ if( rc==SQLITE_OK ){
+ rc = sqlite3_create_function(db, "next_char", 5, SQLITE_UTF8, 0,
+ nextCharFunc, 0, 0);
+ }
return rc;
}
diff --git a/ext/misc/percentile.c b/ext/misc/percentile.c
new file mode 100644
index 0000000..74a4c9d
--- /dev/null
+++ b/ext/misc/percentile.c
@@ -0,0 +1,219 @@
+/*
+** 2013-05-28
+**
+** 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 file contains code to implement the percentile(Y,P) SQL function
+** as described below:
+**
+** (1) The percentile(Y,P) function is an aggregate function taking
+** exactly two arguments.
+**
+** (2) If the P argument to percentile(Y,P) is not the same for every
+** row in the aggregate then an error is thrown. The word "same"
+** in the previous sentence means that the value differ by less
+** than 0.001.
+**
+** (3) If the P argument to percentile(Y,P) evaluates to anything other
+** than a number in the range of 0.0 to 100.0 inclusive then an
+** error is thrown.
+**
+** (4) If any Y argument to percentile(Y,P) evaluates to a value that
+** is not NULL and is not numeric then an error is thrown.
+**
+** (5) If any Y argument to percentile(Y,P) evaluates to plus or minus
+** infinity then an error is thrown. (SQLite always interprets NaN
+** values as NULL.)
+**
+** (6) Both Y and P in percentile(Y,P) can be arbitrary expressions,
+** including CASE WHEN expressions.
+**
+** (7) The percentile(Y,P) aggregate is able to handle inputs of at least
+** one million (1,000,000) rows.
+**
+** (8) If there are no non-NULL values for Y, then percentile(Y,P)
+** returns NULL.
+**
+** (9) If there is exactly one non-NULL value for Y, the percentile(Y,P)
+** returns the one Y value.
+**
+** (10) If there N non-NULL values of Y where N is two or more and
+** the Y values are ordered from least to greatest and a graph is
+** drawn from 0 to N-1 such that the height of the graph at J is
+** the J-th Y value and such that straight lines are drawn between
+** adjacent Y values, then the percentile(Y,P) function returns
+** the height of the graph at P*(N-1)/100.
+**
+** (11) The percentile(Y,P) function always returns either a floating
+** point number or NULL.
+**
+** (12) The percentile(Y,P) is implemented as a single C99 source-code
+** file that compiles into a shared-library or DLL that can be loaded
+** into SQLite using the sqlite3_load_extension() interface.
+*/
+#include "sqlite3ext.h"
+SQLITE_EXTENSION_INIT1
+#include <assert.h>
+#include <string.h>
+#include <stdlib.h>
+
+/* The following object is the session context for a single percentile()
+** function. We have to remember all input Y values until the very end.
+** Those values are accumulated in the Percentile.a[] array.
+*/
+typedef struct Percentile Percentile;
+struct Percentile {
+ unsigned nAlloc; /* Number of slots allocated for a[] */
+ unsigned nUsed; /* Number of slots actually used in a[] */
+ double rPct; /* 1.0 more than the value for P */
+ double *a; /* Array of Y values */
+};
+
+/*
+** Return TRUE if the input floating-point number is an infinity.
+*/
+static int isInfinity(double r){
+ sqlite3_uint64 u;
+ assert( sizeof(u)==sizeof(r) );
+ memcpy(&u, &r, sizeof(u));
+ return ((u>>52)&0x7ff)==0x7ff;
+}
+
+/*
+** Return TRUE if two doubles differ by 0.001 or less
+*/
+static int sameValue(double a, double b){
+ a -= b;
+ return a>=-0.001 && a<=0.001;
+}
+
+/*
+** The "step" function for percentile(Y,P) is called once for each
+** input row.
+*/
+static void percentStep(sqlite3_context *pCtx, int argc, sqlite3_value **argv){
+ Percentile *p;
+ double rPct;
+ int eType;
+ double y;
+ assert( argc==2 );
+
+ /* Requirement 3: P must be a number between 0 and 100 */
+ eType = sqlite3_value_numeric_type(argv[1]);
+ rPct = sqlite3_value_double(argv[1]);
+ if( (eType!=SQLITE_INTEGER && eType!=SQLITE_FLOAT) ||
+ ((rPct = sqlite3_value_double(argv[1]))<0.0 || rPct>100.0) ){
+ sqlite3_result_error(pCtx, "2nd argument to percentile() is not "
+ "a number between 0.0 and 100.0", -1);
+ return;
+ }
+
+ /* Allocate the session context. */
+ p = (Percentile*)sqlite3_aggregate_context(pCtx, sizeof(*p));
+ if( p==0 ) return;
+
+ /* Remember the P value. Throw an error if the P value is different
+ ** from any prior row, per Requirement (2). */
+ if( p->rPct==0.0 ){
+ p->rPct = rPct+1.0;
+ }else if( !sameValue(p->rPct,rPct+1.0) ){
+ sqlite3_result_error(pCtx, "2nd argument to percentile() is not the "
+ "same for all input rows", -1);
+ return;
+ }
+
+ /* Ignore rows for which Y is NULL */
+ eType = sqlite3_value_type(argv[0]);
+ if( eType==SQLITE_NULL ) return;
+
+ /* If not NULL, then Y must be numeric. Otherwise throw an error.
+ ** Requirement 4 */
+ if( eType!=SQLITE_INTEGER && eType!=SQLITE_FLOAT ){
+ sqlite3_result_error(pCtx, "1st argument to percentile() is not "
+ "numeric", -1);
+ return;
+ }
+
+ /* Throw an error if the Y value is infinity or NaN */
+ y = sqlite3_value_double(argv[0]);
+ if( isInfinity(y) ){
+ sqlite3_result_error(pCtx, "Inf input to percentile()", -1);
+ return;
+ }
+
+ /* Allocate and store the Y */
+ if( p->nUsed>=p->nAlloc ){
+ unsigned n = p->nAlloc*2 + 250;
+ double *a = sqlite3_realloc(p->a, sizeof(double)*n);
+ if( a==0 ){
+ sqlite3_free(p->a);
+ memset(p, 0, sizeof(*p));
+ sqlite3_result_error_nomem(pCtx);
+ return;
+ }
+ p->nAlloc = n;
+ p->a = a;
+ }
+ p->a[p->nUsed++] = y;
+}
+
+/*
+** Compare to doubles for sorting using qsort()
+*/
+static int doubleCmp(const void *pA, const void *pB){
+ double a = *(double*)pA;
+ double b = *(double*)pB;
+ if( a==b ) return 0;
+ if( a<b ) return -1;
+ return +1;
+}
+
+/*
+** Called to compute the final output of percentile() and to clean
+** up all allocated memory.
+*/
+static void percentFinal(sqlite3_context *pCtx){
+ Percentile *p;
+ unsigned i1, i2;
+ double v1, v2;
+ double ix, vx;
+ p = (Percentile*)sqlite3_aggregate_context(pCtx, 0);
+ if( p==0 ) return;
+ if( p->a==0 ) return;
+ if( p->nUsed ){
+ qsort(p->a, p->nUsed, sizeof(double), doubleCmp);
+ ix = (p->rPct-1.0)*(p->nUsed-1)*0.01;
+ i1 = (unsigned)ix;
+ i2 = ix==(double)i1 || i1==p->nUsed-1 ? i1 : i1+1;
+ v1 = p->a[i1];
+ v2 = p->a[i2];
+ vx = v1 + (v2-v1)*(ix-i1);
+ sqlite3_result_double(pCtx, vx);
+ }
+ sqlite3_free(p->a);
+ memset(p, 0, sizeof(*p));
+}
+
+
+#ifdef _WIN32
+__declspec(dllexport)
+#endif
+int sqlite3_percentile_init(
+ sqlite3 *db,
+ char **pzErrMsg,
+ const sqlite3_api_routines *pApi
+){
+ int rc = SQLITE_OK;
+ SQLITE_EXTENSION_INIT2(pApi);
+ (void)pzErrMsg; /* Unused parameter */
+ rc = sqlite3_create_function(db, "percentile", 2, SQLITE_UTF8, 0,
+ 0, percentStep, percentFinal);
+ return rc;
+}
diff --git a/ext/misc/regexp.c b/ext/misc/regexp.c
index 16fa7d0..7244d52 100644
--- a/ext/misc/regexp.c
+++ b/ext/misc/regexp.c
@@ -713,6 +713,7 @@ static void re_sql_func(
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 ){
@@ -728,12 +729,15 @@ static void re_sql_func(
sqlite3_result_error_nomem(context);
return;
}
- sqlite3_set_auxdata(context, 0, pRe, (void(*)(void*))re_free);
+ setAux = 1;
}
zStr = (const unsigned char*)sqlite3_value_text(argv[1]);
if( zStr!=0 ){
sqlite3_result_int(context, re_match(pRe, zStr, -1));
}
+ if( setAux ){
+ sqlite3_set_auxdata(context, 0, pRe, (void(*)(void*))re_free);
+ }
}
/*
diff --git a/ext/misc/spellfix.c b/ext/misc/spellfix.c
index eb5442e..2e6743e 100644
--- a/ext/misc/spellfix.c
+++ b/ext/misc/spellfix.c
@@ -26,8 +26,8 @@ SQLITE_EXTENSION_INIT1
# define NEVER(X) 0
typedef unsigned char u8;
typedef unsigned short u16;
-# include <ctype.h>
#endif
+#include <ctype.h>
#ifndef SQLITE_OMIT_VIRTUALTABLE
@@ -1893,7 +1893,7 @@ static int spellfix1Init(
char **pzErr
){
spellfix1_vtab *pNew = 0;
- const char *zModule = argv[0];
+ /* const char *zModule = argv[0]; // not used */
const char *zDbName = argv[1];
const char *zTableName = argv[2];
int nDbName;
@@ -1933,7 +1933,6 @@ static int spellfix1Init(
#define SPELLFIX_COL_COMMAND 11
}
if( rc==SQLITE_OK && isCreate ){
- sqlite3_uint64 r;
spellfix1DbExec(&rc, db,
"CREATE TABLE IF NOT EXISTS \"%w\".\"%w_vocab\"(\n"
" id INTEGER PRIMARY KEY,\n"
@@ -1945,11 +1944,10 @@ static int spellfix1Init(
");\n",
zDbName, zTableName
);
- sqlite3_randomness(sizeof(r), &r);
spellfix1DbExec(&rc, db,
- "CREATE INDEX IF NOT EXISTS \"%w\".\"%w_index_%llx\" "
+ "CREATE INDEX IF NOT EXISTS \"%w\".\"%w_vocab_index_langid_k2\" "
"ON \"%w_vocab\"(langid,k2);",
- zDbName, zModule, r, zTableName
+ zDbName, zTableName, zTableName
);
}
for(i=3; rc==SQLITE_OK && i<argc; i++){
@@ -2051,6 +2049,7 @@ static int spellfix1Close(sqlite3_vtab_cursor *cur){
** (D) scope = $scope
** (E) distance < $distance
** (F) distance <= $distance
+** (G) rowid = $rowid
**
** The plan number is a bit mask formed with these bits:
**
@@ -2060,8 +2059,9 @@ static int spellfix1Close(sqlite3_vtab_cursor *cur){
** 0x08 (D) is found
** 0x10 (E) is found
** 0x20 (F) is found
+** 0x40 (G) is found
**
-** filter.argv[*] values contains $str, $langid, $top, and $scope,
+** filter.argv[*] values contains $str, $langid, $top, $scope and $rowid
** if specified and in that order.
*/
static int spellfix1BestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
@@ -2070,6 +2070,7 @@ static int spellfix1BestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
int iTopTerm = -1;
int iScopeTerm = -1;
int iDistTerm = -1;
+ int iRowidTerm = -1;
int i;
const struct sqlite3_index_constraint *pConstraint;
pConstraint = pIdxInfo->aConstraint;
@@ -2122,6 +2123,15 @@ static int spellfix1BestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
iPlan |= pConstraint->op==SQLITE_INDEX_CONSTRAINT_LT ? 16 : 32;
iDistTerm = i;
}
+
+ /* Terms of the form: distance < $dist or distance <= $dist */
+ if( (iPlan & 64)==0
+ && pConstraint->iColumn<0
+ && pConstraint->op==SQLITE_INDEX_CONSTRAINT_EQ
+ ){
+ iPlan |= 64;
+ iRowidTerm = i;
+ }
}
if( iPlan&1 ){
int idx = 2;
@@ -2148,10 +2158,15 @@ static int spellfix1BestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
pIdxInfo->aConstraintUsage[iDistTerm].argvIndex = idx++;
pIdxInfo->aConstraintUsage[iDistTerm].omit = 1;
}
- pIdxInfo->estimatedCost = (double)10000;
+ pIdxInfo->estimatedCost = 1e5;
+ }else if( (iPlan & 64) ){
+ pIdxInfo->idxNum = 64;
+ pIdxInfo->aConstraintUsage[iRowidTerm].argvIndex = 1;
+ pIdxInfo->aConstraintUsage[iRowidTerm].omit = 1;
+ pIdxInfo->estimatedCost = 5;
}else{
pIdxInfo->idxNum = 0;
- pIdxInfo->estimatedCost = (double)10000000;
+ pIdxInfo->estimatedCost = 1e50;
}
return SQLITE_OK;
}
@@ -2465,16 +2480,23 @@ static int spellfix1FilterForFullScan(
int argc,
sqlite3_value **argv
){
- int rc;
+ int rc = SQLITE_OK;
char *zSql;
spellfix1_vtab *pVTab = pCur->pVTab;
spellfix1ResetCursor(pCur);
+ assert( idxNum==0 || idxNum==64 );
zSql = sqlite3_mprintf(
- "SELECT word, rank, NULL, langid, id FROM \"%w\".\"%w_vocab\"",
- pVTab->zDbName, pVTab->zTableName);
+ "SELECT word, rank, NULL, langid, id FROM \"%w\".\"%w_vocab\"%s",
+ pVTab->zDbName, pVTab->zTableName,
+ ((idxNum & 64) ? " WHERE rowid=?" : "")
+ );
if( zSql==0 ) return SQLITE_NOMEM;
rc = sqlite3_prepare_v2(pVTab->db, zSql, -1, &pCur->pFullScan, 0);
sqlite3_free(zSql);
+ if( rc==SQLITE_OK && (idxNum & 64) ){
+ assert( argc==1 );
+ rc = sqlite3_bind_value(pCur->pFullScan, 1, argv[0]);
+ }
pCur->nRow = pCur->iRow = 0;
if( rc==SQLITE_OK ){
rc = sqlite3_step(pCur->pFullScan);
@@ -2672,7 +2694,7 @@ static int spellfix1Update(
const char *zCmd =
(const char*)sqlite3_value_text(argv[SPELLFIX_COL_COMMAND+2]);
if( zCmd==0 ){
- pVTab->zErrMsg = sqlite3_mprintf("%s.word may not be NULL",
+ pVTab->zErrMsg = sqlite3_mprintf("NOT NULL constraint failed: %s.word",
p->zTableName);
return SQLITE_CONSTRAINT_NOTNULL;
}
diff --git a/ext/misc/totype.c b/ext/misc/totype.c
new file mode 100644
index 0000000..5dc99f3
--- /dev/null
+++ b/ext/misc/totype.c
@@ -0,0 +1,512 @@
+/*
+** 2013-10-14
+**
+** 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 SQLite extension implements functions tointeger(X) and toreal(X).
+**
+** If X is an integer, real, or string value that can be
+** losslessly represented as an integer, then tointeger(X)
+** returns the corresponding integer value.
+** If X is an 8-byte BLOB then that blob is interpreted as
+** a signed two-compliment little-endian encoding of an integer
+** and tointeger(X) returns the corresponding integer value.
+** Otherwise tointeger(X) return NULL.
+**
+** If X is an integer, real, or string value that can be
+** convert into a real number, preserving at least 15 digits
+** of precision, then toreal(X) returns the corresponding real value.
+** If X is an 8-byte BLOB then that blob is interpreted as
+** a 64-bit IEEE754 big-endian floating point value
+** and toreal(X) returns the corresponding real value.
+** Otherwise toreal(X) return NULL.
+**
+** Note that tointeger(X) of an 8-byte BLOB assumes a little-endian
+** encoding whereas toreal(X) of an 8-byte BLOB assumes a big-endian
+** encoding.
+*/
+#include "sqlite3ext.h"
+SQLITE_EXTENSION_INIT1
+#include <assert.h>
+#include <string.h>
+
+/*
+** Determine if this is running on a big-endian or little-endian
+** processor
+*/
+#if defined(i386) || defined(__i386__) || defined(_M_IX86)\
+ || defined(__x86_64) || defined(__x86_64__)
+# define TOTYPE_BIGENDIAN 0
+# define TOTYPE_LITTLEENDIAN 1
+#else
+ const int totype_one = 1;
+# define TOTYPE_BIGENDIAN (*(char *)(&totype_one)==0)
+# define TOTYPE_LITTLEENDIAN (*(char *)(&totype_one)==1)
+#endif
+
+/*
+** Constants for the largest and smallest possible 64-bit signed integers.
+** These macros are designed to work correctly on both 32-bit and 64-bit
+** compilers.
+*/
+#ifndef LARGEST_INT64
+# define LARGEST_INT64 (0xffffffff|(((sqlite3_int64)0x7fffffff)<<32))
+#endif
+
+#ifndef SMALLEST_INT64
+# define SMALLEST_INT64 (((sqlite3_int64)-1) - LARGEST_INT64)
+#endif
+
+/*
+** Return TRUE if character c is a whitespace character
+*/
+static int totypeIsspace(unsigned char c){
+ return c==' ' || c=='\t' || c=='\n' || c=='\v' || c=='\f' || c=='\r';
+}
+
+/*
+** Return TRUE if character c is a digit
+*/
+static int totypeIsdigit(unsigned char c){
+ return c>='0' && c<='9';
+}
+
+/*
+** Compare the 19-character string zNum against the text representation
+** value 2^63: 9223372036854775808. Return negative, zero, or positive
+** if zNum is less than, equal to, or greater than the string.
+** Note that zNum must contain exactly 19 characters.
+**
+** Unlike memcmp() this routine is guaranteed to return the difference
+** in the values of the last digit if the only difference is in the
+** last digit. So, for example,
+**
+** totypeCompare2pow63("9223372036854775800")
+**
+** will return -8.
+*/
+static int totypeCompare2pow63(const char *zNum){
+ int c = 0;
+ int i;
+ /* 012345678901234567 */
+ const char *pow63 = "922337203685477580";
+ for(i=0; c==0 && i<18; i++){
+ c = (zNum[i]-pow63[i])*10;
+ }
+ if( c==0 ){
+ c = zNum[18] - '8';
+ }
+ return c;
+}
+
+/*
+** Convert zNum to a 64-bit signed integer.
+**
+** If the zNum value is representable as a 64-bit twos-complement
+** integer, then write that value into *pNum and return 0.
+**
+** If zNum is exactly 9223372036854665808, return 2. This special
+** case is broken out because while 9223372036854665808 cannot be a
+** signed 64-bit integer, its negative -9223372036854665808 can be.
+**
+** If zNum is too big for a 64-bit integer and is not
+** 9223372036854665808 or if zNum contains any non-numeric text,
+** then return 1.
+**
+** The string is not necessarily zero-terminated.
+*/
+static int totypeAtoi64(const char *zNum, sqlite3_int64 *pNum, int length){
+ sqlite3_uint64 u = 0;
+ int neg = 0; /* assume positive */
+ int i;
+ int c = 0;
+ int nonNum = 0;
+ const char *zStart;
+ const char *zEnd = zNum + length;
+
+ while( zNum<zEnd && totypeIsspace(*zNum) ) zNum++;
+ if( zNum<zEnd ){
+ if( *zNum=='-' ){
+ neg = 1;
+ zNum++;
+ }else if( *zNum=='+' ){
+ zNum++;
+ }
+ }
+ zStart = zNum;
+ while( zNum<zEnd && zNum[0]=='0' ){ zNum++; } /* Skip leading zeros. */
+ for(i=0; &zNum[i]<zEnd && (c=zNum[i])>='0' && c<='9'; i++){
+ u = u*10 + c - '0';
+ }
+ if( u>LARGEST_INT64 ){
+ *pNum = SMALLEST_INT64;
+ }else if( neg ){
+ *pNum = -(sqlite3_int64)u;
+ }else{
+ *pNum = (sqlite3_int64)u;
+ }
+ if( (c!=0 && &zNum[i]<zEnd) || (i==0 && zStart==zNum) || i>19 || nonNum ){
+ /* zNum is empty or contains non-numeric text or is longer
+ ** than 19 digits (thus guaranteeing that it is too large) */
+ return 1;
+ }else if( i<19 ){
+ /* Less than 19 digits, so we know that it fits in 64 bits */
+ assert( u<=LARGEST_INT64 );
+ return 0;
+ }else{
+ /* zNum is a 19-digit numbers. Compare it against 9223372036854775808. */
+ c = totypeCompare2pow63(zNum);
+ if( c<0 ){
+ /* zNum is less than 9223372036854775808 so it fits */
+ assert( u<=LARGEST_INT64 );
+ return 0;
+ }else if( c>0 ){
+ /* zNum is greater than 9223372036854775808 so it overflows */
+ return 1;
+ }else{
+ /* zNum is exactly 9223372036854775808. Fits if negative. The
+ ** special case 2 overflow if positive */
+ assert( u-1==LARGEST_INT64 );
+ assert( (*pNum)==SMALLEST_INT64 );
+ return neg ? 0 : 2;
+ }
+ }
+}
+
+/*
+** The string z[] is an text representation of a real number.
+** Convert this string to a double and write it into *pResult.
+**
+** The string is not necessarily zero-terminated.
+**
+** Return TRUE if the result is a valid real number (or integer) and FALSE
+** if the string is empty or contains extraneous text. Valid numbers
+** are in one of these formats:
+**
+** [+-]digits[E[+-]digits]
+** [+-]digits.[digits][E[+-]digits]
+** [+-].digits[E[+-]digits]
+**
+** Leading and trailing whitespace is ignored for the purpose of determining
+** validity.
+**
+** If some prefix of the input string is a valid number, this routine
+** returns FALSE but it still converts the prefix and writes the result
+** into *pResult.
+*/
+static int totypeAtoF(const char *z, double *pResult, int length){
+ const char *zEnd = z + length;
+ /* sign * significand * (10 ^ (esign * exponent)) */
+ int sign = 1; /* sign of significand */
+ sqlite3_int64 s = 0; /* significand */
+ int d = 0; /* adjust exponent for shifting decimal point */
+ int esign = 1; /* sign of exponent */
+ int e = 0; /* exponent */
+ int eValid = 1; /* True exponent is either not used or is well-formed */
+ double result;
+ int nDigits = 0;
+ int nonNum = 0;
+
+ *pResult = 0.0; /* Default return value, in case of an error */
+
+ /* skip leading spaces */
+ while( z<zEnd && totypeIsspace(*z) ) z++;
+ if( z>=zEnd ) return 0;
+
+ /* get sign of significand */
+ if( *z=='-' ){
+ sign = -1;
+ z++;
+ }else if( *z=='+' ){
+ z++;
+ }
+
+ /* skip leading zeroes */
+ while( z<zEnd && z[0]=='0' ) z++, nDigits++;
+
+ /* copy max significant digits to significand */
+ while( z<zEnd && totypeIsdigit(*z) && s<((LARGEST_INT64-9)/10) ){
+ s = s*10 + (*z - '0');
+ z++, nDigits++;
+ }
+
+ /* skip non-significant significand digits
+ ** (increase exponent by d to shift decimal left) */
+ while( z<zEnd && totypeIsdigit(*z) ) z++, nDigits++, d++;
+ if( z>=zEnd ) goto totype_atof_calc;
+
+ /* if decimal point is present */
+ if( *z=='.' ){
+ z++;
+ /* copy digits from after decimal to significand
+ ** (decrease exponent by d to shift decimal right) */
+ while( z<zEnd && totypeIsdigit(*z) && s<((LARGEST_INT64-9)/10) ){
+ s = s*10 + (*z - '0');
+ z++, nDigits++, d--;
+ }
+ /* skip non-significant digits */
+ while( z<zEnd && totypeIsdigit(*z) ) z++, nDigits++;
+ }
+ if( z>=zEnd ) goto totype_atof_calc;
+
+ /* if exponent is present */
+ if( *z=='e' || *z=='E' ){
+ z++;
+ eValid = 0;
+ if( z>=zEnd ) goto totype_atof_calc;
+ /* get sign of exponent */
+ if( *z=='-' ){
+ esign = -1;
+ z++;
+ }else if( *z=='+' ){
+ z++;
+ }
+ /* copy digits to exponent */
+ while( z<zEnd && totypeIsdigit(*z) ){
+ e = e<10000 ? (e*10 + (*z - '0')) : 10000;
+ z++;
+ eValid = 1;
+ }
+ }
+
+ /* skip trailing spaces */
+ if( nDigits && eValid ){
+ while( z<zEnd && totypeIsspace(*z) ) z++;
+ }
+
+totype_atof_calc:
+ /* adjust exponent by d, and update sign */
+ e = (e*esign) + d;
+ if( e<0 ) {
+ esign = -1;
+ e *= -1;
+ } else {
+ esign = 1;
+ }
+
+ /* if 0 significand */
+ if( !s ) {
+ /* In the IEEE 754 standard, zero is signed.
+ ** Add the sign if we've seen at least one digit */
+ result = (sign<0 && nDigits) ? -(double)0 : (double)0;
+ } else {
+ /* attempt to reduce exponent */
+ if( esign>0 ){
+ while( s<(LARGEST_INT64/10) && e>0 ) e--,s*=10;
+ }else{
+ while( !(s%10) && e>0 ) e--,s/=10;
+ }
+
+ /* adjust the sign of significand */
+ s = sign<0 ? -s : s;
+
+ /* if exponent, scale significand as appropriate
+ ** and store in result. */
+ if( e ){
+ double scale = 1.0;
+ /* attempt to handle extremely small/large numbers better */
+ if( e>307 && e<342 ){
+ while( e%308 ) { scale *= 1.0e+1; e -= 1; }
+ if( esign<0 ){
+ result = s / scale;
+ result /= 1.0e+308;
+ }else{
+ result = s * scale;
+ result *= 1.0e+308;
+ }
+ }else if( e>=342 ){
+ if( esign<0 ){
+ result = 0.0*s;
+ }else{
+ result = 1e308*1e308*s; /* Infinity */
+ }
+ }else{
+ /* 1.0e+22 is the largest power of 10 than can be
+ ** represented exactly. */
+ while( e%22 ) { scale *= 1.0e+1; e -= 1; }
+ while( e>0 ) { scale *= 1.0e+22; e -= 22; }
+ if( esign<0 ){
+ result = s / scale;
+ }else{
+ result = s * scale;
+ }
+ }
+ } else {
+ result = (double)s;
+ }
+ }
+
+ /* store the result */
+ *pResult = result;
+
+ /* return true if number and no extra non-whitespace chracters after */
+ return z>=zEnd && nDigits>0 && eValid && nonNum==0;
+}
+
+/*
+** tointeger(X): If X is any value (integer, double, blob, or string) that
+** can be losslessly converted into an integer, then make the conversion and
+** return the result. Otherwise, return NULL.
+*/
+static void tointegerFunc(
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv
+){
+ assert( argc==1 );
+ (void)argc;
+ switch( sqlite3_value_type(argv[0]) ){
+ case SQLITE_FLOAT: {
+ double rVal = sqlite3_value_double(argv[0]);
+ sqlite3_int64 iVal = (sqlite3_int64)rVal;
+ if( rVal==(double)iVal ){
+ sqlite3_result_int64(context, iVal);
+ }
+ break;
+ }
+ case SQLITE_INTEGER: {
+ sqlite3_result_int64(context, sqlite3_value_int64(argv[0]));
+ break;
+ }
+ case SQLITE_BLOB: {
+ const unsigned char *zBlob = sqlite3_value_blob(argv[0]);
+ if( zBlob ){
+ int nBlob = sqlite3_value_bytes(argv[0]);
+ if( nBlob==sizeof(sqlite3_int64) ){
+ sqlite3_int64 iVal;
+ if( TOTYPE_BIGENDIAN ){
+ int i;
+ unsigned char zBlobRev[sizeof(sqlite3_int64)];
+ for(i=0; i<sizeof(sqlite3_int64); i++){
+ zBlobRev[i] = zBlob[sizeof(sqlite3_int64)-1-i];
+ }
+ memcpy(&iVal, zBlobRev, sizeof(sqlite3_int64));
+ }else{
+ memcpy(&iVal, zBlob, sizeof(sqlite3_int64));
+ }
+ sqlite3_result_int64(context, iVal);
+ }
+ }
+ break;
+ }
+ case SQLITE_TEXT: {
+ const unsigned char *zStr = sqlite3_value_text(argv[0]);
+ if( zStr ){
+ int nStr = sqlite3_value_bytes(argv[0]);
+ if( nStr && !totypeIsspace(zStr[0]) ){
+ sqlite3_int64 iVal;
+ if( !totypeAtoi64((const char*)zStr, &iVal, nStr) ){
+ sqlite3_result_int64(context, iVal);
+ }
+ }
+ }
+ break;
+ }
+ default: {
+ assert( sqlite3_value_type(argv[0])==SQLITE_NULL );
+ break;
+ }
+ }
+}
+
+/*
+** toreal(X): If X is any value (integer, double, blob, or string) that can
+** be losslessly converted into a real number, then do so and return that
+** real number. Otherwise return NULL.
+*/
+#if defined(_MSC_VER)
+#pragma warning(disable: 4748)
+#pragma optimize("", off)
+#endif
+static void torealFunc(
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv
+){
+ assert( argc==1 );
+ (void)argc;
+ switch( sqlite3_value_type(argv[0]) ){
+ case SQLITE_FLOAT: {
+ sqlite3_result_double(context, sqlite3_value_double(argv[0]));
+ break;
+ }
+ case SQLITE_INTEGER: {
+ sqlite3_int64 iVal = sqlite3_value_int64(argv[0]);
+ double rVal = (double)iVal;
+ if( iVal==(sqlite3_int64)rVal ){
+ sqlite3_result_double(context, rVal);
+ }
+ break;
+ }
+ case SQLITE_BLOB: {
+ const unsigned char *zBlob = sqlite3_value_blob(argv[0]);
+ if( zBlob ){
+ int nBlob = sqlite3_value_bytes(argv[0]);
+ if( nBlob==sizeof(double) ){
+ double rVal;
+ if( TOTYPE_LITTLEENDIAN ){
+ int i;
+ unsigned char zBlobRev[sizeof(double)];
+ for(i=0; i<sizeof(double); i++){
+ zBlobRev[i] = zBlob[sizeof(double)-1-i];
+ }
+ memcpy(&rVal, zBlobRev, sizeof(double));
+ }else{
+ memcpy(&rVal, zBlob, sizeof(double));
+ }
+ sqlite3_result_double(context, rVal);
+ }
+ }
+ break;
+ }
+ case SQLITE_TEXT: {
+ const unsigned char *zStr = sqlite3_value_text(argv[0]);
+ if( zStr ){
+ int nStr = sqlite3_value_bytes(argv[0]);
+ if( nStr && !totypeIsspace(zStr[0]) && !totypeIsspace(zStr[nStr-1]) ){
+ double rVal;
+ if( totypeAtoF((const char*)zStr, &rVal, nStr) ){
+ sqlite3_result_double(context, rVal);
+ return;
+ }
+ }
+ }
+ break;
+ }
+ default: {
+ assert( sqlite3_value_type(argv[0])==SQLITE_NULL );
+ break;
+ }
+ }
+}
+#if defined(_MSC_VER)
+#pragma optimize("", on)
+#pragma warning(default: 4748)
+#endif
+
+#ifdef _WIN32
+__declspec(dllexport)
+#endif
+int sqlite3_totype_init(
+ sqlite3 *db,
+ char **pzErrMsg,
+ const sqlite3_api_routines *pApi
+){
+ int rc = SQLITE_OK;
+ SQLITE_EXTENSION_INIT2(pApi);
+ (void)pzErrMsg; /* Unused parameter */
+ rc = sqlite3_create_function(db, "tointeger", 1, SQLITE_UTF8, 0,
+ tointegerFunc, 0, 0);
+ if( rc==SQLITE_OK ){
+ rc = sqlite3_create_function(db, "toreal", 1, SQLITE_UTF8, 0,
+ torealFunc, 0, 0);
+ }
+ return rc;
+}
diff --git a/ext/misc/vfslog.c b/ext/misc/vfslog.c
new file mode 100644
index 0000000..b55b06f
--- /dev/null
+++ b/ext/misc/vfslog.c
@@ -0,0 +1,759 @@
+/*
+** 2013-10-09
+**
+** 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 file contains the implementation of an SQLite vfs wrapper for
+** unix that generates per-database log files of all disk activity.
+*/
+
+/*
+** This module contains code for a wrapper VFS that causes a log of
+** most VFS calls to be written into a file on disk.
+**
+** Each database connection creates a separate log file in the same
+** directory as the original database and named after the original
+** database. A unique suffix is added to avoid name collisions.
+** Separate log files are used so that concurrent processes do not
+** try to write log operations to the same file at the same instant,
+** resulting in overwritten or comingled log text.
+**
+** Each individual log file records operations by a single database
+** connection on both the original database and its associated rollback
+** journal.
+**
+** The log files are in the comma-separated-value (CSV) format. The
+** log files can be imported into an SQLite database using the ".import"
+** command of the SQLite command-line shell for analysis.
+**
+** One technique for using this module is to append the text of this
+** module to the end of a standard "sqlite3.c" amalgamation file then
+** add the following compile-time options:
+**
+** -DSQLITE_EXTRA_INIT=sqlite3_register_vfslog
+** -DSQLITE_USE_FCNTL_TRACE
+**
+** The first compile-time option causes the sqlite3_register_vfslog()
+** function, defined below, to be invoked when SQLite is initialized.
+** That causes this custom VFS to become the default VFS for all
+** subsequent connections. The SQLITE_USE_FCNTL_TRACE option causes
+** the SQLite core to issue extra sqlite3_file_control() operations
+** with SQLITE_FCNTL_TRACE to give some indication of what is going
+** on in the core.
+*/
+
+#include "sqlite3.h"
+#include <string.h>
+#include <assert.h>
+#include <stdio.h>
+#if SQLITE_OS_UNIX
+# include <unistd.h>
+#endif
+
+/*
+** Forward declaration of objects used by this utility
+*/
+typedef struct VLogLog VLogLog;
+typedef struct VLogVfs VLogVfs;
+typedef struct VLogFile VLogFile;
+
+/* There is a pair (an array of size 2) of the following objects for
+** each database file being logged. The first contains the filename
+** and is used to log I/O with the main database. The second has
+** a NULL filename and is used to log I/O for the journal. Both
+** out pointers are the same.
+*/
+struct VLogLog {
+ VLogLog *pNext; /* Next in a list of all active logs */
+ VLogLog **ppPrev; /* Pointer to this in the list */
+ int nRef; /* Number of references to this object */
+ int nFilename; /* Length of zFilename in bytes */
+ char *zFilename; /* Name of database file. NULL for journal */
+ FILE *out; /* Write information here */
+};
+
+struct VLogVfs {
+ sqlite3_vfs base; /* VFS methods */
+ sqlite3_vfs *pVfs; /* Parent VFS */
+};
+
+struct VLogFile {
+ sqlite3_file base; /* IO methods */
+ sqlite3_file *pReal; /* Underlying file handle */
+ VLogLog *pLog; /* The log file for this file */
+};
+
+#define REALVFS(p) (((VLogVfs*)(p))->pVfs)
+
+/*
+** Methods for VLogFile
+*/
+static int vlogClose(sqlite3_file*);
+static int vlogRead(sqlite3_file*, void*, int iAmt, sqlite3_int64 iOfst);
+static int vlogWrite(sqlite3_file*,const void*,int iAmt, sqlite3_int64 iOfst);
+static int vlogTruncate(sqlite3_file*, sqlite3_int64 size);
+static int vlogSync(sqlite3_file*, int flags);
+static int vlogFileSize(sqlite3_file*, sqlite3_int64 *pSize);
+static int vlogLock(sqlite3_file*, int);
+static int vlogUnlock(sqlite3_file*, int);
+static int vlogCheckReservedLock(sqlite3_file*, int *pResOut);
+static int vlogFileControl(sqlite3_file*, int op, void *pArg);
+static int vlogSectorSize(sqlite3_file*);
+static int vlogDeviceCharacteristics(sqlite3_file*);
+
+/*
+** Methods for VLogVfs
+*/
+static int vlogOpen(sqlite3_vfs*, const char *, sqlite3_file*, int , int *);
+static int vlogDelete(sqlite3_vfs*, const char *zName, int syncDir);
+static int vlogAccess(sqlite3_vfs*, const char *zName, int flags, int *);
+static int vlogFullPathname(sqlite3_vfs*, const char *zName, int, char *zOut);
+static void *vlogDlOpen(sqlite3_vfs*, const char *zFilename);
+static void vlogDlError(sqlite3_vfs*, int nByte, char *zErrMsg);
+static void (*vlogDlSym(sqlite3_vfs *pVfs, void *p, const char*zSym))(void);
+static void vlogDlClose(sqlite3_vfs*, void*);
+static int vlogRandomness(sqlite3_vfs*, int nByte, char *zOut);
+static int vlogSleep(sqlite3_vfs*, int microseconds);
+static int vlogCurrentTime(sqlite3_vfs*, double*);
+static int vlogGetLastError(sqlite3_vfs*, int, char *);
+static int vlogCurrentTimeInt64(sqlite3_vfs*, sqlite3_int64*);
+
+static VLogVfs vlog_vfs = {
+ {
+ 1, /* iVersion */
+ 0, /* szOsFile (set by register_vlog()) */
+ 1024, /* mxPathname */
+ 0, /* pNext */
+ "vfslog", /* zName */
+ 0, /* pAppData */
+ vlogOpen, /* xOpen */
+ vlogDelete, /* xDelete */
+ vlogAccess, /* xAccess */
+ vlogFullPathname, /* xFullPathname */
+ vlogDlOpen, /* xDlOpen */
+ vlogDlError, /* xDlError */
+ vlogDlSym, /* xDlSym */
+ vlogDlClose, /* xDlClose */
+ vlogRandomness, /* xRandomness */
+ vlogSleep, /* xSleep */
+ vlogCurrentTime, /* xCurrentTime */
+ vlogGetLastError, /* xGetLastError */
+ vlogCurrentTimeInt64 /* xCurrentTimeInt64 */
+ },
+ 0
+};
+
+static sqlite3_io_methods vlog_io_methods = {
+ 1, /* iVersion */
+ vlogClose, /* xClose */
+ vlogRead, /* xRead */
+ vlogWrite, /* xWrite */
+ vlogTruncate, /* xTruncate */
+ vlogSync, /* xSync */
+ vlogFileSize, /* xFileSize */
+ vlogLock, /* xLock */
+ vlogUnlock, /* xUnlock */
+ vlogCheckReservedLock, /* xCheckReservedLock */
+ vlogFileControl, /* xFileControl */
+ vlogSectorSize, /* xSectorSize */
+ vlogDeviceCharacteristics, /* xDeviceCharacteristics */
+ 0, /* xShmMap */
+ 0, /* xShmLock */
+ 0, /* xShmBarrier */
+ 0 /* xShmUnmap */
+};
+
+#if SQLITE_OS_UNIX && !defined(NO_GETTOD)
+#include <sys/time.h>
+static sqlite3_uint64 vlog_time(){
+ struct timeval sTime;
+ gettimeofday(&sTime, 0);
+ return sTime.tv_usec + (sqlite3_uint64)sTime.tv_sec * 1000000;
+}
+#elif SQLITE_OS_WIN
+#include <windows.h>
+#include <time.h>
+static sqlite3_uint64 vlog_time(){
+ FILETIME ft;
+ sqlite3_uint64 u64time = 0;
+
+ GetSystemTimeAsFileTime(&ft);
+
+ u64time |= ft.dwHighDateTime;
+ u64time <<= 32;
+ u64time |= ft.dwLowDateTime;
+
+ /* ft is 100-nanosecond intervals, we want microseconds */
+ return u64time /(sqlite3_uint64)10;
+}
+#else
+static sqlite3_uint64 vlog_time(){
+ return 0;
+}
+#endif
+
+
+/*
+** Write a message to the log file
+*/
+static void vlogLogPrint(
+ VLogLog *pLog, /* The log file to write into */
+ sqlite3_int64 tStart, /* Start time of system call */
+ sqlite3_int64 tElapse, /* Elapse time of system call */
+ const char *zOp, /* Type of system call */
+ sqlite3_int64 iArg1, /* First argument */
+ sqlite3_int64 iArg2, /* Second argument */
+ const char *zArg3, /* Third argument */
+ int iRes /* Result */
+){
+ char z1[40], z2[40], z3[2000];
+ if( pLog==0 ) return;
+ if( iArg1>=0 ){
+ sqlite3_snprintf(sizeof(z1), z1, "%lld", iArg1);
+ }else{
+ z1[0] = 0;
+ }
+ if( iArg2>=0 ){
+ sqlite3_snprintf(sizeof(z2), z2, "%lld", iArg2);
+ }else{
+ z2[0] = 0;
+ }
+ if( zArg3 ){
+ sqlite3_snprintf(sizeof(z3), z3, "\"%.*w\"", sizeof(z3)-4, zArg3);
+ }else{
+ z3[0] = 0;
+ }
+ fprintf(pLog->out,"%lld,%lld,%s,%d,%s,%s,%s,%d\n",
+ tStart, tElapse, zOp, pLog->zFilename==0, z1, z2, z3, iRes);
+}
+
+/*
+** List of all active log connections. Protected by the master mutex.
+*/
+static VLogLog *allLogs = 0;
+
+/*
+** Close a VLogLog object
+*/
+static void vlogLogClose(VLogLog *p){
+ if( p ){
+ sqlite3_mutex *pMutex;
+ p->nRef--;
+ if( p->nRef>0 || p->zFilename==0 ) return;
+ pMutex = sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER);
+ sqlite3_mutex_enter(pMutex);
+ *p->ppPrev = p->pNext;
+ if( p->pNext ) p->pNext->ppPrev = p->ppPrev;
+ sqlite3_mutex_leave(pMutex);
+ fclose(p->out);
+ sqlite3_free(p);
+ }
+}
+
+/*
+** Open a VLogLog object on the given file
+*/
+static VLogLog *vlogLogOpen(const char *zFilename){
+ int nName = (int)strlen(zFilename);
+ int isJournal = 0;
+ sqlite3_mutex *pMutex;
+ VLogLog *pLog, *pTemp;
+ sqlite3_int64 tNow = 0;
+ if( nName>4 && strcmp(zFilename+nName-4,"-wal")==0 ){
+ return 0; /* Do not log wal files */
+ }else
+ if( nName>8 && strcmp(zFilename+nName-8,"-journal")==0 ){
+ nName -= 8;
+ isJournal = 1;
+ }else if( nName>12
+ && sqlite3_strglob("-mj??????9??", zFilename+nName-12)==0 ){
+ return 0; /* Do not log master journal files */
+ }
+ pTemp = sqlite3_malloc( sizeof(*pLog)*2 + nName + 60 );
+ if( pTemp==0 ) return 0;
+ pMutex = sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER);
+ sqlite3_mutex_enter(pMutex);
+ for(pLog=allLogs; pLog; pLog=pLog->pNext){
+ if( pLog->nFilename==nName && !memcmp(pLog->zFilename, zFilename, nName) ){
+ break;
+ }
+ }
+ if( pLog==0 ){
+ pLog = pTemp;
+ pTemp = 0;
+ memset(pLog, 0, sizeof(*pLog)*2);
+ pLog->zFilename = (char*)&pLog[2];
+ tNow = vlog_time();
+ sqlite3_snprintf(nName+60, pLog->zFilename, "%.*s-debuglog-%lld",
+ nName, zFilename, tNow);
+ pLog->out = fopen(pLog->zFilename, "a");
+ if( pLog->out==0 ){
+ sqlite3_mutex_leave(pMutex);
+ sqlite3_free(pLog);
+ return 0;
+ }
+ pLog->nFilename = nName;
+ pLog[1].out = pLog[0].out;
+ pLog->ppPrev = &allLogs;
+ if( allLogs ) allLogs->ppPrev = &pLog->pNext;
+ pLog->pNext = allLogs;
+ allLogs = pLog;
+ }
+ sqlite3_mutex_leave(pMutex);
+ if( pTemp ){
+ sqlite3_free(pTemp);
+ }else{
+#if SQLITE_OS_UNIX
+ char zHost[200];
+ zHost[0] = 0;
+ gethostname(zHost, sizeof(zHost)-1);
+ zHost[sizeof(zHost)-1] = 0;
+ vlogLogPrint(pLog, tNow, 0, "IDENT", getpid(), -1, zHost, 0);
+#endif
+ }
+ if( pLog && isJournal ) pLog++;
+ pLog->nRef++;
+ return pLog;
+}
+
+
+/*
+** Close an vlog-file.
+*/
+static int vlogClose(sqlite3_file *pFile){
+ sqlite3_uint64 tStart, tElapse;
+ int rc = SQLITE_OK;
+ VLogFile *p = (VLogFile *)pFile;
+
+ tStart = vlog_time();
+ if( p->pReal->pMethods ){
+ rc = p->pReal->pMethods->xClose(p->pReal);
+ }
+ tElapse = vlog_time() - tStart;
+ vlogLogPrint(p->pLog, tStart, tElapse, "CLOSE", -1, -1, 0, rc);
+ vlogLogClose(p->pLog);
+ return rc;
+}
+
+/*
+** Compute signature for a block of content.
+**
+** For blocks of 16 or fewer bytes, the signature is just a hex dump of
+** the entire block.
+**
+** For blocks of more than 16 bytes, the signature is a hex dump of the
+** first 8 bytes followed by a 64-bit has of the entire block.
+*/
+static void vlogSignature(unsigned char *p, int n, char *zCksum){
+ unsigned int s0 = 0, s1 = 0;
+ unsigned int *pI;
+ int i;
+ if( n<=16 ){
+ for(i=0; i<n; i++) sqlite3_snprintf(3, zCksum+i*2, "%02x", p[i]);
+ }else{
+ pI = (unsigned int*)p;
+ for(i=0; i<n-7; i+=8){
+ s0 += pI[0] + s1;
+ s1 += pI[1] + s0;
+ pI += 2;
+ }
+ for(i=0; i<8; i++) sqlite3_snprintf(3, zCksum+i*2, "%02x", p[i]);
+ sqlite3_snprintf(18, zCksum+i*2, "-%08x%08x", s0, s1);
+ }
+}
+
+/*
+** Convert a big-endian 32-bit integer into a native integer
+*/
+static int bigToNative(const unsigned char *x){
+ return (x[0]<<24) + (x[1]<<16) + (x[2]<<8) + x[3];
+}
+
+/*
+** Read data from an vlog-file.
+*/
+static int vlogRead(
+ sqlite3_file *pFile,
+ void *zBuf,
+ int iAmt,
+ sqlite_int64 iOfst
+){
+ int rc;
+ sqlite3_uint64 tStart, tElapse;
+ VLogFile *p = (VLogFile *)pFile;
+ char zSig[40];
+
+ tStart = vlog_time();
+ rc = p->pReal->pMethods->xRead(p->pReal, zBuf, iAmt, iOfst);
+ tElapse = vlog_time() - tStart;
+ if( rc==SQLITE_OK ){
+ vlogSignature(zBuf, iAmt, zSig);
+ }else{
+ zSig[0] = 0;
+ }
+ vlogLogPrint(p->pLog, tStart, tElapse, "READ", iAmt, iOfst, zSig, rc);
+ if( rc==SQLITE_OK
+ && p->pLog
+ && p->pLog->zFilename
+ && iOfst<=24
+ && iOfst+iAmt>=28
+ ){
+ unsigned char *x = ((unsigned char*)zBuf)+(24-iOfst);
+ unsigned iCtr, nFree = -1;
+ char *zFree = 0;
+ char zStr[12];
+ iCtr = bigToNative(x);
+ if( iOfst+iAmt>=40 ){
+ zFree = zStr;
+ sqlite3_snprintf(sizeof(zStr), zStr, "%d", bigToNative(x+8));
+ nFree = bigToNative(x+12);
+ }
+ vlogLogPrint(p->pLog, tStart, 0, "CHNGCTR-READ", iCtr, nFree, zFree, 0);
+ }
+ return rc;
+}
+
+/*
+** Write data to an vlog-file.
+*/
+static int vlogWrite(
+ sqlite3_file *pFile,
+ const void *z,
+ int iAmt,
+ sqlite_int64 iOfst
+){
+ int rc;
+ sqlite3_uint64 tStart, tElapse;
+ VLogFile *p = (VLogFile *)pFile;
+ char zSig[40];
+
+ tStart = vlog_time();
+ vlogSignature((unsigned char*)z, iAmt, zSig);
+ rc = p->pReal->pMethods->xWrite(p->pReal, z, iAmt, iOfst);
+ tElapse = vlog_time() - tStart;
+ vlogLogPrint(p->pLog, tStart, tElapse, "WRITE", iAmt, iOfst, zSig, rc);
+ if( rc==SQLITE_OK
+ && p->pLog
+ && p->pLog->zFilename
+ && iOfst<=24
+ && iOfst+iAmt>=28
+ ){
+ unsigned char *x = ((unsigned char*)z)+(24-iOfst);
+ unsigned iCtr, nFree = -1;
+ char *zFree = 0;
+ char zStr[12];
+ iCtr = bigToNative(x);
+ if( iOfst+iAmt>=40 ){
+ zFree = zStr;
+ sqlite3_snprintf(sizeof(zStr), zStr, "%d", bigToNative(x+8));
+ nFree = bigToNative(x+12);
+ }
+ vlogLogPrint(p->pLog, tStart, 0, "CHNGCTR-WRITE", iCtr, nFree, zFree, 0);
+ }
+ return rc;
+}
+
+/*
+** Truncate an vlog-file.
+*/
+static int vlogTruncate(sqlite3_file *pFile, sqlite_int64 size){
+ int rc;
+ sqlite3_uint64 tStart, tElapse;
+ VLogFile *p = (VLogFile *)pFile;
+ tStart = vlog_time();
+ rc = p->pReal->pMethods->xTruncate(p->pReal, size);
+ tElapse = vlog_time() - tStart;
+ vlogLogPrint(p->pLog, tStart, tElapse, "TRUNCATE", size, -1, 0, rc);
+ return rc;
+}
+
+/*
+** Sync an vlog-file.
+*/
+static int vlogSync(sqlite3_file *pFile, int flags){
+ int rc;
+ sqlite3_uint64 tStart, tElapse;
+ VLogFile *p = (VLogFile *)pFile;
+ tStart = vlog_time();
+ rc = p->pReal->pMethods->xSync(p->pReal, flags);
+ tElapse = vlog_time() - tStart;
+ vlogLogPrint(p->pLog, tStart, tElapse, "SYNC", flags, -1, 0, rc);
+ return rc;
+}
+
+/*
+** Return the current file-size of an vlog-file.
+*/
+static int vlogFileSize(sqlite3_file *pFile, sqlite_int64 *pSize){
+ int rc;
+ sqlite3_uint64 tStart, tElapse;
+ VLogFile *p = (VLogFile *)pFile;
+ tStart = vlog_time();
+ rc = p->pReal->pMethods->xFileSize(p->pReal, pSize);
+ tElapse = vlog_time() - tStart;
+ vlogLogPrint(p->pLog, tStart, tElapse, "FILESIZE", *pSize, -1, 0, rc);
+ return rc;
+}
+
+/*
+** Lock an vlog-file.
+*/
+static int vlogLock(sqlite3_file *pFile, int eLock){
+ int rc;
+ sqlite3_uint64 tStart, tElapse;
+ VLogFile *p = (VLogFile *)pFile;
+ tStart = vlog_time();
+ rc = p->pReal->pMethods->xLock(p->pReal, eLock);
+ tElapse = vlog_time() - tStart;
+ vlogLogPrint(p->pLog, tStart, tElapse, "LOCK", eLock, -1, 0, rc);
+ return rc;
+}
+
+/*
+** Unlock an vlog-file.
+*/
+static int vlogUnlock(sqlite3_file *pFile, int eLock){
+ int rc;
+ sqlite3_uint64 tStart;
+ VLogFile *p = (VLogFile *)pFile;
+ tStart = vlog_time();
+ vlogLogPrint(p->pLog, tStart, 0, "UNLOCK", eLock, -1, 0, 0);
+ rc = p->pReal->pMethods->xUnlock(p->pReal, eLock);
+ return rc;
+}
+
+/*
+** Check if another file-handle holds a RESERVED lock on an vlog-file.
+*/
+static int vlogCheckReservedLock(sqlite3_file *pFile, int *pResOut){
+ int rc;
+ sqlite3_uint64 tStart, tElapse;
+ VLogFile *p = (VLogFile *)pFile;
+ tStart = vlog_time();
+ rc = p->pReal->pMethods->xCheckReservedLock(p->pReal, pResOut);
+ tElapse = vlog_time() - tStart;
+ vlogLogPrint(p->pLog, tStart, tElapse, "CHECKRESERVEDLOCK",
+ *pResOut, -1, "", rc);
+ return rc;
+}
+
+/*
+** File control method. For custom operations on an vlog-file.
+*/
+static int vlogFileControl(sqlite3_file *pFile, int op, void *pArg){
+ VLogFile *p = (VLogFile *)pFile;
+ sqlite3_uint64 tStart, tElapse;
+ int rc;
+ tStart = vlog_time();
+ rc = p->pReal->pMethods->xFileControl(p->pReal, op, pArg);
+ if( op==SQLITE_FCNTL_VFSNAME && rc==SQLITE_OK ){
+ *(char**)pArg = sqlite3_mprintf("vlog/%z", *(char**)pArg);
+ }
+ tElapse = vlog_time() - tStart;
+ if( op==SQLITE_FCNTL_TRACE ){
+ vlogLogPrint(p->pLog, tStart, tElapse, "TRACE", op, -1, pArg, rc);
+ }else if( op==SQLITE_FCNTL_PRAGMA ){
+ const char **azArg = (const char **)pArg;
+ vlogLogPrint(p->pLog, tStart, tElapse, "FILECONTROL", op, -1, azArg[1], rc);
+ }else if( op==SQLITE_FCNTL_SIZE_HINT ){
+ sqlite3_int64 sz = *(sqlite3_int64*)pArg;
+ vlogLogPrint(p->pLog, tStart, tElapse, "FILECONTROL", op, sz, 0, rc);
+ }else{
+ vlogLogPrint(p->pLog, tStart, tElapse, "FILECONTROL", op, -1, 0, rc);
+ }
+ return rc;
+}
+
+/*
+** Return the sector-size in bytes for an vlog-file.
+*/
+static int vlogSectorSize(sqlite3_file *pFile){
+ int rc;
+ sqlite3_uint64 tStart, tElapse;
+ VLogFile *p = (VLogFile *)pFile;
+ tStart = vlog_time();
+ rc = p->pReal->pMethods->xSectorSize(p->pReal);
+ tElapse = vlog_time() - tStart;
+ vlogLogPrint(p->pLog, tStart, tElapse, "SECTORSIZE", -1, -1, 0, rc);
+ return rc;
+}
+
+/*
+** Return the device characteristic flags supported by an vlog-file.
+*/
+static int vlogDeviceCharacteristics(sqlite3_file *pFile){
+ int rc;
+ sqlite3_uint64 tStart, tElapse;
+ VLogFile *p = (VLogFile *)pFile;
+ tStart = vlog_time();
+ rc = p->pReal->pMethods->xDeviceCharacteristics(p->pReal);
+ tElapse = vlog_time() - tStart;
+ vlogLogPrint(p->pLog, tStart, tElapse, "DEVCHAR", -1, -1, 0, rc);
+ return rc;
+}
+
+
+/*
+** Open an vlog file handle.
+*/
+static int vlogOpen(
+ sqlite3_vfs *pVfs,
+ const char *zName,
+ sqlite3_file *pFile,
+ int flags,
+ int *pOutFlags
+){
+ int rc;
+ sqlite3_uint64 tStart, tElapse;
+ sqlite3_int64 iArg2;
+ VLogFile *p = (VLogFile*)pFile;
+
+ p->pReal = (sqlite3_file*)&p[1];
+ if( (flags & (SQLITE_OPEN_MAIN_DB|SQLITE_OPEN_MAIN_JOURNAL))!=0 ){
+ p->pLog = vlogLogOpen(zName);
+ }else{
+ p->pLog = 0;
+ }
+ tStart = vlog_time();
+ rc = REALVFS(pVfs)->xOpen(REALVFS(pVfs), zName, p->pReal, flags, pOutFlags);
+ tElapse = vlog_time() - tStart;
+ iArg2 = pOutFlags ? *pOutFlags : -1;
+ vlogLogPrint(p->pLog, tStart, tElapse, "OPEN", flags, iArg2, 0, rc);
+ if( rc==SQLITE_OK ){
+ pFile->pMethods = &vlog_io_methods;
+ }else{
+ if( p->pLog ) vlogLogClose(p->pLog);
+ p->pLog = 0;
+ }
+ return rc;
+}
+
+/*
+** Delete the file located at zPath. If the dirSync argument is true,
+** ensure the file-system modifications are synced to disk before
+** returning.
+*/
+static int vlogDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){
+ int rc;
+ sqlite3_uint64 tStart, tElapse;
+ VLogLog *pLog;
+ tStart = vlog_time();
+ rc = REALVFS(pVfs)->xDelete(REALVFS(pVfs), zPath, dirSync);
+ tElapse = vlog_time() - tStart;
+ pLog = vlogLogOpen(zPath);
+ vlogLogPrint(pLog, tStart, tElapse, "DELETE", dirSync, -1, 0, rc);
+ vlogLogClose(pLog);
+ return rc;
+}
+
+/*
+** Test for access permissions. Return true if the requested permission
+** is available, or false otherwise.
+*/
+static int vlogAccess(
+ sqlite3_vfs *pVfs,
+ const char *zPath,
+ int flags,
+ int *pResOut
+){
+ int rc;
+ sqlite3_uint64 tStart, tElapse;
+ VLogLog *pLog;
+ tStart = vlog_time();
+ rc = REALVFS(pVfs)->xAccess(REALVFS(pVfs), zPath, flags, pResOut);
+ tElapse = vlog_time() - tStart;
+ pLog = vlogLogOpen(zPath);
+ vlogLogPrint(pLog, tStart, tElapse, "ACCESS", flags, *pResOut, 0, rc);
+ vlogLogClose(pLog);
+ return rc;
+}
+
+/*
+** Populate buffer zOut with the full canonical pathname corresponding
+** to the pathname in zPath. zOut is guaranteed to point to a buffer
+** of at least (INST_MAX_PATHNAME+1) bytes.
+*/
+static int vlogFullPathname(
+ sqlite3_vfs *pVfs,
+ const char *zPath,
+ int nOut,
+ char *zOut
+){
+ return REALVFS(pVfs)->xFullPathname(REALVFS(pVfs), zPath, nOut, zOut);
+}
+
+/*
+** Open the dynamic library located at zPath and return a handle.
+*/
+static void *vlogDlOpen(sqlite3_vfs *pVfs, const char *zPath){
+ return REALVFS(pVfs)->xDlOpen(REALVFS(pVfs), zPath);
+}
+
+/*
+** Populate the buffer zErrMsg (size nByte bytes) with a human readable
+** utf-8 string describing the most recent error encountered associated
+** with dynamic libraries.
+*/
+static void vlogDlError(sqlite3_vfs *pVfs, int nByte, char *zErrMsg){
+ REALVFS(pVfs)->xDlError(REALVFS(pVfs), nByte, zErrMsg);
+}
+
+/*
+** Return a pointer to the symbol zSymbol in the dynamic library pHandle.
+*/
+static void (*vlogDlSym(sqlite3_vfs *pVfs, void *p, const char *zSym))(void){
+ return REALVFS(pVfs)->xDlSym(REALVFS(pVfs), p, zSym);
+}
+
+/*
+** Close the dynamic library handle pHandle.
+*/
+static void vlogDlClose(sqlite3_vfs *pVfs, void *pHandle){
+ REALVFS(pVfs)->xDlClose(REALVFS(pVfs), pHandle);
+}
+
+/*
+** Populate the buffer pointed to by zBufOut with nByte bytes of
+** random data.
+*/
+static int vlogRandomness(sqlite3_vfs *pVfs, int nByte, char *zBufOut){
+ return REALVFS(pVfs)->xRandomness(REALVFS(pVfs), nByte, zBufOut);
+}
+
+/*
+** Sleep for nMicro microseconds. Return the number of microseconds
+** actually slept.
+*/
+static int vlogSleep(sqlite3_vfs *pVfs, int nMicro){
+ return REALVFS(pVfs)->xSleep(REALVFS(pVfs), nMicro);
+}
+
+/*
+** Return the current time as a Julian Day number in *pTimeOut.
+*/
+static int vlogCurrentTime(sqlite3_vfs *pVfs, double *pTimeOut){
+ return REALVFS(pVfs)->xCurrentTime(REALVFS(pVfs), pTimeOut);
+}
+
+static int vlogGetLastError(sqlite3_vfs *pVfs, int a, char *b){
+ return REALVFS(pVfs)->xGetLastError(REALVFS(pVfs), a, b);
+}
+static int vlogCurrentTimeInt64(sqlite3_vfs *pVfs, sqlite3_int64 *p){
+ return REALVFS(pVfs)->xCurrentTimeInt64(REALVFS(pVfs), p);
+}
+
+/*
+** Register debugvfs as the default VFS for this process.
+*/
+int sqlite3_register_vfslog(const char *zArg){
+ vlog_vfs.pVfs = sqlite3_vfs_find(0);
+ vlog_vfs.base.szOsFile = sizeof(VLogFile) + vlog_vfs.pVfs->szOsFile;
+ return sqlite3_vfs_register(&vlog_vfs.base, 1);
+}
diff --git a/ext/misc/vtshim.c b/ext/misc/vtshim.c
new file mode 100644
index 0000000..01348e8
--- /dev/null
+++ b/ext/misc/vtshim.c
@@ -0,0 +1,551 @@
+/*
+** 2013-06-12
+**
+** 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.
+**
+*************************************************************************
+**
+** A shim that sits between the SQLite virtual table interface and
+** runtimes with garbage collector based memory management.
+*/
+#include "sqlite3ext.h"
+SQLITE_EXTENSION_INIT1
+#include <assert.h>
+#include <string.h>
+
+#ifndef SQLITE_OMIT_VIRTUALTABLE
+
+/* Forward references */
+typedef struct vtshim_aux vtshim_aux;
+typedef struct vtshim_vtab vtshim_vtab;
+typedef struct vtshim_cursor vtshim_cursor;
+
+
+/* The vtshim_aux argument is the auxiliary parameter that is passed
+** into sqlite3_create_module_v2().
+*/
+struct vtshim_aux {
+ void *pChildAux; /* pAux for child virtual tables */
+ void (*xChildDestroy)(void*); /* Destructor for pChildAux */
+ sqlite3_module *pMod; /* Methods for child virtual tables */
+ sqlite3 *db; /* The database to which we are attached */
+ char *zName; /* Name of the module */
+ int bDisposed; /* True if disposed */
+ vtshim_vtab *pAllVtab; /* List of all vtshim_vtab objects */
+ sqlite3_module sSelf; /* Methods used by this shim */
+};
+
+/* A vtshim virtual table object */
+struct vtshim_vtab {
+ sqlite3_vtab base; /* Base class - must be first */
+ sqlite3_vtab *pChild; /* Child virtual table */
+ vtshim_aux *pAux; /* Pointer to vtshim_aux object */
+ vtshim_cursor *pAllCur; /* List of all cursors */
+ vtshim_vtab **ppPrev; /* Previous on list */
+ vtshim_vtab *pNext; /* Next on list */
+};
+
+/* A vtshim cursor object */
+struct vtshim_cursor {
+ sqlite3_vtab_cursor base; /* Base class - must be first */
+ sqlite3_vtab_cursor *pChild; /* Cursor generated by the managed subclass */
+ vtshim_cursor **ppPrev; /* Previous on list of all cursors */
+ vtshim_cursor *pNext; /* Next on list of all cursors */
+};
+
+/* Macro used to copy the child vtable error message to outer vtable */
+#define VTSHIM_COPY_ERRMSG() \
+ do { \
+ sqlite3_free(pVtab->base.zErrMsg); \
+ pVtab->base.zErrMsg = sqlite3_mprintf("%s", pVtab->pChild->zErrMsg); \
+ } while (0)
+
+/* Methods for the vtshim module */
+static int vtshimCreate(
+ sqlite3 *db,
+ void *ppAux,
+ int argc,
+ const char *const*argv,
+ sqlite3_vtab **ppVtab,
+ char **pzErr
+){
+ vtshim_aux *pAux = (vtshim_aux*)ppAux;
+ vtshim_vtab *pNew;
+ int rc;
+
+ assert( db==pAux->db );
+ if( pAux->bDisposed ){
+ if( pzErr ){
+ *pzErr = sqlite3_mprintf("virtual table was disposed: \"%s\"",
+ pAux->zName);
+ }
+ return SQLITE_ERROR;
+ }
+ pNew = sqlite3_malloc( sizeof(*pNew) );
+ *ppVtab = (sqlite3_vtab*)pNew;
+ if( pNew==0 ) return SQLITE_NOMEM;
+ memset(pNew, 0, sizeof(*pNew));
+ rc = pAux->pMod->xCreate(db, pAux->pChildAux, argc, argv,
+ &pNew->pChild, pzErr);
+ if( rc ){
+ sqlite3_free(pNew);
+ *ppVtab = 0;
+ }
+ pNew->pAux = pAux;
+ pNew->ppPrev = &pAux->pAllVtab;
+ pNew->pNext = pAux->pAllVtab;
+ if( pAux->pAllVtab ) pAux->pAllVtab->ppPrev = &pNew->pNext;
+ pAux->pAllVtab = pNew;
+ return rc;
+}
+
+static int vtshimConnect(
+ sqlite3 *db,
+ void *ppAux,
+ int argc,
+ const char *const*argv,
+ sqlite3_vtab **ppVtab,
+ char **pzErr
+){
+ vtshim_aux *pAux = (vtshim_aux*)ppAux;
+ vtshim_vtab *pNew;
+ int rc;
+
+ assert( db==pAux->db );
+ if( pAux->bDisposed ){
+ if( pzErr ){
+ *pzErr = sqlite3_mprintf("virtual table was disposed: \"%s\"",
+ pAux->zName);
+ }
+ return SQLITE_ERROR;
+ }
+ pNew = sqlite3_malloc( sizeof(*pNew) );
+ *ppVtab = (sqlite3_vtab*)pNew;
+ if( pNew==0 ) return SQLITE_NOMEM;
+ memset(pNew, 0, sizeof(*pNew));
+ rc = pAux->pMod->xConnect(db, pAux->pChildAux, argc, argv,
+ &pNew->pChild, pzErr);
+ if( rc ){
+ sqlite3_free(pNew);
+ *ppVtab = 0;
+ }
+ pNew->pAux = pAux;
+ pNew->ppPrev = &pAux->pAllVtab;
+ pNew->pNext = pAux->pAllVtab;
+ if( pAux->pAllVtab ) pAux->pAllVtab->ppPrev = &pNew->pNext;
+ pAux->pAllVtab = pNew;
+ return rc;
+}
+
+static int vtshimBestIndex(
+ sqlite3_vtab *pBase,
+ sqlite3_index_info *pIdxInfo
+){
+ vtshim_vtab *pVtab = (vtshim_vtab*)pBase;
+ vtshim_aux *pAux = pVtab->pAux;
+ int rc;
+ if( pAux->bDisposed ) return SQLITE_ERROR;
+ rc = pAux->pMod->xBestIndex(pVtab->pChild, pIdxInfo);
+ if( rc!=SQLITE_OK ){
+ VTSHIM_COPY_ERRMSG();
+ }
+ return rc;
+}
+
+static int vtshimDisconnect(sqlite3_vtab *pBase){
+ vtshim_vtab *pVtab = (vtshim_vtab*)pBase;
+ vtshim_aux *pAux = pVtab->pAux;
+ int rc = SQLITE_OK;
+ if( !pAux->bDisposed ){
+ rc = pAux->pMod->xDisconnect(pVtab->pChild);
+ }
+ if( pVtab->pNext ) pVtab->pNext->ppPrev = pVtab->ppPrev;
+ *pVtab->ppPrev = pVtab->pNext;
+ sqlite3_free(pVtab);
+ return rc;
+}
+
+static int vtshimDestroy(sqlite3_vtab *pBase){
+ vtshim_vtab *pVtab = (vtshim_vtab*)pBase;
+ vtshim_aux *pAux = pVtab->pAux;
+ int rc = SQLITE_OK;
+ if( !pAux->bDisposed ){
+ rc = pAux->pMod->xDestroy(pVtab->pChild);
+ }
+ if( pVtab->pNext ) pVtab->pNext->ppPrev = pVtab->ppPrev;
+ *pVtab->ppPrev = pVtab->pNext;
+ sqlite3_free(pVtab);
+ return rc;
+}
+
+static int vtshimOpen(sqlite3_vtab *pBase, sqlite3_vtab_cursor **ppCursor){
+ vtshim_vtab *pVtab = (vtshim_vtab*)pBase;
+ vtshim_aux *pAux = pVtab->pAux;
+ vtshim_cursor *pCur;
+ int rc;
+ *ppCursor = 0;
+ if( pAux->bDisposed ) return SQLITE_ERROR;
+ pCur = sqlite3_malloc( sizeof(*pCur) );
+ if( pCur==0 ) return SQLITE_NOMEM;
+ memset(pCur, 0, sizeof(*pCur));
+ rc = pAux->pMod->xOpen(pVtab->pChild, &pCur->pChild);
+ if( rc ){
+ sqlite3_free(pCur);
+ VTSHIM_COPY_ERRMSG();
+ return rc;
+ }
+ pCur->pChild->pVtab = pVtab->pChild;
+ *ppCursor = &pCur->base;
+ pCur->ppPrev = &pVtab->pAllCur;
+ if( pVtab->pAllCur ) pVtab->pAllCur->ppPrev = &pCur->pNext;
+ pCur->pNext = pVtab->pAllCur;
+ pVtab->pAllCur = pCur;
+ return SQLITE_OK;
+}
+
+static int vtshimClose(sqlite3_vtab_cursor *pX){
+ vtshim_cursor *pCur = (vtshim_cursor*)pX;
+ vtshim_vtab *pVtab = (vtshim_vtab*)pCur->base.pVtab;
+ vtshim_aux *pAux = pVtab->pAux;
+ int rc = SQLITE_OK;
+ if( !pAux->bDisposed ){
+ rc = pAux->pMod->xClose(pCur->pChild);
+ if( rc!=SQLITE_OK ){
+ VTSHIM_COPY_ERRMSG();
+ }
+ }
+ if( pCur->pNext ) pCur->pNext->ppPrev = pCur->ppPrev;
+ *pCur->ppPrev = pCur->pNext;
+ sqlite3_free(pCur);
+ return rc;
+}
+
+static int vtshimFilter(
+ sqlite3_vtab_cursor *pX,
+ int idxNum,
+ const char *idxStr,
+ int argc,
+ sqlite3_value **argv
+){
+ vtshim_cursor *pCur = (vtshim_cursor*)pX;
+ vtshim_vtab *pVtab = (vtshim_vtab*)pCur->base.pVtab;
+ vtshim_aux *pAux = pVtab->pAux;
+ int rc;
+ if( pAux->bDisposed ) return SQLITE_ERROR;
+ rc = pAux->pMod->xFilter(pCur->pChild, idxNum, idxStr, argc, argv);
+ if( rc!=SQLITE_OK ){
+ VTSHIM_COPY_ERRMSG();
+ }
+ return rc;
+}
+
+static int vtshimNext(sqlite3_vtab_cursor *pX){
+ vtshim_cursor *pCur = (vtshim_cursor*)pX;
+ vtshim_vtab *pVtab = (vtshim_vtab*)pCur->base.pVtab;
+ vtshim_aux *pAux = pVtab->pAux;
+ int rc;
+ if( pAux->bDisposed ) return SQLITE_ERROR;
+ rc = pAux->pMod->xNext(pCur->pChild);
+ if( rc!=SQLITE_OK ){
+ VTSHIM_COPY_ERRMSG();
+ }
+ return rc;
+}
+
+static int vtshimEof(sqlite3_vtab_cursor *pX){
+ vtshim_cursor *pCur = (vtshim_cursor*)pX;
+ vtshim_vtab *pVtab = (vtshim_vtab*)pCur->base.pVtab;
+ vtshim_aux *pAux = pVtab->pAux;
+ int rc;
+ if( pAux->bDisposed ) return 1;
+ rc = pAux->pMod->xEof(pCur->pChild);
+ VTSHIM_COPY_ERRMSG();
+ return rc;
+}
+
+static int vtshimColumn(sqlite3_vtab_cursor *pX, sqlite3_context *ctx, int i){
+ vtshim_cursor *pCur = (vtshim_cursor*)pX;
+ vtshim_vtab *pVtab = (vtshim_vtab*)pCur->base.pVtab;
+ vtshim_aux *pAux = pVtab->pAux;
+ int rc;
+ if( pAux->bDisposed ) return SQLITE_ERROR;
+ rc = pAux->pMod->xColumn(pCur->pChild, ctx, i);
+ if( rc!=SQLITE_OK ){
+ VTSHIM_COPY_ERRMSG();
+ }
+ return rc;
+}
+
+static int vtshimRowid(sqlite3_vtab_cursor *pX, sqlite3_int64 *pRowid){
+ vtshim_cursor *pCur = (vtshim_cursor*)pX;
+ vtshim_vtab *pVtab = (vtshim_vtab*)pCur->base.pVtab;
+ vtshim_aux *pAux = pVtab->pAux;
+ int rc;
+ if( pAux->bDisposed ) return SQLITE_ERROR;
+ rc = pAux->pMod->xRowid(pCur->pChild, pRowid);
+ if( rc!=SQLITE_OK ){
+ VTSHIM_COPY_ERRMSG();
+ }
+ return rc;
+}
+
+static int vtshimUpdate(
+ sqlite3_vtab *pBase,
+ int argc,
+ sqlite3_value **argv,
+ sqlite3_int64 *pRowid
+){
+ vtshim_vtab *pVtab = (vtshim_vtab*)pBase;
+ vtshim_aux *pAux = pVtab->pAux;
+ int rc;
+ if( pAux->bDisposed ) return SQLITE_ERROR;
+ rc = pAux->pMod->xUpdate(pVtab->pChild, argc, argv, pRowid);
+ if( rc!=SQLITE_OK ){
+ VTSHIM_COPY_ERRMSG();
+ }
+ return rc;
+}
+
+static int vtshimBegin(sqlite3_vtab *pBase){
+ vtshim_vtab *pVtab = (vtshim_vtab*)pBase;
+ vtshim_aux *pAux = pVtab->pAux;
+ int rc;
+ if( pAux->bDisposed ) return SQLITE_ERROR;
+ rc = pAux->pMod->xBegin(pVtab->pChild);
+ if( rc!=SQLITE_OK ){
+ VTSHIM_COPY_ERRMSG();
+ }
+ return rc;
+}
+
+static int vtshimSync(sqlite3_vtab *pBase){
+ vtshim_vtab *pVtab = (vtshim_vtab*)pBase;
+ vtshim_aux *pAux = pVtab->pAux;
+ int rc;
+ if( pAux->bDisposed ) return SQLITE_ERROR;
+ rc = pAux->pMod->xSync(pVtab->pChild);
+ if( rc!=SQLITE_OK ){
+ VTSHIM_COPY_ERRMSG();
+ }
+ return rc;
+}
+
+static int vtshimCommit(sqlite3_vtab *pBase){
+ vtshim_vtab *pVtab = (vtshim_vtab*)pBase;
+ vtshim_aux *pAux = pVtab->pAux;
+ int rc;
+ if( pAux->bDisposed ) return SQLITE_ERROR;
+ rc = pAux->pMod->xCommit(pVtab->pChild);
+ if( rc!=SQLITE_OK ){
+ VTSHIM_COPY_ERRMSG();
+ }
+ return rc;
+}
+
+static int vtshimRollback(sqlite3_vtab *pBase){
+ vtshim_vtab *pVtab = (vtshim_vtab*)pBase;
+ vtshim_aux *pAux = pVtab->pAux;
+ int rc;
+ if( pAux->bDisposed ) return SQLITE_ERROR;
+ rc = pAux->pMod->xRollback(pVtab->pChild);
+ if( rc!=SQLITE_OK ){
+ VTSHIM_COPY_ERRMSG();
+ }
+ return rc;
+}
+
+static int vtshimFindFunction(
+ sqlite3_vtab *pBase,
+ int nArg,
+ const char *zName,
+ void (**pxFunc)(sqlite3_context*,int,sqlite3_value**),
+ void **ppArg
+){
+ vtshim_vtab *pVtab = (vtshim_vtab*)pBase;
+ vtshim_aux *pAux = pVtab->pAux;
+ int rc;
+ if( pAux->bDisposed ) return 0;
+ rc = pAux->pMod->xFindFunction(pVtab->pChild, nArg, zName, pxFunc, ppArg);
+ VTSHIM_COPY_ERRMSG();
+ return rc;
+}
+
+static int vtshimRename(sqlite3_vtab *pBase, const char *zNewName){
+ vtshim_vtab *pVtab = (vtshim_vtab*)pBase;
+ vtshim_aux *pAux = pVtab->pAux;
+ int rc;
+ if( pAux->bDisposed ) return SQLITE_ERROR;
+ rc = pAux->pMod->xRename(pVtab->pChild, zNewName);
+ if( rc!=SQLITE_OK ){
+ VTSHIM_COPY_ERRMSG();
+ }
+ return rc;
+}
+
+static int vtshimSavepoint(sqlite3_vtab *pBase, int n){
+ vtshim_vtab *pVtab = (vtshim_vtab*)pBase;
+ vtshim_aux *pAux = pVtab->pAux;
+ int rc;
+ if( pAux->bDisposed ) return SQLITE_ERROR;
+ rc = pAux->pMod->xSavepoint(pVtab->pChild, n);
+ if( rc!=SQLITE_OK ){
+ VTSHIM_COPY_ERRMSG();
+ }
+ return rc;
+}
+
+static int vtshimRelease(sqlite3_vtab *pBase, int n){
+ vtshim_vtab *pVtab = (vtshim_vtab*)pBase;
+ vtshim_aux *pAux = pVtab->pAux;
+ int rc;
+ if( pAux->bDisposed ) return SQLITE_ERROR;
+ rc = pAux->pMod->xRelease(pVtab->pChild, n);
+ if( rc!=SQLITE_OK ){
+ VTSHIM_COPY_ERRMSG();
+ }
+ return rc;
+}
+
+static int vtshimRollbackTo(sqlite3_vtab *pBase, int n){
+ vtshim_vtab *pVtab = (vtshim_vtab*)pBase;
+ vtshim_aux *pAux = pVtab->pAux;
+ int rc;
+ if( pAux->bDisposed ) return SQLITE_ERROR;
+ rc = pAux->pMod->xRollbackTo(pVtab->pChild, n);
+ if( rc!=SQLITE_OK ){
+ VTSHIM_COPY_ERRMSG();
+ }
+ return rc;
+}
+
+/* The destructor function for a disposible module */
+static void vtshimAuxDestructor(void *pXAux){
+ vtshim_aux *pAux = (vtshim_aux*)pXAux;
+ assert( pAux->pAllVtab==0 );
+ if( !pAux->bDisposed && pAux->xChildDestroy ){
+ pAux->xChildDestroy(pAux->pChildAux);
+ pAux->xChildDestroy = 0;
+ }
+ sqlite3_free(pAux->zName);
+ sqlite3_free(pAux->pMod);
+ sqlite3_free(pAux);
+}
+
+static int vtshimCopyModule(
+ const sqlite3_module *pMod, /* Source module to be copied */
+ sqlite3_module **ppMod /* Destination for copied module */
+){
+ sqlite3_module *p;
+ if( !pMod || !ppMod ) return SQLITE_ERROR;
+ p = sqlite3_malloc( sizeof(*p) );
+ if( p==0 ) return SQLITE_NOMEM;
+ memcpy(p, pMod, sizeof(*p));
+ *ppMod = p;
+ return SQLITE_OK;
+}
+
+#ifdef _WIN32
+__declspec(dllexport)
+#endif
+void *sqlite3_create_disposable_module(
+ sqlite3 *db, /* SQLite connection to register module with */
+ const char *zName, /* Name of the module */
+ const sqlite3_module *p, /* Methods for the module */
+ void *pClientData, /* Client data for xCreate/xConnect */
+ void(*xDestroy)(void*) /* Module destructor function */
+){
+ vtshim_aux *pAux;
+ sqlite3_module *pMod;
+ int rc;
+ pAux = sqlite3_malloc( sizeof(*pAux) );
+ if( pAux==0 ){
+ if( xDestroy ) xDestroy(pClientData);
+ return 0;
+ }
+ rc = vtshimCopyModule(p, &pMod);
+ if( rc!=SQLITE_OK ){
+ sqlite3_free(pAux);
+ return 0;
+ }
+ pAux->pChildAux = pClientData;
+ pAux->xChildDestroy = xDestroy;
+ pAux->pMod = pMod;
+ pAux->db = db;
+ pAux->zName = sqlite3_mprintf("%s", zName);
+ pAux->bDisposed = 0;
+ pAux->pAllVtab = 0;
+ pAux->sSelf.iVersion = p->iVersion<=2 ? p->iVersion : 2;
+ pAux->sSelf.xCreate = p->xCreate ? vtshimCreate : 0;
+ pAux->sSelf.xConnect = p->xConnect ? vtshimConnect : 0;
+ pAux->sSelf.xBestIndex = p->xBestIndex ? vtshimBestIndex : 0;
+ pAux->sSelf.xDisconnect = p->xDisconnect ? vtshimDisconnect : 0;
+ pAux->sSelf.xDestroy = p->xDestroy ? vtshimDestroy : 0;
+ pAux->sSelf.xOpen = p->xOpen ? vtshimOpen : 0;
+ pAux->sSelf.xClose = p->xClose ? vtshimClose : 0;
+ pAux->sSelf.xFilter = p->xFilter ? vtshimFilter : 0;
+ pAux->sSelf.xNext = p->xNext ? vtshimNext : 0;
+ pAux->sSelf.xEof = p->xEof ? vtshimEof : 0;
+ pAux->sSelf.xColumn = p->xColumn ? vtshimColumn : 0;
+ pAux->sSelf.xRowid = p->xRowid ? vtshimRowid : 0;
+ pAux->sSelf.xUpdate = p->xUpdate ? vtshimUpdate : 0;
+ pAux->sSelf.xBegin = p->xBegin ? vtshimBegin : 0;
+ pAux->sSelf.xSync = p->xSync ? vtshimSync : 0;
+ pAux->sSelf.xCommit = p->xCommit ? vtshimCommit : 0;
+ pAux->sSelf.xRollback = p->xRollback ? vtshimRollback : 0;
+ pAux->sSelf.xFindFunction = p->xFindFunction ? vtshimFindFunction : 0;
+ pAux->sSelf.xRename = p->xRename ? vtshimRename : 0;
+ if( p->iVersion>=2 ){
+ pAux->sSelf.xSavepoint = p->xSavepoint ? vtshimSavepoint : 0;
+ pAux->sSelf.xRelease = p->xRelease ? vtshimRelease : 0;
+ pAux->sSelf.xRollbackTo = p->xRollbackTo ? vtshimRollbackTo : 0;
+ }else{
+ pAux->sSelf.xSavepoint = 0;
+ pAux->sSelf.xRelease = 0;
+ pAux->sSelf.xRollbackTo = 0;
+ }
+ rc = sqlite3_create_module_v2(db, zName, &pAux->sSelf,
+ pAux, vtshimAuxDestructor);
+ return rc==SQLITE_OK ? (void*)pAux : 0;
+}
+
+#ifdef _WIN32
+__declspec(dllexport)
+#endif
+void sqlite3_dispose_module(void *pX){
+ vtshim_aux *pAux = (vtshim_aux*)pX;
+ if( !pAux->bDisposed ){
+ vtshim_vtab *pVtab;
+ vtshim_cursor *pCur;
+ for(pVtab=pAux->pAllVtab; pVtab; pVtab=pVtab->pNext){
+ for(pCur=pVtab->pAllCur; pCur; pCur=pCur->pNext){
+ pAux->pMod->xClose(pCur->pChild);
+ }
+ pAux->pMod->xDisconnect(pVtab->pChild);
+ }
+ pAux->bDisposed = 1;
+ if( pAux->xChildDestroy ){
+ pAux->xChildDestroy(pAux->pChildAux);
+ pAux->xChildDestroy = 0;
+ }
+ }
+}
+
+
+#endif /* SQLITE_OMIT_VIRTUALTABLE */
+
+#ifdef _WIN32
+__declspec(dllexport)
+#endif
+int sqlite3_vtshim_init(
+ sqlite3 *db,
+ char **pzErrMsg,
+ const sqlite3_api_routines *pApi
+){
+ SQLITE_EXTENSION_INIT2(pApi);
+ return SQLITE_OK;
+}