Merge branch 'sqlite-release' into release-integration

This commit is contained in:
Stephen Lombardo 2012-05-15 19:06:40 -04:00
commit 53c6fc9f5f
214 changed files with 13008 additions and 2633 deletions

View File

@ -954,6 +954,7 @@ clean:
rm -f mkkeywordhash$(BEXE) keywordhash.h rm -f mkkeywordhash$(BEXE) keywordhash.h
rm -f $(PUBLISH) rm -f $(PUBLISH)
rm -f *.da *.bb *.bbg gmon.out rm -f *.da *.bb *.bbg gmon.out
rm -rf quota2a quota2b quota2c
rm -rf tsrc .target_source rm -rf tsrc .target_source
rm -f tclsqlite3$(TEXE) rm -f tclsqlite3$(TEXE)
rm -f testfixture$(TEXE) test.db rm -f testfixture$(TEXE) test.db

View File

@ -979,9 +979,15 @@ sqlite3_analyzer.exe: sqlite3_analyzer.c
clean: clean:
del /Q *.lo *.ilk *.lib *.obj *.pdb sqlite3.exe libsqlite3.lib del /Q *.lo *.ilk *.lib *.obj *.pdb sqlite3.exe libsqlite3.lib
del /Q *.da *.bb *.bbg gmon.out
del /Q sqlite3.h opcodes.c opcodes.h del /Q sqlite3.h opcodes.c opcodes.h
del /Q lemon.exe lempar.c parse.* del /Q lemon.exe lempar.c parse.*
del /Q mkkeywordhash.exe keywordhash.h del /Q mkkeywordhash.exe keywordhash.h
-rmdir /Q/S .deps
-rmdir /Q/S .libs
-rmdir /Q/S quota2a
-rmdir /Q/S quota2b
-rmdir /Q/S quota2c
-rmdir /Q/S tsrc -rmdir /Q/S tsrc
del /Q .target_source del /Q .target_source
del /Q tclsqlite3.exe del /Q tclsqlite3.exe

View File

@ -657,6 +657,7 @@ clean:
rm -f lemon lempar.c parse.* sqlite*.tar.gz mkkeywordhash keywordhash.h rm -f lemon lempar.c parse.* sqlite*.tar.gz mkkeywordhash keywordhash.h
rm -f $(PUBLISH) rm -f $(PUBLISH)
rm -f *.da *.bb *.bbg gmon.out rm -f *.da *.bb *.bbg gmon.out
rm -rf quota2a quota2b quota2c
rm -rf tsrc target_source rm -rf tsrc target_source
rm -f testloadext.dll libtestloadext.so rm -f testloadext.dll libtestloadext.so
rm -f sqlite3.c fts?amal.c tclsqlite3.c rm -f sqlite3.c fts?amal.c tclsqlite3.c

View File

@ -1 +1 @@
3.7.10 3.7.12

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

View File

@ -33,6 +33,9 @@
/* Define to 1 if you have the `localtime_s' function. */ /* Define to 1 if you have the `localtime_s' function. */
#undef HAVE_LOCALTIME_S #undef HAVE_LOCALTIME_S
/* Define to 1 if you have the <malloc.h> header file. */
#undef HAVE_MALLOC_H
/* Define to 1 if you have the `malloc_usable_size' function. */ /* Define to 1 if you have the `malloc_usable_size' function. */
#undef HAVE_MALLOC_USABLE_SIZE #undef HAVE_MALLOC_USABLE_SIZE

21
configure vendored
View File

@ -1,6 +1,6 @@
#! /bin/sh #! /bin/sh
# Guess values for system-dependent variables and create Makefiles. # Guess values for system-dependent variables and create Makefiles.
# Generated by GNU Autoconf 2.62 for sqlite 3.7.10. # Generated by GNU Autoconf 2.62 for sqlite 3.7.12.
# #
# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001, # Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001,
# 2002, 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc. # 2002, 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
@ -743,8 +743,8 @@ SHELL=${CONFIG_SHELL-/bin/sh}
# Identity of this package. # Identity of this package.
PACKAGE_NAME='sqlite' PACKAGE_NAME='sqlite'
PACKAGE_TARNAME='sqlite' PACKAGE_TARNAME='sqlite'
PACKAGE_VERSION='3.7.10' PACKAGE_VERSION='3.7.12'
PACKAGE_STRING='sqlite 3.7.10' PACKAGE_STRING='sqlite 3.7.12'
PACKAGE_BUGREPORT='' PACKAGE_BUGREPORT=''
# Factoring default headers for most tests. # Factoring default headers for most tests.
@ -1485,7 +1485,7 @@ if test "$ac_init_help" = "long"; then
# Omit some internal or obsolete options to make the list less imposing. # Omit some internal or obsolete options to make the list less imposing.
# This message is too long to be a string in the A/UX 3.1 sh. # This message is too long to be a string in the A/UX 3.1 sh.
cat <<_ACEOF cat <<_ACEOF
\`configure' configures sqlite 3.7.10 to adapt to many kinds of systems. \`configure' configures sqlite 3.7.12 to adapt to many kinds of systems.
Usage: $0 [OPTION]... [VAR=VALUE]... Usage: $0 [OPTION]... [VAR=VALUE]...
@ -1550,7 +1550,7 @@ fi
if test -n "$ac_init_help"; then if test -n "$ac_init_help"; then
case $ac_init_help in case $ac_init_help in
short | recursive ) echo "Configuration of sqlite 3.7.10:";; short | recursive ) echo "Configuration of sqlite 3.7.12:";;
esac esac
cat <<\_ACEOF cat <<\_ACEOF
@ -1666,7 +1666,7 @@ fi
test -n "$ac_init_help" && exit $ac_status test -n "$ac_init_help" && exit $ac_status
if $ac_init_version; then if $ac_init_version; then
cat <<\_ACEOF cat <<\_ACEOF
sqlite configure 3.7.10 sqlite configure 3.7.12
generated by GNU Autoconf 2.62 generated by GNU Autoconf 2.62
Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001, Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001,
@ -1680,7 +1680,7 @@ cat >config.log <<_ACEOF
This file contains any messages produced by compilers while This file contains any messages produced by compilers while
running configure, to aid debugging if configure makes a mistake. running configure, to aid debugging if configure makes a mistake.
It was created by sqlite $as_me 3.7.10, which was It was created by sqlite $as_me 3.7.12, which was
generated by GNU Autoconf 2.62. Invocation command line was generated by GNU Autoconf 2.62. Invocation command line was
$ $0 $@ $ $0 $@
@ -11992,7 +11992,8 @@ fi
for ac_header in sys/types.h stdlib.h stdint.h inttypes.h
for ac_header in sys/types.h stdlib.h stdint.h inttypes.h malloc.h
do do
as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
@ -14031,7 +14032,7 @@ exec 6>&1
# report actual input values of CONFIG_FILES etc. instead of their # report actual input values of CONFIG_FILES etc. instead of their
# values after options handling. # values after options handling.
ac_log=" ac_log="
This file was extended by sqlite $as_me 3.7.10, which was This file was extended by sqlite $as_me 3.7.12, which was
generated by GNU Autoconf 2.62. Invocation command line was generated by GNU Autoconf 2.62. Invocation command line was
CONFIG_FILES = $CONFIG_FILES CONFIG_FILES = $CONFIG_FILES
@ -14084,7 +14085,7 @@ Report bugs to <bug-autoconf@gnu.org>."
_ACEOF _ACEOF
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
ac_cs_version="\\ ac_cs_version="\\
sqlite config.status 3.7.10 sqlite config.status 3.7.12
configured by $0, generated by GNU Autoconf 2.62, configured by $0, generated by GNU Autoconf 2.62,
with options \\"`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`\\" with options \\"`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`\\"

View File

@ -122,7 +122,7 @@ AC_CHECK_TYPES([int8_t, int16_t, int32_t, int64_t, intptr_t, uint8_t,
######### #########
# Check for needed/wanted headers # Check for needed/wanted headers
AC_CHECK_HEADERS([sys/types.h stdlib.h stdint.h inttypes.h]) AC_CHECK_HEADERS([sys/types.h stdlib.h stdint.h inttypes.h malloc.h])
######### #########
# Figure out whether or not we have these functions # Figure out whether or not we have these functions

View File

@ -944,7 +944,7 @@ static int asyncFileControl(sqlite3_file *id, int op, void *pArg){
return SQLITE_OK; return SQLITE_OK;
} }
} }
return SQLITE_ERROR; return SQLITE_NOTFOUND;
} }
/* /*
@ -1044,15 +1044,18 @@ static int asyncOpen(
char *z; char *z;
int isAsyncOpen = doAsynchronousOpen(flags); int isAsyncOpen = doAsynchronousOpen(flags);
/* If zName is NULL, then the upper layer is requesting an anonymous file */ /* If zName is NULL, then the upper layer is requesting an anonymous file.
** Otherwise, allocate enough space to make a copy of the file name (along
** with the second nul-terminator byte required by xOpen).
*/
if( zName ){ if( zName ){
nName = (int)strlen(zName)+1; nName = (int)strlen(zName);
} }
nByte = ( nByte = (
sizeof(AsyncFileData) + /* AsyncFileData structure */ sizeof(AsyncFileData) + /* AsyncFileData structure */
2 * pVfs->szOsFile + /* AsyncFileData.pBaseRead and pBaseWrite */ 2 * pVfs->szOsFile + /* AsyncFileData.pBaseRead and pBaseWrite */
nName /* AsyncFileData.zName */ nName + 2 /* AsyncFileData.zName */
); );
z = sqlite3_malloc(nByte); z = sqlite3_malloc(nByte);
if( !z ){ if( !z ){

View File

@ -70,7 +70,7 @@
** A doclist is stored like this: ** A doclist is stored like this:
** **
** array { ** array {
** varint docid; ** varint docid; (delta from previous doclist)
** array { (position list for column 0) ** array { (position list for column 0)
** varint position; (2 more than the delta from previous position) ** varint position; (2 more than the delta from previous position)
** } ** }
@ -101,8 +101,8 @@
** at D signals the start of a new column; the 1 at E indicates that the ** at D signals the start of a new column; the 1 at E indicates that the
** new column is column number 1. There are two positions at 12 and 45 ** new column is column number 1. There are two positions at 12 and 45
** (14-2 and 35-2+12). The 0 at H indicate the end-of-document. The ** (14-2 and 35-2+12). The 0 at H indicate the end-of-document. The
** 234 at I is the next docid. It has one position 72 (72-2) and then ** 234 at I is the delta to next docid (357). It has one position 70
** terminates with the 0 at K. ** (72-2) and then terminates with the 0 at K.
** **
** A "position-list" is the list of positions for multiple columns for ** A "position-list" is the list of positions for multiple columns for
** a single docid. A "column-list" is the set of positions for a single ** a single docid. A "column-list" is the set of positions for a single
@ -286,10 +286,6 @@
** will eventually overtake the earlier data and knock it out. The ** will eventually overtake the earlier data and knock it out. The
** query logic likewise merges doclists so that newer data knocks out ** query logic likewise merges doclists so that newer data knocks out
** older data. ** older data.
**
** TODO(shess) Provide a VACUUM type operation to clear out all
** deletions and duplications. This would basically be a forced merge
** into a single segment.
*/ */
#include "fts3Int.h" #include "fts3Int.h"
@ -469,6 +465,7 @@ static int fts3DisconnectMethod(sqlite3_vtab *pVtab){
sqlite3_free(p->zReadExprlist); sqlite3_free(p->zReadExprlist);
sqlite3_free(p->zWriteExprlist); sqlite3_free(p->zWriteExprlist);
sqlite3_free(p->zContentTbl); sqlite3_free(p->zContentTbl);
sqlite3_free(p->zLanguageid);
/* Invoke the tokenizer destructor to free the tokenizer. */ /* Invoke the tokenizer destructor to free the tokenizer. */
p->pTokenizer->pModule->xDestroy(p->pTokenizer); p->pTokenizer->pModule->xDestroy(p->pTokenizer);
@ -545,7 +542,9 @@ static void fts3DeclareVtab(int *pRc, Fts3Table *p){
int rc; /* Return code */ int rc; /* Return code */
char *zSql; /* SQL statement passed to declare_vtab() */ char *zSql; /* SQL statement passed to declare_vtab() */
char *zCols; /* List of user defined columns */ char *zCols; /* List of user defined columns */
const char *zLanguageid;
zLanguageid = (p->zLanguageid ? p->zLanguageid : "__langid");
sqlite3_vtab_config(p->db, SQLITE_VTAB_CONSTRAINT_SUPPORT, 1); sqlite3_vtab_config(p->db, SQLITE_VTAB_CONSTRAINT_SUPPORT, 1);
/* Create a list of user columns for the virtual table */ /* Create a list of user columns for the virtual table */
@ -556,7 +555,8 @@ static void fts3DeclareVtab(int *pRc, Fts3Table *p){
/* Create the whole "CREATE TABLE" statement to pass to SQLite */ /* Create the whole "CREATE TABLE" statement to pass to SQLite */
zSql = sqlite3_mprintf( zSql = sqlite3_mprintf(
"CREATE TABLE x(%s %Q HIDDEN, docid HIDDEN)", zCols, p->zName "CREATE TABLE x(%s %Q HIDDEN, docid HIDDEN, %Q HIDDEN)",
zCols, p->zName, zLanguageid
); );
if( !zCols || !zSql ){ if( !zCols || !zSql ){
rc = SQLITE_NOMEM; rc = SQLITE_NOMEM;
@ -570,6 +570,18 @@ static void fts3DeclareVtab(int *pRc, Fts3Table *p){
} }
} }
/*
** Create the %_stat table if it does not already exist.
*/
void sqlite3Fts3CreateStatTable(int *pRc, Fts3Table *p){
fts3DbExec(pRc, p->db,
"CREATE TABLE IF NOT EXISTS %Q.'%q_stat'"
"(id INTEGER PRIMARY KEY, value BLOB);",
p->zDb, p->zName
);
if( (*pRc)==SQLITE_OK ) p->bHasStat = 1;
}
/* /*
** Create the backing store tables (%_content, %_segments and %_segdir) ** Create the backing store tables (%_content, %_segments and %_segdir)
** required by the FTS3 table passed as the only argument. This is done ** required by the FTS3 table passed as the only argument. This is done
@ -585,6 +597,7 @@ static int fts3CreateTables(Fts3Table *p){
sqlite3 *db = p->db; /* The database connection */ sqlite3 *db = p->db; /* The database connection */
if( p->zContentTbl==0 ){ if( p->zContentTbl==0 ){
const char *zLanguageid = p->zLanguageid;
char *zContentCols; /* Columns of %_content table */ char *zContentCols; /* Columns of %_content table */
/* Create a list of user columns for the content table */ /* Create a list of user columns for the content table */
@ -593,6 +606,9 @@ static int fts3CreateTables(Fts3Table *p){
char *z = p->azColumn[i]; char *z = p->azColumn[i];
zContentCols = sqlite3_mprintf("%z, 'c%d%q'", zContentCols, i, z); zContentCols = sqlite3_mprintf("%z, 'c%d%q'", zContentCols, i, z);
} }
if( zLanguageid && zContentCols ){
zContentCols = sqlite3_mprintf("%z, langid", zContentCols, zLanguageid);
}
if( zContentCols==0 ) rc = SQLITE_NOMEM; if( zContentCols==0 ) rc = SQLITE_NOMEM;
/* Create the content table */ /* Create the content table */
@ -626,11 +642,9 @@ static int fts3CreateTables(Fts3Table *p){
p->zDb, p->zName p->zDb, p->zName
); );
} }
assert( p->bHasStat==p->bFts4 );
if( p->bHasStat ){ if( p->bHasStat ){
fts3DbExec(&rc, db, sqlite3Fts3CreateStatTable(&rc, p);
"CREATE TABLE %Q.'%q_stat'(id INTEGER PRIMARY KEY, value BLOB);",
p->zDb, p->zName
);
} }
return rc; return rc;
} }
@ -737,7 +751,7 @@ static void fts3Appendf(
static char *fts3QuoteId(char const *zInput){ static char *fts3QuoteId(char const *zInput){
int nRet; int nRet;
char *zRet; char *zRet;
nRet = 2 + strlen(zInput)*2 + 1; nRet = 2 + (int)strlen(zInput)*2 + 1;
zRet = sqlite3_malloc(nRet); zRet = sqlite3_malloc(nRet);
if( zRet ){ if( zRet ){
int i; int i;
@ -792,14 +806,20 @@ static char *fts3ReadExprList(Fts3Table *p, const char *zFunc, int *pRc){
for(i=0; i<p->nColumn; i++){ for(i=0; i<p->nColumn; i++){
fts3Appendf(pRc, &zRet, ",%s(x.'c%d%q')", zFunction, i, p->azColumn[i]); fts3Appendf(pRc, &zRet, ",%s(x.'c%d%q')", zFunction, i, p->azColumn[i]);
} }
if( p->zLanguageid ){
fts3Appendf(pRc, &zRet, ", x.%Q", "langid");
}
sqlite3_free(zFree); sqlite3_free(zFree);
}else{ }else{
fts3Appendf(pRc, &zRet, "rowid"); fts3Appendf(pRc, &zRet, "rowid");
for(i=0; i<p->nColumn; i++){ for(i=0; i<p->nColumn; i++){
fts3Appendf(pRc, &zRet, ", x.'%q'", p->azColumn[i]); fts3Appendf(pRc, &zRet, ", x.'%q'", p->azColumn[i]);
} }
if( p->zLanguageid ){
fts3Appendf(pRc, &zRet, ", x.%Q", p->zLanguageid);
}
} }
fts3Appendf(pRc, &zRet, "FROM '%q'.'%q%s' AS x", fts3Appendf(pRc, &zRet, " FROM '%q'.'%q%s' AS x",
p->zDb, p->zDb,
(p->zContentTbl ? p->zContentTbl : p->zName), (p->zContentTbl ? p->zContentTbl : p->zName),
(p->zContentTbl ? "" : "_content") (p->zContentTbl ? "" : "_content")
@ -842,6 +862,9 @@ static char *fts3WriteExprList(Fts3Table *p, const char *zFunc, int *pRc){
for(i=0; i<p->nColumn; i++){ for(i=0; i<p->nColumn; i++){
fts3Appendf(pRc, &zRet, ",%s(?)", zFunction); fts3Appendf(pRc, &zRet, ",%s(?)", zFunction);
} }
if( p->zLanguageid ){
fts3Appendf(pRc, &zRet, ", ?");
}
sqlite3_free(zFree); sqlite3_free(zFree);
return zRet; return zRet;
} }
@ -984,7 +1007,7 @@ static int fts3ContentColumns(
nCol = sqlite3_column_count(pStmt); nCol = sqlite3_column_count(pStmt);
for(i=0; i<nCol; i++){ for(i=0; i<nCol; i++){
const char *zCol = sqlite3_column_name(pStmt, i); const char *zCol = sqlite3_column_name(pStmt, i);
nStr += strlen(zCol) + 1; nStr += (int)strlen(zCol) + 1;
} }
/* Allocate and populate the array to return. */ /* Allocate and populate the array to return. */
@ -995,7 +1018,7 @@ static int fts3ContentColumns(
char *p = (char *)&azCol[nCol]; char *p = (char *)&azCol[nCol];
for(i=0; i<nCol; i++){ for(i=0; i<nCol; i++){
const char *zCol = sqlite3_column_name(pStmt, i); const char *zCol = sqlite3_column_name(pStmt, i);
int n = strlen(zCol)+1; int n = (int)strlen(zCol)+1;
memcpy(p, zCol, n); memcpy(p, zCol, n);
azCol[i] = p; azCol[i] = p;
p += n; p += n;
@ -1057,6 +1080,7 @@ static int fts3InitVtab(
char *zCompress = 0; /* compress=? parameter (or NULL) */ char *zCompress = 0; /* compress=? parameter (or NULL) */
char *zUncompress = 0; /* uncompress=? parameter (or NULL) */ char *zUncompress = 0; /* uncompress=? parameter (or NULL) */
char *zContent = 0; /* content=? parameter (or NULL) */ char *zContent = 0; /* content=? parameter (or NULL) */
char *zLanguageid = 0; /* languageid=? parameter (or NULL) */
assert( strlen(argv[0])==4 ); assert( strlen(argv[0])==4 );
assert( (sqlite3_strnicmp(argv[0], "fts4", 4)==0 && isFts4) assert( (sqlite3_strnicmp(argv[0], "fts4", 4)==0 && isFts4)
@ -1106,7 +1130,8 @@ static int fts3InitVtab(
{ "compress", 8 }, /* 2 -> COMPRESS */ { "compress", 8 }, /* 2 -> COMPRESS */
{ "uncompress", 10 }, /* 3 -> UNCOMPRESS */ { "uncompress", 10 }, /* 3 -> UNCOMPRESS */
{ "order", 5 }, /* 4 -> ORDER */ { "order", 5 }, /* 4 -> ORDER */
{ "content", 7 } /* 5 -> CONTENT */ { "content", 7 }, /* 5 -> CONTENT */
{ "languageid", 10 } /* 6 -> LANGUAGEID */
}; };
int iOpt; int iOpt;
@ -1160,12 +1185,18 @@ static int fts3InitVtab(
bDescIdx = (zVal[0]=='d' || zVal[0]=='D'); bDescIdx = (zVal[0]=='d' || zVal[0]=='D');
break; break;
default: /* CONTENT */ case 5: /* CONTENT */
assert( iOpt==5 ); sqlite3_free(zContent);
sqlite3_free(zUncompress);
zContent = zVal; zContent = zVal;
zVal = 0; zVal = 0;
break; break;
case 6: /* LANGUAGEID */
assert( iOpt==6 );
sqlite3_free(zLanguageid);
zLanguageid = zVal;
zVal = 0;
break;
} }
} }
sqlite3_free(zVal); sqlite3_free(zVal);
@ -1195,8 +1226,21 @@ static int fts3InitVtab(
sqlite3_free((void*)aCol); sqlite3_free((void*)aCol);
aCol = 0; aCol = 0;
rc = fts3ContentColumns(db, argv[1], zContent, &aCol, &nCol, &nString); rc = fts3ContentColumns(db, argv[1], zContent, &aCol, &nCol, &nString);
/* If a languageid= option was specified, remove the language id
** column from the aCol[] array. */
if( rc==SQLITE_OK && zLanguageid ){
int j;
for(j=0; j<nCol; j++){
if( sqlite3_stricmp(zLanguageid, aCol[j])==0 ){
int k;
for(k=j; k<nCol; k++) aCol[k] = aCol[k+1];
nCol--;
break;
}
}
}
} }
assert( rc!=SQLITE_OK || nCol>0 );
} }
if( rc!=SQLITE_OK ) goto fts3_init_out; if( rc!=SQLITE_OK ) goto fts3_init_out;
@ -1241,9 +1285,13 @@ static int fts3InitVtab(
p->nMaxPendingData = FTS3_MAX_PENDING_DATA; p->nMaxPendingData = FTS3_MAX_PENDING_DATA;
p->bHasDocsize = (isFts4 && bNoDocsize==0); p->bHasDocsize = (isFts4 && bNoDocsize==0);
p->bHasStat = isFts4; p->bHasStat = isFts4;
p->bFts4 = isFts4;
p->bDescIdx = bDescIdx; p->bDescIdx = bDescIdx;
p->bAutoincrmerge = 0xff; /* 0xff means setting unknown */
p->zContentTbl = zContent; p->zContentTbl = zContent;
p->zLanguageid = zLanguageid;
zContent = 0; zContent = 0;
zLanguageid = 0;
TESTONLY( p->inTransaction = -1 ); TESTONLY( p->inTransaction = -1 );
TESTONLY( p->mxSavepoint = -1 ); TESTONLY( p->mxSavepoint = -1 );
@ -1292,6 +1340,16 @@ static int fts3InitVtab(
rc = fts3CreateTables(p); rc = fts3CreateTables(p);
} }
/* Check to see if a legacy fts3 table has been "upgraded" by the
** addition of a %_stat table so that it can use incremental merge.
*/
if( !isFts4 && !isCreate ){
int rc2 = SQLITE_OK;
fts3DbExec(&rc2, db, "SELECT 1 FROM %Q.'%q_stat' WHERE id=2",
p->zDb, p->zName);
if( rc2==SQLITE_OK ) p->bHasStat = 1;
}
/* Figure out the page-size for the database. This is required in order to /* Figure out the page-size for the database. This is required in order to
** estimate the cost of loading large doclists from the database. */ ** estimate the cost of loading large doclists from the database. */
fts3DatabasePageSize(&rc, p); fts3DatabasePageSize(&rc, p);
@ -1306,6 +1364,7 @@ fts3_init_out:
sqlite3_free(zCompress); sqlite3_free(zCompress);
sqlite3_free(zUncompress); sqlite3_free(zUncompress);
sqlite3_free(zContent); sqlite3_free(zContent);
sqlite3_free(zLanguageid);
sqlite3_free((void *)aCol); sqlite3_free((void *)aCol);
if( rc!=SQLITE_OK ){ if( rc!=SQLITE_OK ){
if( p ){ if( p ){
@ -1357,6 +1416,7 @@ static int fts3BestIndexMethod(sqlite3_vtab *pVTab, sqlite3_index_info *pInfo){
Fts3Table *p = (Fts3Table *)pVTab; Fts3Table *p = (Fts3Table *)pVTab;
int i; /* Iterator variable */ int i; /* Iterator variable */
int iCons = -1; /* Index of constraint to use */ int iCons = -1; /* Index of constraint to use */
int iLangidCons = -1; /* Index of langid=x constraint, if present */
/* By default use a full table scan. This is an expensive option, /* By default use a full table scan. This is an expensive option,
** so search through the constraints to see if a more efficient ** so search through the constraints to see if a more efficient
@ -1369,7 +1429,8 @@ static int fts3BestIndexMethod(sqlite3_vtab *pVTab, sqlite3_index_info *pInfo){
if( pCons->usable==0 ) continue; if( pCons->usable==0 ) continue;
/* A direct lookup on the rowid or docid column. Assign a cost of 1.0. */ /* A direct lookup on the rowid or docid column. Assign a cost of 1.0. */
if( pCons->op==SQLITE_INDEX_CONSTRAINT_EQ if( iCons<0
&& pCons->op==SQLITE_INDEX_CONSTRAINT_EQ
&& (pCons->iColumn<0 || pCons->iColumn==p->nColumn+1 ) && (pCons->iColumn<0 || pCons->iColumn==p->nColumn+1 )
){ ){
pInfo->idxNum = FTS3_DOCID_SEARCH; pInfo->idxNum = FTS3_DOCID_SEARCH;
@ -1392,7 +1453,13 @@ static int fts3BestIndexMethod(sqlite3_vtab *pVTab, sqlite3_index_info *pInfo){
pInfo->idxNum = FTS3_FULLTEXT_SEARCH + pCons->iColumn; pInfo->idxNum = FTS3_FULLTEXT_SEARCH + pCons->iColumn;
pInfo->estimatedCost = 2.0; pInfo->estimatedCost = 2.0;
iCons = i; iCons = i;
break; }
/* Equality constraint on the langid column */
if( pCons->op==SQLITE_INDEX_CONSTRAINT_EQ
&& pCons->iColumn==p->nColumn + 2
){
iLangidCons = i;
} }
} }
@ -1400,6 +1467,9 @@ static int fts3BestIndexMethod(sqlite3_vtab *pVTab, sqlite3_index_info *pInfo){
pInfo->aConstraintUsage[iCons].argvIndex = 1; pInfo->aConstraintUsage[iCons].argvIndex = 1;
pInfo->aConstraintUsage[iCons].omit = 1; pInfo->aConstraintUsage[iCons].omit = 1;
} }
if( iLangidCons>=0 ){
pInfo->aConstraintUsage[iLangidCons].argvIndex = 2;
}
/* Regardless of the strategy selected, FTS can deliver rows in rowid (or /* Regardless of the strategy selected, FTS can deliver rows in rowid (or
** docid) order. Both ascending and descending are possible. ** docid) order. Both ascending and descending are possible.
@ -2283,7 +2353,7 @@ static int fts3DoclistOrMerge(
} }
*paOut = aOut; *paOut = aOut;
*pnOut = (p-aOut); *pnOut = (int)(p-aOut);
assert( *pnOut<=n1+n2+FTS3_VARINT_MAX-1 ); assert( *pnOut<=n1+n2+FTS3_VARINT_MAX-1 );
return SQLITE_OK; return SQLITE_OK;
} }
@ -2347,7 +2417,7 @@ static void fts3DoclistPhraseMerge(
} }
} }
*pnRight = p - aOut; *pnRight = (int)(p - aOut);
} }
/* /*
@ -2549,6 +2619,7 @@ static int fts3SegReaderCursorAppend(
*/ */
static int fts3SegReaderCursor( static int fts3SegReaderCursor(
Fts3Table *p, /* FTS3 table handle */ Fts3Table *p, /* FTS3 table handle */
int iLangid, /* Language id */
int iIndex, /* Index to search (from 0 to p->nIndex-1) */ int iIndex, /* Index to search (from 0 to p->nIndex-1) */
int iLevel, /* Level of segments to scan */ int iLevel, /* Level of segments to scan */
const char *zTerm, /* Term to query for */ const char *zTerm, /* Term to query for */
@ -2577,7 +2648,7 @@ static int fts3SegReaderCursor(
if( iLevel!=FTS3_SEGCURSOR_PENDING ){ if( iLevel!=FTS3_SEGCURSOR_PENDING ){
if( rc==SQLITE_OK ){ if( rc==SQLITE_OK ){
rc = sqlite3Fts3AllSegdirs(p, iIndex, iLevel, &pStmt); rc = sqlite3Fts3AllSegdirs(p, iLangid, iIndex, iLevel, &pStmt);
} }
while( rc==SQLITE_OK && SQLITE_ROW==(rc = sqlite3_step(pStmt)) ){ while( rc==SQLITE_OK && SQLITE_ROW==(rc = sqlite3_step(pStmt)) ){
@ -2600,7 +2671,9 @@ static int fts3SegReaderCursor(
} }
rc = sqlite3Fts3SegReaderNew(pCsr->nSegment+1, rc = sqlite3Fts3SegReaderNew(pCsr->nSegment+1,
iStartBlock, iLeavesEndBlock, iEndBlock, zRoot, nRoot, &pSeg (isPrefix==0 && isScan==0),
iStartBlock, iLeavesEndBlock,
iEndBlock, zRoot, nRoot, &pSeg
); );
if( rc!=SQLITE_OK ) goto finished; if( rc!=SQLITE_OK ) goto finished;
rc = fts3SegReaderCursorAppend(pCsr, pSeg); rc = fts3SegReaderCursorAppend(pCsr, pSeg);
@ -2620,6 +2693,7 @@ static int fts3SegReaderCursor(
*/ */
int sqlite3Fts3SegReaderCursor( int sqlite3Fts3SegReaderCursor(
Fts3Table *p, /* FTS3 table handle */ Fts3Table *p, /* FTS3 table handle */
int iLangid, /* Language-id to search */
int iIndex, /* Index to search (from 0 to p->nIndex-1) */ int iIndex, /* Index to search (from 0 to p->nIndex-1) */
int iLevel, /* Level of segments to scan */ int iLevel, /* Level of segments to scan */
const char *zTerm, /* Term to query for */ const char *zTerm, /* Term to query for */
@ -2637,14 +2711,9 @@ int sqlite3Fts3SegReaderCursor(
assert( FTS3_SEGCURSOR_ALL<0 && FTS3_SEGCURSOR_PENDING<0 ); assert( FTS3_SEGCURSOR_ALL<0 && FTS3_SEGCURSOR_PENDING<0 );
assert( isPrefix==0 || isScan==0 ); assert( isPrefix==0 || isScan==0 );
/* "isScan" is only set to true by the ft4aux module, an ordinary
** full-text tables. */
assert( isScan==0 || p->aIndex==0 );
memset(pCsr, 0, sizeof(Fts3MultiSegReader)); memset(pCsr, 0, sizeof(Fts3MultiSegReader));
return fts3SegReaderCursor( return fts3SegReaderCursor(
p, iIndex, iLevel, zTerm, nTerm, isPrefix, isScan, pCsr p, iLangid, iIndex, iLevel, zTerm, nTerm, isPrefix, isScan, pCsr
); );
} }
@ -2656,11 +2725,14 @@ int sqlite3Fts3SegReaderCursor(
*/ */
static int fts3SegReaderCursorAddZero( static int fts3SegReaderCursorAddZero(
Fts3Table *p, /* FTS virtual table handle */ Fts3Table *p, /* FTS virtual table handle */
int iLangid,
const char *zTerm, /* Term to scan doclist of */ const char *zTerm, /* Term to scan doclist of */
int nTerm, /* Number of bytes in zTerm */ int nTerm, /* Number of bytes in zTerm */
Fts3MultiSegReader *pCsr /* Fts3MultiSegReader to modify */ Fts3MultiSegReader *pCsr /* Fts3MultiSegReader to modify */
){ ){
return fts3SegReaderCursor(p, 0, FTS3_SEGCURSOR_ALL, zTerm, nTerm, 0, 0,pCsr); return fts3SegReaderCursor(p,
iLangid, 0, FTS3_SEGCURSOR_ALL, zTerm, nTerm, 0, 0,pCsr
);
} }
/* /*
@ -2696,8 +2768,9 @@ static int fts3TermSegReaderCursor(
for(i=1; bFound==0 && i<p->nIndex; i++){ for(i=1; bFound==0 && i<p->nIndex; i++){
if( p->aIndex[i].nPrefix==nTerm ){ if( p->aIndex[i].nPrefix==nTerm ){
bFound = 1; bFound = 1;
rc = sqlite3Fts3SegReaderCursor( rc = sqlite3Fts3SegReaderCursor(p, pCsr->iLangid,
p, i, FTS3_SEGCURSOR_ALL, zTerm, nTerm, 0, 0, pSegcsr); i, FTS3_SEGCURSOR_ALL, zTerm, nTerm, 0, 0, pSegcsr
);
pSegcsr->bLookup = 1; pSegcsr->bLookup = 1;
} }
} }
@ -2705,19 +2778,21 @@ static int fts3TermSegReaderCursor(
for(i=1; bFound==0 && i<p->nIndex; i++){ for(i=1; bFound==0 && i<p->nIndex; i++){
if( p->aIndex[i].nPrefix==nTerm+1 ){ if( p->aIndex[i].nPrefix==nTerm+1 ){
bFound = 1; bFound = 1;
rc = sqlite3Fts3SegReaderCursor( rc = sqlite3Fts3SegReaderCursor(p, pCsr->iLangid,
p, i, FTS3_SEGCURSOR_ALL, zTerm, nTerm, 1, 0, pSegcsr i, FTS3_SEGCURSOR_ALL, zTerm, nTerm, 1, 0, pSegcsr
); );
if( rc==SQLITE_OK ){ if( rc==SQLITE_OK ){
rc = fts3SegReaderCursorAddZero(p, zTerm, nTerm, pSegcsr); rc = fts3SegReaderCursorAddZero(
p, pCsr->iLangid, zTerm, nTerm, pSegcsr
);
} }
} }
} }
} }
if( bFound==0 ){ if( bFound==0 ){
rc = sqlite3Fts3SegReaderCursor( rc = sqlite3Fts3SegReaderCursor(p, pCsr->iLangid,
p, 0, FTS3_SEGCURSOR_ALL, zTerm, nTerm, isPrefix, 0, pSegcsr 0, FTS3_SEGCURSOR_ALL, zTerm, nTerm, isPrefix, 0, pSegcsr
); );
pSegcsr->bLookup = !isPrefix; pSegcsr->bLookup = !isPrefix;
} }
@ -2872,7 +2947,7 @@ static int fts3FilterMethod(
UNUSED_PARAMETER(nVal); UNUSED_PARAMETER(nVal);
assert( idxNum>=0 && idxNum<=(FTS3_FULLTEXT_SEARCH+p->nColumn) ); assert( idxNum>=0 && idxNum<=(FTS3_FULLTEXT_SEARCH+p->nColumn) );
assert( nVal==0 || nVal==1 ); assert( nVal==0 || nVal==1 || nVal==2 );
assert( (nVal==0)==(idxNum==FTS3_FULLSCAN_SEARCH) ); assert( (nVal==0)==(idxNum==FTS3_FULLSCAN_SEARCH) );
assert( p->pSegments==0 ); assert( p->pSegments==0 );
@ -2897,8 +2972,11 @@ static int fts3FilterMethod(
return SQLITE_NOMEM; return SQLITE_NOMEM;
} }
rc = sqlite3Fts3ExprParse(p->pTokenizer, p->azColumn, p->bHasStat, pCsr->iLangid = 0;
p->nColumn, iCol, zQuery, -1, &pCsr->pExpr if( nVal==2 ) pCsr->iLangid = sqlite3_value_int(apVal[1]);
rc = sqlite3Fts3ExprParse(p->pTokenizer, pCsr->iLangid,
p->azColumn, p->bFts4, p->nColumn, iCol, zQuery, -1, &pCsr->pExpr
); );
if( rc!=SQLITE_OK ){ if( rc!=SQLITE_OK ){
if( rc==SQLITE_ERROR ){ if( rc==SQLITE_ERROR ){
@ -2969,10 +3047,17 @@ static int fts3RowidMethod(sqlite3_vtab_cursor *pCursor, sqlite_int64 *pRowid){
/* /*
** This is the xColumn method, called by SQLite to request a value from ** This is the xColumn method, called by SQLite to request a value from
** the row that the supplied cursor currently points to. ** the row that the supplied cursor currently points to.
**
** If:
**
** (iCol < p->nColumn) -> The value of the iCol'th user column.
** (iCol == p->nColumn) -> Magic column with the same name as the table.
** (iCol == p->nColumn+1) -> Docid column
** (iCol == p->nColumn+2) -> Langid column
*/ */
static int fts3ColumnMethod( static int fts3ColumnMethod(
sqlite3_vtab_cursor *pCursor, /* Cursor to retrieve value from */ sqlite3_vtab_cursor *pCursor, /* Cursor to retrieve value from */
sqlite3_context *pContext, /* Context for sqlite3_result_xxx() calls */ sqlite3_context *pCtx, /* Context for sqlite3_result_xxx() calls */
int iCol /* Index of column to read value from */ int iCol /* Index of column to read value from */
){ ){
int rc = SQLITE_OK; /* Return Code */ int rc = SQLITE_OK; /* Return Code */
@ -2980,22 +3065,34 @@ static int fts3ColumnMethod(
Fts3Table *p = (Fts3Table *)pCursor->pVtab; Fts3Table *p = (Fts3Table *)pCursor->pVtab;
/* The column value supplied by SQLite must be in range. */ /* The column value supplied by SQLite must be in range. */
assert( iCol>=0 && iCol<=p->nColumn+1 ); assert( iCol>=0 && iCol<=p->nColumn+2 );
if( iCol==p->nColumn+1 ){ if( iCol==p->nColumn+1 ){
/* This call is a request for the "docid" column. Since "docid" is an /* This call is a request for the "docid" column. Since "docid" is an
** alias for "rowid", use the xRowid() method to obtain the value. ** alias for "rowid", use the xRowid() method to obtain the value.
*/ */
sqlite3_result_int64(pContext, pCsr->iPrevId); sqlite3_result_int64(pCtx, pCsr->iPrevId);
}else if( iCol==p->nColumn ){ }else if( iCol==p->nColumn ){
/* The extra column whose name is the same as the table. /* The extra column whose name is the same as the table.
** Return a blob which is a pointer to the cursor. ** Return a blob which is a pointer to the cursor. */
*/ sqlite3_result_blob(pCtx, &pCsr, sizeof(pCsr), SQLITE_TRANSIENT);
sqlite3_result_blob(pContext, &pCsr, sizeof(pCsr), SQLITE_TRANSIENT); }else if( iCol==p->nColumn+2 && pCsr->pExpr ){
sqlite3_result_int64(pCtx, pCsr->iLangid);
}else{ }else{
/* The requested column is either a user column (one that contains
** indexed data), or the language-id column. */
rc = fts3CursorSeek(0, pCsr); rc = fts3CursorSeek(0, pCsr);
if( rc==SQLITE_OK && sqlite3_data_count(pCsr->pStmt)>(iCol+1) ){
sqlite3_result_value(pContext, sqlite3_column_value(pCsr->pStmt, iCol+1)); if( rc==SQLITE_OK ){
if( iCol==p->nColumn+2 ){
int iLangid = 0;
if( p->zLanguageid ){
iLangid = sqlite3_column_int(pCsr->pStmt, p->nColumn+1);
}
sqlite3_result_int(pCtx, iLangid);
}else if( sqlite3_data_count(pCsr->pStmt)>(iCol+1) ){
sqlite3_result_value(pCtx, sqlite3_column_value(pCsr->pStmt, iCol+1));
}
} }
} }
@ -3022,8 +3119,42 @@ static int fts3UpdateMethod(
** hash-table to the database. ** hash-table to the database.
*/ */
static int fts3SyncMethod(sqlite3_vtab *pVtab){ static int fts3SyncMethod(sqlite3_vtab *pVtab){
int rc = sqlite3Fts3PendingTermsFlush((Fts3Table *)pVtab);
sqlite3Fts3SegmentsClose((Fts3Table *)pVtab); /* Following an incremental-merge operation, assuming that the input
** segments are not completely consumed (the usual case), they are updated
** in place to remove the entries that have already been merged. This
** involves updating the leaf block that contains the smallest unmerged
** entry and each block (if any) between the leaf and the root node. So
** if the height of the input segment b-trees is N, and input segments
** are merged eight at a time, updating the input segments at the end
** of an incremental-merge requires writing (8*(1+N)) blocks. N is usually
** small - often between 0 and 2. So the overhead of the incremental
** merge is somewhere between 8 and 24 blocks. To avoid this overhead
** dwarfing the actual productive work accomplished, the incremental merge
** is only attempted if it will write at least 64 leaf blocks. Hence
** nMinMerge.
**
** Of course, updating the input segments also involves deleting a bunch
** of blocks from the segments table. But this is not considered overhead
** as it would also be required by a crisis-merge that used the same input
** segments.
*/
const u32 nMinMerge = 64; /* Minimum amount of incr-merge work to do */
Fts3Table *p = (Fts3Table*)pVtab;
int rc = sqlite3Fts3PendingTermsFlush(p);
if( rc==SQLITE_OK && p->bAutoincrmerge==1 && p->nLeafAdd>(nMinMerge/16) ){
int mxLevel = 0; /* Maximum relative level value in db */
int A; /* Incr-merge parameter A */
rc = sqlite3Fts3MaxLevel(p, &mxLevel);
assert( rc==SQLITE_OK || mxLevel==0 );
A = p->nLeafAdd * mxLevel;
A += (A/2);
if( A>(int)nMinMerge ) rc = sqlite3Fts3Incrmerge(p, A, 8);
}
sqlite3Fts3SegmentsClose(p);
return rc; return rc;
} }
@ -3031,13 +3162,14 @@ static int fts3SyncMethod(sqlite3_vtab *pVtab){
** Implementation of xBegin() method. This is a no-op. ** Implementation of xBegin() method. This is a no-op.
*/ */
static int fts3BeginMethod(sqlite3_vtab *pVtab){ static int fts3BeginMethod(sqlite3_vtab *pVtab){
TESTONLY( Fts3Table *p = (Fts3Table*)pVtab ); Fts3Table *p = (Fts3Table*)pVtab;
UNUSED_PARAMETER(pVtab); UNUSED_PARAMETER(pVtab);
assert( p->pSegments==0 ); assert( p->pSegments==0 );
assert( p->nPendingData==0 ); assert( p->nPendingData==0 );
assert( p->inTransaction!=1 ); assert( p->inTransaction!=1 );
TESTONLY( p->inTransaction = 1 ); TESTONLY( p->inTransaction = 1 );
TESTONLY( p->mxSavepoint = -1; ); TESTONLY( p->mxSavepoint = -1; );
p->nLeafAdd = 0;
return SQLITE_OK; return SQLITE_OK;
} }
@ -3332,11 +3464,15 @@ static int fts3RenameMethod(
** Flush the contents of the pending-terms table to disk. ** Flush the contents of the pending-terms table to disk.
*/ */
static int fts3SavepointMethod(sqlite3_vtab *pVtab, int iSavepoint){ static int fts3SavepointMethod(sqlite3_vtab *pVtab, int iSavepoint){
int rc = SQLITE_OK;
UNUSED_PARAMETER(iSavepoint); UNUSED_PARAMETER(iSavepoint);
assert( ((Fts3Table *)pVtab)->inTransaction ); assert( ((Fts3Table *)pVtab)->inTransaction );
assert( ((Fts3Table *)pVtab)->mxSavepoint < iSavepoint ); assert( ((Fts3Table *)pVtab)->mxSavepoint < iSavepoint );
TESTONLY( ((Fts3Table *)pVtab)->mxSavepoint = iSavepoint ); TESTONLY( ((Fts3Table *)pVtab)->mxSavepoint = iSavepoint );
return fts3SyncMethod(pVtab); if( ((Fts3Table *)pVtab)->bIgnoreSavepoint==0 ){
rc = fts3SyncMethod(pVtab);
}
return rc;
} }
/* /*
@ -3696,7 +3832,7 @@ static int fts3EvalDeferredPhrase(Fts3Cursor *pCsr, Fts3Phrase *pPhrase){
fts3PoslistPhraseMerge(&aOut, iToken-iPrev, 0, 1, &p1, &p2); fts3PoslistPhraseMerge(&aOut, iToken-iPrev, 0, 1, &p1, &p2);
sqlite3_free(aPoslist); sqlite3_free(aPoslist);
aPoslist = pList; aPoslist = pList;
nPoslist = aOut - aPoslist; nPoslist = (int)(aOut - aPoslist);
if( nPoslist==0 ){ if( nPoslist==0 ){
sqlite3_free(aPoslist); sqlite3_free(aPoslist);
pPhrase->doclist.pList = 0; pPhrase->doclist.pList = 0;
@ -3740,7 +3876,7 @@ static int fts3EvalDeferredPhrase(Fts3Cursor *pCsr, Fts3Phrase *pPhrase){
pPhrase->doclist.pList = aOut; pPhrase->doclist.pList = aOut;
if( fts3PoslistPhraseMerge(&aOut, nDistance, 0, 1, &p1, &p2) ){ if( fts3PoslistPhraseMerge(&aOut, nDistance, 0, 1, &p1, &p2) ){
pPhrase->doclist.bFreeList = 1; pPhrase->doclist.bFreeList = 1;
pPhrase->doclist.nList = (aOut - pPhrase->doclist.pList); pPhrase->doclist.nList = (int)(aOut - pPhrase->doclist.pList);
}else{ }else{
sqlite3_free(aOut); sqlite3_free(aOut);
pPhrase->doclist.pList = 0; pPhrase->doclist.pList = 0;
@ -3809,7 +3945,7 @@ void sqlite3Fts3DoclistPrev(
int nDoclist, /* Length of aDoclist in bytes */ int nDoclist, /* Length of aDoclist in bytes */
char **ppIter, /* IN/OUT: Iterator pointer */ char **ppIter, /* IN/OUT: Iterator pointer */
sqlite3_int64 *piDocid, /* IN/OUT: Docid pointer */ sqlite3_int64 *piDocid, /* IN/OUT: Docid pointer */
int *pnList, /* IN/OUT: List length pointer */ int *pnList, /* OUT: List length pointer */
u8 *pbEof /* OUT: End-of-file flag */ u8 *pbEof /* OUT: End-of-file flag */
){ ){
char *p = *ppIter; char *p = *ppIter;
@ -3836,7 +3972,7 @@ void sqlite3Fts3DoclistPrev(
iMul = (bDescIdx ? -1 : 1); iMul = (bDescIdx ? -1 : 1);
} }
*pnList = pEnd - pNext; *pnList = (int)(pEnd - pNext);
*ppIter = pNext; *ppIter = pNext;
*piDocid = iDocid; *piDocid = iDocid;
}else{ }else{
@ -3850,12 +3986,47 @@ void sqlite3Fts3DoclistPrev(
}else{ }else{
char *pSave = p; char *pSave = p;
fts3ReversePoslist(aDoclist, &p); fts3ReversePoslist(aDoclist, &p);
*pnList = (pSave - p); *pnList = (int)(pSave - p);
} }
*ppIter = p; *ppIter = p;
} }
} }
/*
** Iterate forwards through a doclist.
*/
void sqlite3Fts3DoclistNext(
int bDescIdx, /* True if the doclist is desc */
char *aDoclist, /* Pointer to entire doclist */
int nDoclist, /* Length of aDoclist in bytes */
char **ppIter, /* IN/OUT: Iterator pointer */
sqlite3_int64 *piDocid, /* IN/OUT: Docid pointer */
u8 *pbEof /* OUT: End-of-file flag */
){
char *p = *ppIter;
assert( nDoclist>0 );
assert( *pbEof==0 );
assert( p || *piDocid==0 );
assert( !p || (p>=aDoclist && p<=&aDoclist[nDoclist]) );
if( p==0 ){
p = aDoclist;
p += sqlite3Fts3GetVarint(p, piDocid);
}else{
fts3PoslistCopy(0, &p);
if( p>=&aDoclist[nDoclist] ){
*pbEof = 1;
}else{
sqlite3_int64 iVar;
p += sqlite3Fts3GetVarint(p, &iVar);
*piDocid += ((bDescIdx ? -1 : 1) * iVar);
}
}
*ppIter = p;
}
/* /*
** Attempt to move the phrase iterator to point to the next matching docid. ** Attempt to move the phrase iterator to point to the next matching docid.
** If an error occurs, return an SQLite error code. Otherwise, return ** If an error occurs, return an SQLite error code. Otherwise, return
@ -3910,7 +4081,7 @@ static int fts3EvalPhraseNext(
} }
pDL->pList = pIter; pDL->pList = pIter;
fts3PoslistCopy(0, &pIter); fts3PoslistCopy(0, &pIter);
pDL->nList = (pIter - pDL->pList); pDL->nList = (int)(pIter - pDL->pList);
/* pIter now points just past the 0x00 that terminates the position- /* pIter now points just past the 0x00 that terminates the position-
** list for document pDL->iDocid. However, if this position-list was ** list for document pDL->iDocid. However, if this position-list was
@ -4251,7 +4422,7 @@ static int fts3EvalStart(Fts3Cursor *pCsr){
fts3EvalAllocateReaders(pCsr, pCsr->pExpr, &nToken, &nOr, &rc); fts3EvalAllocateReaders(pCsr, pCsr->pExpr, &nToken, &nOr, &rc);
/* Determine which, if any, tokens in the expression should be deferred. */ /* Determine which, if any, tokens in the expression should be deferred. */
if( rc==SQLITE_OK && nToken>1 && pTab->bHasStat ){ if( rc==SQLITE_OK && nToken>1 && pTab->bFts4 ){
Fts3TokenAndCost *aTC; Fts3TokenAndCost *aTC;
Fts3Expr **apOr; Fts3Expr **apOr;
aTC = (Fts3TokenAndCost *)sqlite3_malloc( aTC = (Fts3TokenAndCost *)sqlite3_malloc(
@ -4268,8 +4439,8 @@ static int fts3EvalStart(Fts3Cursor *pCsr){
Fts3Expr **ppOr = apOr; Fts3Expr **ppOr = apOr;
fts3EvalTokenCosts(pCsr, 0, pCsr->pExpr, &pTC, &ppOr, &rc); fts3EvalTokenCosts(pCsr, 0, pCsr->pExpr, &pTC, &ppOr, &rc);
nToken = pTC-aTC; nToken = (int)(pTC-aTC);
nOr = ppOr-apOr; nOr = (int)(ppOr-apOr);
if( rc==SQLITE_OK ){ if( rc==SQLITE_OK ){
rc = fts3EvalSelectDeferred(pCsr, 0, aTC, nToken); rc = fts3EvalSelectDeferred(pCsr, 0, aTC, nToken);
@ -4341,7 +4512,7 @@ static int fts3EvalNearTrim(
&pOut, aTmp, nParam1, nParam2, paPoslist, &p2 &pOut, aTmp, nParam1, nParam2, paPoslist, &p2
); );
if( res ){ if( res ){
nNew = (pOut - pPhrase->doclist.pList) - 1; nNew = (int)(pOut - pPhrase->doclist.pList) - 1;
assert( pPhrase->doclist.pList[nNew]=='\0' ); assert( pPhrase->doclist.pList[nNew]=='\0' );
assert( nNew<=pPhrase->doclist.nList && nNew>0 ); assert( nNew<=pPhrase->doclist.nList && nNew>0 );
memset(&pPhrase->doclist.pList[nNew], 0, pPhrase->doclist.nList - nNew); memset(&pPhrase->doclist.pList[nNew], 0, pPhrase->doclist.nList - nNew);
@ -5011,26 +5182,87 @@ int sqlite3Fts3EvalPhraseStats(
** This function works regardless of whether or not the phrase is deferred, ** This function works regardless of whether or not the phrase is deferred,
** incremental, or neither. ** incremental, or neither.
*/ */
char *sqlite3Fts3EvalPhrasePoslist( int sqlite3Fts3EvalPhrasePoslist(
Fts3Cursor *pCsr, /* FTS3 cursor object */ Fts3Cursor *pCsr, /* FTS3 cursor object */
Fts3Expr *pExpr, /* Phrase to return doclist for */ Fts3Expr *pExpr, /* Phrase to return doclist for */
int iCol /* Column to return position list for */ int iCol, /* Column to return position list for */
char **ppOut /* OUT: Pointer to position list */
){ ){
Fts3Phrase *pPhrase = pExpr->pPhrase; Fts3Phrase *pPhrase = pExpr->pPhrase;
Fts3Table *pTab = (Fts3Table *)pCsr->base.pVtab; Fts3Table *pTab = (Fts3Table *)pCsr->base.pVtab;
char *pIter = pPhrase->doclist.pList; char *pIter;
int iThis; int iThis;
sqlite3_int64 iDocid;
/* If this phrase is applies specifically to some column other than
** column iCol, return a NULL pointer. */
*ppOut = 0;
assert( iCol>=0 && iCol<pTab->nColumn ); assert( iCol>=0 && iCol<pTab->nColumn );
if( !pIter if( (pPhrase->iColumn<pTab->nColumn && pPhrase->iColumn!=iCol) ){
|| pExpr->bEof return SQLITE_OK;
|| pExpr->iDocid!=pCsr->iPrevId
|| (pPhrase->iColumn<pTab->nColumn && pPhrase->iColumn!=iCol)
){
return 0;
} }
assert( pPhrase->doclist.nList>0 ); iDocid = pExpr->iDocid;
pIter = pPhrase->doclist.pList;
if( iDocid!=pCsr->iPrevId || pExpr->bEof ){
int bDescDoclist = pTab->bDescIdx; /* For DOCID_CMP macro */
int bOr = 0;
u8 bEof = 0;
Fts3Expr *p;
/* Check if this phrase descends from an OR expression node. If not,
** return NULL. Otherwise, the entry that corresponds to docid
** pCsr->iPrevId may lie earlier in the doclist buffer. */
for(p=pExpr->pParent; p; p=p->pParent){
if( p->eType==FTSQUERY_OR ) bOr = 1;
}
if( bOr==0 ) return SQLITE_OK;
/* This is the descendent of an OR node. In this case we cannot use
** an incremental phrase. Load the entire doclist for the phrase
** into memory in this case. */
if( pPhrase->bIncr ){
int rc = SQLITE_OK;
int bEofSave = pExpr->bEof;
fts3EvalRestart(pCsr, pExpr, &rc);
while( rc==SQLITE_OK && !pExpr->bEof ){
fts3EvalNextRow(pCsr, pExpr, &rc);
if( bEofSave==0 && pExpr->iDocid==iDocid ) break;
}
pIter = pPhrase->doclist.pList;
assert( rc!=SQLITE_OK || pPhrase->bIncr==0 );
if( rc!=SQLITE_OK ) return rc;
}
if( pExpr->bEof ){
pIter = 0;
iDocid = 0;
}
bEof = (pPhrase->doclist.nAll==0);
assert( bDescDoclist==0 || bDescDoclist==1 );
assert( pCsr->bDesc==0 || pCsr->bDesc==1 );
if( pCsr->bDesc==bDescDoclist ){
int dummy;
while( (pIter==0 || DOCID_CMP(iDocid, pCsr->iPrevId)>0 ) && bEof==0 ){
sqlite3Fts3DoclistPrev(
bDescDoclist, pPhrase->doclist.aAll, pPhrase->doclist.nAll,
&pIter, &iDocid, &dummy, &bEof
);
}
}else{
while( (pIter==0 || DOCID_CMP(iDocid, pCsr->iPrevId)<0 ) && bEof==0 ){
sqlite3Fts3DoclistNext(
bDescDoclist, pPhrase->doclist.aAll, pPhrase->doclist.nAll,
&pIter, &iDocid, &bEof
);
}
}
if( bEof || iDocid!=pCsr->iPrevId ) pIter = 0;
}
if( pIter==0 ) return SQLITE_OK;
if( *pIter==0x01 ){ if( *pIter==0x01 ){
pIter++; pIter++;
pIter += sqlite3Fts3GetVarint32(pIter, &iThis); pIter += sqlite3Fts3GetVarint32(pIter, &iThis);
@ -5044,7 +5276,8 @@ char *sqlite3Fts3EvalPhrasePoslist(
pIter += sqlite3Fts3GetVarint32(pIter, &iThis); pIter += sqlite3Fts3GetVarint32(pIter, &iThis);
} }
return ((iCol==iThis)?pIter:0); *ppOut = ((iCol==iThis)?pIter:0);
return SQLITE_OK;
} }
/* /*
@ -5067,6 +5300,7 @@ void sqlite3Fts3EvalPhraseCleanup(Fts3Phrase *pPhrase){
} }
} }
/* /*
** Return SQLITE_CORRUPT_VTAB. ** Return SQLITE_CORRUPT_VTAB.
*/ */

View File

@ -67,6 +67,9 @@ extern const sqlite3_api_routines *sqlite3_api;
#ifndef MIN #ifndef MIN
# define MIN(x,y) ((x)<(y)?(x):(y)) # define MIN(x,y) ((x)<(y)?(x):(y))
#endif #endif
#ifndef MAX
# define MAX(x,y) ((x)>(y)?(x):(y))
#endif
/* /*
** Maximum length of a varint encoded integer. The varint format is different ** Maximum length of a varint encoded integer. The varint format is different
@ -121,7 +124,7 @@ extern const sqlite3_api_routines *sqlite3_api;
# define NEVER(X) (0) # define NEVER(X) (0)
#else #else
# define ALWAYS(x) (x) # define ALWAYS(x) (x)
# define NEVER(X) (x) # define NEVER(x) (x)
#endif #endif
/* /*
@ -131,6 +134,7 @@ typedef unsigned char u8; /* 1-byte (or larger) unsigned integer */
typedef short int i16; /* 2-byte (or larger) signed integer */ typedef short int i16; /* 2-byte (or larger) signed integer */
typedef unsigned int u32; /* 4-byte unsigned integer */ typedef unsigned int u32; /* 4-byte unsigned integer */
typedef sqlite3_uint64 u64; /* 8-byte unsigned integer */ typedef sqlite3_uint64 u64; /* 8-byte unsigned integer */
typedef sqlite3_int64 i64; /* 8-byte signed integer */
/* /*
** Macro used to suppress compiler warnings for unused parameters. ** Macro used to suppress compiler warnings for unused parameters.
@ -192,36 +196,44 @@ struct Fts3Table {
char **azColumn; /* column names. malloced */ char **azColumn; /* column names. malloced */
sqlite3_tokenizer *pTokenizer; /* tokenizer for inserts and queries */ sqlite3_tokenizer *pTokenizer; /* tokenizer for inserts and queries */
char *zContentTbl; /* content=xxx option, or NULL */ char *zContentTbl; /* content=xxx option, or NULL */
char *zLanguageid; /* languageid=xxx option, or NULL */
u8 bAutoincrmerge; /* True if automerge=1 */
u32 nLeafAdd; /* Number of leaf blocks added this trans */
/* Precompiled statements used by the implementation. Each of these /* Precompiled statements used by the implementation. Each of these
** statements is run and reset within a single virtual table API call. ** statements is run and reset within a single virtual table API call.
*/ */
sqlite3_stmt *aStmt[27]; sqlite3_stmt *aStmt[37];
char *zReadExprlist; char *zReadExprlist;
char *zWriteExprlist; char *zWriteExprlist;
int nNodeSize; /* Soft limit for node size */ int nNodeSize; /* Soft limit for node size */
u8 bFts4; /* True for FTS4, false for FTS3 */
u8 bHasStat; /* True if %_stat table exists */ u8 bHasStat; /* True if %_stat table exists */
u8 bHasDocsize; /* True if %_docsize table exists */ u8 bHasDocsize; /* True if %_docsize table exists */
u8 bDescIdx; /* True if doclists are in reverse order */ u8 bDescIdx; /* True if doclists are in reverse order */
u8 bIgnoreSavepoint; /* True to ignore xSavepoint invocations */
int nPgsz; /* Page size for host database */ int nPgsz; /* Page size for host database */
char *zSegmentsTbl; /* Name of %_segments table */ char *zSegmentsTbl; /* Name of %_segments table */
sqlite3_blob *pSegments; /* Blob handle open on %_segments table */ sqlite3_blob *pSegments; /* Blob handle open on %_segments table */
/* TODO: Fix the first paragraph of this comment. /*
** ** The following array of hash tables is used to buffer pending index
** The following hash table is used to buffer pending index updates during ** updates during transactions. All pending updates buffered at any one
** transactions. Variable nPendingData estimates the memory size of the ** time must share a common language-id (see the FTS4 langid= feature).
** pending data, including hash table overhead, but not malloc overhead. ** The current language id is stored in variable iPrevLangid.
** When nPendingData exceeds nMaxPendingData, the buffer is flushed
** automatically. Variable iPrevDocid is the docid of the most recently
** inserted record.
** **
** A single FTS4 table may have multiple full-text indexes. For each index ** A single FTS4 table may have multiple full-text indexes. For each index
** there is an entry in the aIndex[] array. Index 0 is an index of all the ** there is an entry in the aIndex[] array. Index 0 is an index of all the
** terms that appear in the document set. Each subsequent index in aIndex[] ** terms that appear in the document set. Each subsequent index in aIndex[]
** is an index of prefixes of a specific length. ** is an index of prefixes of a specific length.
**
** Variable nPendingData contains an estimate the memory consumed by the
** pending data structures, including hash table overhead, but not including
** malloc overhead. When nPendingData exceeds nMaxPendingData, all hash
** tables are flushed to disk. Variable iPrevDocid is the docid of the most
** recently inserted record.
*/ */
int nIndex; /* Size of aIndex[] */ int nIndex; /* Size of aIndex[] */
struct Fts3Index { struct Fts3Index {
@ -231,12 +243,13 @@ struct Fts3Table {
int nMaxPendingData; /* Max pending data before flush to disk */ int nMaxPendingData; /* Max pending data before flush to disk */
int nPendingData; /* Current bytes of pending data */ int nPendingData; /* Current bytes of pending data */
sqlite_int64 iPrevDocid; /* Docid of most recently inserted document */ sqlite_int64 iPrevDocid; /* Docid of most recently inserted document */
int iPrevLangid; /* Langid of recently inserted document */
#if defined(SQLITE_DEBUG) || defined(SQLITE_COVERAGE_TEST) #if defined(SQLITE_DEBUG) || defined(SQLITE_COVERAGE_TEST)
/* State variables used for validating that the transaction control /* State variables used for validating that the transaction control
** methods of the virtual table are called at appropriate times. These ** methods of the virtual table are called at appropriate times. These
** values do not contribution to the FTS computation; they are used for ** values do not contribute to FTS functionality; they are used for
** verifying the SQLite core. ** verifying the operation of the SQLite core.
*/ */
int inTransaction; /* True after xBegin but before xCommit/xRollback */ int inTransaction; /* True after xBegin but before xCommit/xRollback */
int mxSavepoint; /* Largest valid xSavepoint integer */ int mxSavepoint; /* Largest valid xSavepoint integer */
@ -255,6 +268,7 @@ struct Fts3Cursor {
u8 isRequireSeek; /* True if must seek pStmt to %_content row */ u8 isRequireSeek; /* True if must seek pStmt to %_content row */
sqlite3_stmt *pStmt; /* Prepared statement in use by the cursor */ sqlite3_stmt *pStmt; /* Prepared statement in use by the cursor */
Fts3Expr *pExpr; /* Parsed MATCH query string */ Fts3Expr *pExpr; /* Parsed MATCH query string */
int iLangid; /* Language being queried for */
int nPhrase; /* Number of matchable phrases in query */ int nPhrase; /* Number of matchable phrases in query */
Fts3DeferredToken *pDeferred; /* Deferred search tokens, if any */ Fts3DeferredToken *pDeferred; /* Deferred search tokens, if any */
sqlite3_int64 iPrevId; /* Previous id read from aDoclist */ sqlite3_int64 iPrevId; /* Previous id read from aDoclist */
@ -401,12 +415,12 @@ int sqlite3Fts3UpdateMethod(sqlite3_vtab*,int,sqlite3_value**,sqlite3_int64*);
int sqlite3Fts3PendingTermsFlush(Fts3Table *); int sqlite3Fts3PendingTermsFlush(Fts3Table *);
void sqlite3Fts3PendingTermsClear(Fts3Table *); void sqlite3Fts3PendingTermsClear(Fts3Table *);
int sqlite3Fts3Optimize(Fts3Table *); int sqlite3Fts3Optimize(Fts3Table *);
int sqlite3Fts3SegReaderNew(int, sqlite3_int64, int sqlite3Fts3SegReaderNew(int, int, sqlite3_int64,
sqlite3_int64, sqlite3_int64, const char *, int, Fts3SegReader**); sqlite3_int64, sqlite3_int64, const char *, int, Fts3SegReader**);
int sqlite3Fts3SegReaderPending( int sqlite3Fts3SegReaderPending(
Fts3Table*,int,const char*,int,int,Fts3SegReader**); Fts3Table*,int,const char*,int,int,Fts3SegReader**);
void sqlite3Fts3SegReaderFree(Fts3SegReader *); void sqlite3Fts3SegReaderFree(Fts3SegReader *);
int sqlite3Fts3AllSegdirs(Fts3Table*, int, int, sqlite3_stmt **); int sqlite3Fts3AllSegdirs(Fts3Table*, int, int, int, sqlite3_stmt **);
int sqlite3Fts3ReadLock(Fts3Table *); int sqlite3Fts3ReadLock(Fts3Table *);
int sqlite3Fts3ReadBlock(Fts3Table*, sqlite3_int64, char **, int*, int*); int sqlite3Fts3ReadBlock(Fts3Table*, sqlite3_int64, char **, int*, int*);
@ -418,6 +432,7 @@ int sqlite3Fts3DeferToken(Fts3Cursor *, Fts3PhraseToken *, int);
int sqlite3Fts3CacheDeferredDoclists(Fts3Cursor *); int sqlite3Fts3CacheDeferredDoclists(Fts3Cursor *);
void sqlite3Fts3FreeDeferredDoclists(Fts3Cursor *); void sqlite3Fts3FreeDeferredDoclists(Fts3Cursor *);
void sqlite3Fts3SegmentsClose(Fts3Table *); void sqlite3Fts3SegmentsClose(Fts3Table *);
int sqlite3Fts3MaxLevel(Fts3Table *, int *);
/* Special values interpreted by sqlite3SegReaderCursor() */ /* Special values interpreted by sqlite3SegReaderCursor() */
#define FTS3_SEGCURSOR_PENDING -1 #define FTS3_SEGCURSOR_PENDING -1
@ -427,8 +442,8 @@ int sqlite3Fts3SegReaderStart(Fts3Table*, Fts3MultiSegReader*, Fts3SegFilter*);
int sqlite3Fts3SegReaderStep(Fts3Table *, Fts3MultiSegReader *); int sqlite3Fts3SegReaderStep(Fts3Table *, Fts3MultiSegReader *);
void sqlite3Fts3SegReaderFinish(Fts3MultiSegReader *); void sqlite3Fts3SegReaderFinish(Fts3MultiSegReader *);
int sqlite3Fts3SegReaderCursor( int sqlite3Fts3SegReaderCursor(Fts3Table *,
Fts3Table *, int, int, const char *, int, int, int, Fts3MultiSegReader *); int, int, int, const char *, int, int, int, Fts3MultiSegReader *);
/* Flags allowed as part of the 4th argument to SegmentReaderIterate() */ /* Flags allowed as part of the 4th argument to SegmentReaderIterate() */
#define FTS3_SEGMENT_REQUIRE_POS 0x00000001 #define FTS3_SEGMENT_REQUIRE_POS 0x00000001
@ -469,6 +484,8 @@ struct Fts3MultiSegReader {
int nDoclist; /* Size of aDoclist[] in bytes */ int nDoclist; /* Size of aDoclist[] in bytes */
}; };
int sqlite3Fts3Incrmerge(Fts3Table*,int,int);
/* fts3.c */ /* fts3.c */
int sqlite3Fts3PutVarint(char *, sqlite3_int64); int sqlite3Fts3PutVarint(char *, sqlite3_int64);
int sqlite3Fts3GetVarint(const char *, sqlite_int64 *); int sqlite3Fts3GetVarint(const char *, sqlite_int64 *);
@ -478,6 +495,7 @@ void sqlite3Fts3Dequote(char *);
void sqlite3Fts3DoclistPrev(int,char*,int,char**,sqlite3_int64*,int*,u8*); void sqlite3Fts3DoclistPrev(int,char*,int,char**,sqlite3_int64*,int*,u8*);
int sqlite3Fts3EvalPhraseStats(Fts3Cursor *, Fts3Expr *, u32 *); int sqlite3Fts3EvalPhraseStats(Fts3Cursor *, Fts3Expr *, u32 *);
int sqlite3Fts3FirstFilter(sqlite3_int64, char *, int, char *); int sqlite3Fts3FirstFilter(sqlite3_int64, char *, int, char *);
void sqlite3Fts3CreateStatTable(int*, Fts3Table*);
/* fts3_tokenizer.c */ /* fts3_tokenizer.c */
const char *sqlite3Fts3NextToken(const char *, int *); const char *sqlite3Fts3NextToken(const char *, int *);
@ -495,7 +513,7 @@ void sqlite3Fts3Snippet(sqlite3_context *, Fts3Cursor *, const char *,
void sqlite3Fts3Matchinfo(sqlite3_context *, Fts3Cursor *, const char *); void sqlite3Fts3Matchinfo(sqlite3_context *, Fts3Cursor *, const char *);
/* fts3_expr.c */ /* fts3_expr.c */
int sqlite3Fts3ExprParse(sqlite3_tokenizer *, int sqlite3Fts3ExprParse(sqlite3_tokenizer *, int,
char **, int, int, int, const char *, int, Fts3Expr ** char **, int, int, int, const char *, int, Fts3Expr **
); );
void sqlite3Fts3ExprFree(Fts3Expr *); void sqlite3Fts3ExprFree(Fts3Expr *);
@ -504,6 +522,10 @@ int sqlite3Fts3ExprInitTestInterface(sqlite3 *db);
int sqlite3Fts3InitTerm(sqlite3 *db); int sqlite3Fts3InitTerm(sqlite3 *db);
#endif #endif
int sqlite3Fts3OpenTokenizer(sqlite3_tokenizer *, int, const char *, int,
sqlite3_tokenizer_cursor **
);
/* fts3_aux.c */ /* fts3_aux.c */
int sqlite3Fts3InitAux(sqlite3 *db); int sqlite3Fts3InitAux(sqlite3 *db);
@ -513,7 +535,7 @@ int sqlite3Fts3MsrIncrStart(
Fts3Table*, Fts3MultiSegReader*, int, const char*, int); Fts3Table*, Fts3MultiSegReader*, int, const char*, int);
int sqlite3Fts3MsrIncrNext( int sqlite3Fts3MsrIncrNext(
Fts3Table *, Fts3MultiSegReader *, sqlite3_int64 *, char **, int *); Fts3Table *, Fts3MultiSegReader *, sqlite3_int64 *, char **, int *);
char *sqlite3Fts3EvalPhrasePoslist(Fts3Cursor *, Fts3Expr *, int iCol); int sqlite3Fts3EvalPhrasePoslist(Fts3Cursor *, Fts3Expr *, int iCol, char **);
int sqlite3Fts3MsrOvfl(Fts3Cursor *, Fts3MultiSegReader *, int *); int sqlite3Fts3MsrOvfl(Fts3Cursor *, Fts3MultiSegReader *, int *);
int sqlite3Fts3MsrIncrRestart(Fts3MultiSegReader *pCsr); int sqlite3Fts3MsrIncrRestart(Fts3MultiSegReader *pCsr);

View File

@ -79,9 +79,9 @@ static int fts3auxConnectMethod(
} }
zDb = argv[1]; zDb = argv[1];
nDb = strlen(zDb); nDb = (int)strlen(zDb);
zFts3 = argv[3]; zFts3 = argv[3];
nFts3 = strlen(zFts3); nFts3 = (int)strlen(zFts3);
rc = sqlite3_declare_vtab(db, FTS3_TERMS_SCHEMA); rc = sqlite3_declare_vtab(db, FTS3_TERMS_SCHEMA);
if( rc!=SQLITE_OK ) return rc; if( rc!=SQLITE_OK ) return rc;
@ -376,7 +376,7 @@ static int fts3auxFilterMethod(
if( pCsr->zStop==0 ) return SQLITE_NOMEM; if( pCsr->zStop==0 ) return SQLITE_NOMEM;
} }
rc = sqlite3Fts3SegReaderCursor(pFts3, 0, FTS3_SEGCURSOR_ALL, rc = sqlite3Fts3SegReaderCursor(pFts3, 0, 0, FTS3_SEGCURSOR_ALL,
pCsr->filter.zTerm, pCsr->filter.nTerm, 0, isScan, &pCsr->csr pCsr->filter.zTerm, pCsr->filter.nTerm, 0, isScan, &pCsr->csr
); );
if( rc==SQLITE_OK ){ if( rc==SQLITE_OK ){

View File

@ -92,6 +92,7 @@ int sqlite3_fts3_enable_parentheses = 0;
typedef struct ParseContext ParseContext; typedef struct ParseContext ParseContext;
struct ParseContext { struct ParseContext {
sqlite3_tokenizer *pTokenizer; /* Tokenizer module */ sqlite3_tokenizer *pTokenizer; /* Tokenizer module */
int iLangid; /* Language id used with tokenizer */
const char **azCol; /* Array of column names for fts3 table */ const char **azCol; /* Array of column names for fts3 table */
int bFts4; /* True to allow FTS4-only syntax */ int bFts4; /* True to allow FTS4-only syntax */
int nCol; /* Number of entries in azCol[] */ int nCol; /* Number of entries in azCol[] */
@ -127,6 +128,33 @@ static void *fts3MallocZero(int nByte){
return pRet; return pRet;
} }
int sqlite3Fts3OpenTokenizer(
sqlite3_tokenizer *pTokenizer,
int iLangid,
const char *z,
int n,
sqlite3_tokenizer_cursor **ppCsr
){
sqlite3_tokenizer_module const *pModule = pTokenizer->pModule;
sqlite3_tokenizer_cursor *pCsr = 0;
int rc;
rc = pModule->xOpen(pTokenizer, z, n, &pCsr);
assert( rc==SQLITE_OK || pCsr==0 );
if( rc==SQLITE_OK ){
pCsr->pTokenizer = pTokenizer;
if( pModule->iVersion>=1 ){
rc = pModule->xLanguageid(pCsr, iLangid);
if( rc!=SQLITE_OK ){
pModule->xClose(pCsr);
pCsr = 0;
}
}
}
*ppCsr = pCsr;
return rc;
}
/* /*
** Extract the next token from buffer z (length n) using the tokenizer ** Extract the next token from buffer z (length n) using the tokenizer
@ -154,15 +182,13 @@ static int getNextToken(
Fts3Expr *pRet = 0; Fts3Expr *pRet = 0;
int nConsumed = 0; int nConsumed = 0;
rc = pModule->xOpen(pTokenizer, z, n, &pCursor); rc = sqlite3Fts3OpenTokenizer(pTokenizer, pParse->iLangid, z, n, &pCursor);
if( rc==SQLITE_OK ){ if( rc==SQLITE_OK ){
const char *zToken; const char *zToken;
int nToken, iStart, iEnd, iPosition; int nToken, iStart, iEnd, iPosition;
int nByte; /* total space to allocate */ int nByte; /* total space to allocate */
pCursor->pTokenizer = pTokenizer;
rc = pModule->xNext(pCursor, &zToken, &nToken, &iStart, &iEnd, &iPosition); rc = pModule->xNext(pCursor, &zToken, &nToken, &iStart, &iEnd, &iPosition);
if( rc==SQLITE_OK ){ if( rc==SQLITE_OK ){
nByte = sizeof(Fts3Expr) + sizeof(Fts3Phrase) + nToken; nByte = sizeof(Fts3Expr) + sizeof(Fts3Phrase) + nToken;
pRet = (Fts3Expr *)fts3MallocZero(nByte); pRet = (Fts3Expr *)fts3MallocZero(nByte);
@ -268,10 +294,10 @@ static int getNextString(
** appends buffer zTemp to buffer p, and fills in the Fts3Expr and Fts3Phrase ** appends buffer zTemp to buffer p, and fills in the Fts3Expr and Fts3Phrase
** structures. ** structures.
*/ */
rc = pModule->xOpen(pTokenizer, zInput, nInput, &pCursor); rc = sqlite3Fts3OpenTokenizer(
pTokenizer, pParse->iLangid, zInput, nInput, &pCursor);
if( rc==SQLITE_OK ){ if( rc==SQLITE_OK ){
int ii; int ii;
pCursor->pTokenizer = pTokenizer;
for(ii=0; rc==SQLITE_OK; ii++){ for(ii=0; rc==SQLITE_OK; ii++){
const char *zByte; const char *zByte;
int nByte, iBegin, iEnd, iPos; int nByte, iBegin, iEnd, iPos;
@ -745,6 +771,7 @@ exprparse_out:
*/ */
int sqlite3Fts3ExprParse( int sqlite3Fts3ExprParse(
sqlite3_tokenizer *pTokenizer, /* Tokenizer module */ sqlite3_tokenizer *pTokenizer, /* Tokenizer module */
int iLangid, /* Language id for tokenizer */
char **azCol, /* Array of column names for fts3 table */ char **azCol, /* Array of column names for fts3 table */
int bFts4, /* True to allow FTS4-only syntax */ int bFts4, /* True to allow FTS4-only syntax */
int nCol, /* Number of entries in azCol[] */ int nCol, /* Number of entries in azCol[] */
@ -755,11 +782,13 @@ int sqlite3Fts3ExprParse(
int nParsed; int nParsed;
int rc; int rc;
ParseContext sParse; ParseContext sParse;
memset(&sParse, 0, sizeof(ParseContext));
sParse.pTokenizer = pTokenizer; sParse.pTokenizer = pTokenizer;
sParse.iLangid = iLangid;
sParse.azCol = (const char **)azCol; sParse.azCol = (const char **)azCol;
sParse.nCol = nCol; sParse.nCol = nCol;
sParse.iDefaultCol = iDefaultCol; sParse.iDefaultCol = iDefaultCol;
sParse.nNest = 0;
sParse.bFts4 = bFts4; sParse.bFts4 = bFts4;
if( z==0 ){ if( z==0 ){
*ppExpr = 0; *ppExpr = 0;
@ -950,7 +979,7 @@ static void fts3ExprTest(
} }
rc = sqlite3Fts3ExprParse( rc = sqlite3Fts3ExprParse(
pTokenizer, azCol, 0, nCol, nCol, zExpr, nExpr, &pExpr pTokenizer, 0, azCol, 0, nCol, nCol, zExpr, nExpr, &pExpr
); );
if( rc!=SQLITE_OK && rc!=SQLITE_NOMEM ){ if( rc!=SQLITE_OK && rc!=SQLITE_NOMEM ){
sqlite3_result_error(context, "Error parsing expression", -1); sqlite3_result_error(context, "Error parsing expression", -1);

View File

@ -110,7 +110,10 @@ static int icuOpen(
*ppCursor = 0; *ppCursor = 0;
if( nInput<0 ){ if( zInput==0 ){
nInput = 0;
zInput = "";
}else if( nInput<0 ){
nInput = strlen(zInput); nInput = strlen(zInput);
} }
nChar = nInput+1; nChar = nInput+1;

View File

@ -630,6 +630,7 @@ static const sqlite3_tokenizer_module porterTokenizerModule = {
porterOpen, porterOpen,
porterClose, porterClose,
porterNext, porterNext,
0
}; };
/* /*

View File

@ -360,10 +360,11 @@ static int fts3SnippetFindPositions(Fts3Expr *pExpr, int iPhrase, void *ctx){
SnippetIter *p = (SnippetIter *)ctx; SnippetIter *p = (SnippetIter *)ctx;
SnippetPhrase *pPhrase = &p->aPhrase[iPhrase]; SnippetPhrase *pPhrase = &p->aPhrase[iPhrase];
char *pCsr; char *pCsr;
int rc;
pPhrase->nToken = pExpr->pPhrase->nToken; pPhrase->nToken = pExpr->pPhrase->nToken;
rc = sqlite3Fts3EvalPhrasePoslist(p->pCsr, pExpr, p->iCol, &pCsr);
pCsr = sqlite3Fts3EvalPhrasePoslist(p->pCsr, pExpr, p->iCol); assert( rc==SQLITE_OK || pCsr==0 );
if( pCsr ){ if( pCsr ){
int iFirst = 0; int iFirst = 0;
pPhrase->pList = pCsr; pPhrase->pList = pCsr;
@ -374,10 +375,12 @@ static int fts3SnippetFindPositions(Fts3Expr *pExpr, int iPhrase, void *ctx){
pPhrase->iHead = iFirst; pPhrase->iHead = iFirst;
pPhrase->iTail = iFirst; pPhrase->iTail = iFirst;
}else{ }else{
assert( pPhrase->pList==0 && pPhrase->pHead==0 && pPhrase->pTail==0 ); assert( rc!=SQLITE_OK || (
pPhrase->pList==0 && pPhrase->pHead==0 && pPhrase->pTail==0
));
} }
return SQLITE_OK; return rc;
} }
/* /*
@ -532,6 +535,7 @@ static int fts3StringAppend(
*/ */
static int fts3SnippetShift( static int fts3SnippetShift(
Fts3Table *pTab, /* FTS3 table snippet comes from */ Fts3Table *pTab, /* FTS3 table snippet comes from */
int iLangid, /* Language id to use in tokenizing */
int nSnippet, /* Number of tokens desired for snippet */ int nSnippet, /* Number of tokens desired for snippet */
const char *zDoc, /* Document text to extract snippet from */ const char *zDoc, /* Document text to extract snippet from */
int nDoc, /* Size of buffer zDoc in bytes */ int nDoc, /* Size of buffer zDoc in bytes */
@ -567,11 +571,10 @@ static int fts3SnippetShift(
/* Open a cursor on zDoc/nDoc. Check if there are (nSnippet+nDesired) /* Open a cursor on zDoc/nDoc. Check if there are (nSnippet+nDesired)
** or more tokens in zDoc/nDoc. ** or more tokens in zDoc/nDoc.
*/ */
rc = pMod->xOpen(pTab->pTokenizer, zDoc, nDoc, &pC); rc = sqlite3Fts3OpenTokenizer(pTab->pTokenizer, iLangid, zDoc, nDoc, &pC);
if( rc!=SQLITE_OK ){ if( rc!=SQLITE_OK ){
return rc; return rc;
} }
pC->pTokenizer = pTab->pTokenizer;
while( rc==SQLITE_OK && iCurrent<(nSnippet+nDesired) ){ while( rc==SQLITE_OK && iCurrent<(nSnippet+nDesired) ){
const char *ZDUMMY; int DUMMY1, DUMMY2, DUMMY3; const char *ZDUMMY; int DUMMY1, DUMMY2, DUMMY3;
rc = pMod->xNext(pC, &ZDUMMY, &DUMMY1, &DUMMY2, &DUMMY3, &iCurrent); rc = pMod->xNext(pC, &ZDUMMY, &DUMMY1, &DUMMY2, &DUMMY3, &iCurrent);
@ -631,11 +634,10 @@ static int fts3SnippetText(
/* Open a token cursor on the document. */ /* Open a token cursor on the document. */
pMod = (sqlite3_tokenizer_module *)pTab->pTokenizer->pModule; pMod = (sqlite3_tokenizer_module *)pTab->pTokenizer->pModule;
rc = pMod->xOpen(pTab->pTokenizer, zDoc, nDoc, &pC); rc = sqlite3Fts3OpenTokenizer(pTab->pTokenizer, pCsr->iLangid, zDoc,nDoc,&pC);
if( rc!=SQLITE_OK ){ if( rc!=SQLITE_OK ){
return rc; return rc;
} }
pC->pTokenizer = pTab->pTokenizer;
while( rc==SQLITE_OK ){ while( rc==SQLITE_OK ){
int iBegin; /* Offset in zDoc of start of token */ int iBegin; /* Offset in zDoc of start of token */
@ -657,7 +659,9 @@ static int fts3SnippetText(
if( !isShiftDone ){ if( !isShiftDone ){
int n = nDoc - iBegin; int n = nDoc - iBegin;
rc = fts3SnippetShift(pTab, nSnippet, &zDoc[iBegin], n, &iPos, &hlmask); rc = fts3SnippetShift(
pTab, pCsr->iLangid, nSnippet, &zDoc[iBegin], n, &iPos, &hlmask
);
isShiftDone = 1; isShiftDone = 1;
/* Now that the shift has been done, check if the initial "..." are /* Now that the shift has been done, check if the initial "..." are
@ -769,13 +773,14 @@ static int fts3ExprLocalHitsCb(
int iPhrase, /* Phrase number */ int iPhrase, /* Phrase number */
void *pCtx /* Pointer to MatchInfo structure */ void *pCtx /* Pointer to MatchInfo structure */
){ ){
int rc = SQLITE_OK;
MatchInfo *p = (MatchInfo *)pCtx; MatchInfo *p = (MatchInfo *)pCtx;
int iStart = iPhrase * p->nCol * 3; int iStart = iPhrase * p->nCol * 3;
int i; int i;
for(i=0; i<p->nCol; i++){ for(i=0; i<p->nCol && rc==SQLITE_OK; i++){
char *pCsr; char *pCsr;
pCsr = sqlite3Fts3EvalPhrasePoslist(p->pCursor, pExpr, i); rc = sqlite3Fts3EvalPhrasePoslist(p->pCursor, pExpr, i, &pCsr);
if( pCsr ){ if( pCsr ){
p->aMatchinfo[iStart+i*3] = fts3ColumnlistCount(&pCsr); p->aMatchinfo[iStart+i*3] = fts3ColumnlistCount(&pCsr);
}else{ }else{
@ -783,7 +788,7 @@ static int fts3ExprLocalHitsCb(
} }
} }
return SQLITE_OK; return rc;
} }
static int fts3MatchinfoCheck( static int fts3MatchinfoCheck(
@ -793,8 +798,8 @@ static int fts3MatchinfoCheck(
){ ){
if( (cArg==FTS3_MATCHINFO_NPHRASE) if( (cArg==FTS3_MATCHINFO_NPHRASE)
|| (cArg==FTS3_MATCHINFO_NCOL) || (cArg==FTS3_MATCHINFO_NCOL)
|| (cArg==FTS3_MATCHINFO_NDOC && pTab->bHasStat) || (cArg==FTS3_MATCHINFO_NDOC && pTab->bFts4)
|| (cArg==FTS3_MATCHINFO_AVGLENGTH && pTab->bHasStat) || (cArg==FTS3_MATCHINFO_AVGLENGTH && pTab->bFts4)
|| (cArg==FTS3_MATCHINFO_LENGTH && pTab->bHasDocsize) || (cArg==FTS3_MATCHINFO_LENGTH && pTab->bHasDocsize)
|| (cArg==FTS3_MATCHINFO_LCS) || (cArg==FTS3_MATCHINFO_LCS)
|| (cArg==FTS3_MATCHINFO_HITS) || (cArg==FTS3_MATCHINFO_HITS)
@ -944,8 +949,10 @@ static int fts3MatchinfoLcs(Fts3Cursor *pCsr, MatchInfo *pInfo){
int nLive = 0; /* Number of iterators in aIter not at EOF */ int nLive = 0; /* Number of iterators in aIter not at EOF */
for(i=0; i<pInfo->nPhrase; i++){ for(i=0; i<pInfo->nPhrase; i++){
int rc;
LcsIterator *pIt = &aIter[i]; LcsIterator *pIt = &aIter[i];
pIt->pRead = sqlite3Fts3EvalPhrasePoslist(pCsr, pIt->pExpr, iCol); rc = sqlite3Fts3EvalPhrasePoslist(pCsr, pIt->pExpr, iCol, &pIt->pRead);
if( rc!=SQLITE_OK ) return rc;
if( pIt->pRead ){ if( pIt->pRead ){
pIt->iPos = pIt->iPosOffset; pIt->iPos = pIt->iPosOffset;
fts3LcsIteratorAdvance(&aIter[i]); fts3LcsIteratorAdvance(&aIter[i]);
@ -1297,9 +1304,10 @@ static int fts3ExprTermOffsetInit(Fts3Expr *pExpr, int iPhrase, void *ctx){
int iTerm; /* For looping through nTerm phrase terms */ int iTerm; /* For looping through nTerm phrase terms */
char *pList; /* Pointer to position list for phrase */ char *pList; /* Pointer to position list for phrase */
int iPos = 0; /* First position in position-list */ int iPos = 0; /* First position in position-list */
int rc;
UNUSED_PARAMETER(iPhrase); UNUSED_PARAMETER(iPhrase);
pList = sqlite3Fts3EvalPhrasePoslist(p->pCsr, pExpr, p->iCol); rc = sqlite3Fts3EvalPhrasePoslist(p->pCsr, pExpr, p->iCol, &pList);
nTerm = pExpr->pPhrase->nToken; nTerm = pExpr->pPhrase->nToken;
if( pList ){ if( pList ){
fts3GetDeltaPosition(&pList, &iPos); fts3GetDeltaPosition(&pList, &iPos);
@ -1313,7 +1321,7 @@ static int fts3ExprTermOffsetInit(Fts3Expr *pExpr, int iPhrase, void *ctx){
pT->iPos = iPos; pT->iPos = iPos;
} }
return SQLITE_OK; return rc;
} }
/* /*
@ -1390,9 +1398,10 @@ void sqlite3Fts3Offsets(
} }
/* Initialize a tokenizer iterator to iterate through column iCol. */ /* Initialize a tokenizer iterator to iterate through column iCol. */
rc = pMod->xOpen(pTab->pTokenizer, zDoc, nDoc, &pC); rc = sqlite3Fts3OpenTokenizer(pTab->pTokenizer, pCsr->iLangid,
zDoc, nDoc, &pC
);
if( rc!=SQLITE_OK ) goto offsets_out; if( rc!=SQLITE_OK ) goto offsets_out;
pC->pTokenizer = pTab->pTokenizer;
rc = pMod->xNext(pC, &ZDUMMY, &NDUMMY, &iStart, &iEnd, &iCurrent); rc = pMod->xNext(pC, &ZDUMMY, &NDUMMY, &iStart, &iEnd, &iCurrent);
while( rc==SQLITE_OK ){ while( rc==SQLITE_OK ){

View File

@ -73,6 +73,7 @@ static int fts3termConnectMethod(
Fts3termTable *p; /* Virtual table object to return */ Fts3termTable *p; /* Virtual table object to return */
int iIndex = 0; int iIndex = 0;
UNUSED_PARAMETER(pCtx);
if( argc==5 ){ if( argc==5 ){
iIndex = atoi(argv[4]); iIndex = atoi(argv[4]);
argc--; argc--;
@ -87,9 +88,9 @@ static int fts3termConnectMethod(
} }
zDb = argv[1]; zDb = argv[1];
nDb = strlen(zDb); nDb = (int)strlen(zDb);
zFts3 = argv[3]; zFts3 = argv[3];
nFts3 = strlen(zFts3); nFts3 = (int)strlen(zFts3);
rc = sqlite3_declare_vtab(db, FTS3_TERMS_SCHEMA); rc = sqlite3_declare_vtab(db, FTS3_TERMS_SCHEMA);
if( rc!=SQLITE_OK ) return rc; if( rc!=SQLITE_OK ) return rc;
@ -231,12 +232,12 @@ static int fts3termNextMethod(sqlite3_vtab_cursor *pCursor){
if( v==1 ){ if( v==1 ){
pCsr->pNext += sqlite3Fts3GetVarint(pCsr->pNext, &v); pCsr->pNext += sqlite3Fts3GetVarint(pCsr->pNext, &v);
pCsr->iCol += v; pCsr->iCol += (int)v;
pCsr->iPos = 0; pCsr->iPos = 0;
pCsr->pNext += sqlite3Fts3GetVarint(pCsr->pNext, &v); pCsr->pNext += sqlite3Fts3GetVarint(pCsr->pNext, &v);
} }
pCsr->iPos += (v - 2); pCsr->iPos += (int)(v - 2);
return SQLITE_OK; return SQLITE_OK;
} }
@ -271,7 +272,7 @@ static int fts3termFilterMethod(
pCsr->filter.flags = FTS3_SEGMENT_REQUIRE_POS|FTS3_SEGMENT_IGNORE_EMPTY; pCsr->filter.flags = FTS3_SEGMENT_REQUIRE_POS|FTS3_SEGMENT_IGNORE_EMPTY;
pCsr->filter.flags |= FTS3_SEGMENT_SCAN; pCsr->filter.flags |= FTS3_SEGMENT_SCAN;
rc = sqlite3Fts3SegReaderCursor(pFts3, p->iIndex, FTS3_SEGCURSOR_ALL, rc = sqlite3Fts3SegReaderCursor(pFts3, 0, p->iIndex, FTS3_SEGCURSOR_ALL,
pCsr->filter.zTerm, pCsr->filter.nTerm, 0, 1, &pCsr->csr pCsr->filter.zTerm, pCsr->filter.nTerm, 0, 1, &pCsr->csr
); );
if( rc==SQLITE_OK ){ if( rc==SQLITE_OK ){
@ -357,7 +358,10 @@ int sqlite3Fts3InitTerm(sqlite3 *db){
0, /* xCommit */ 0, /* xCommit */
0, /* xRollback */ 0, /* xRollback */
0, /* xFindFunction */ 0, /* xFindFunction */
0 /* xRename */ 0, /* xRename */
0, /* xSavepoint */
0, /* xRelease */
0 /* xRollbackTo */
}; };
int rc; /* Return code */ int rc; /* Return code */

View File

@ -13,13 +13,17 @@
** This file is not part of the production FTS code. It is only used for ** This file is not part of the production FTS code. It is only used for
** testing. It contains a Tcl command that can be used to test if a document ** testing. It contains a Tcl command that can be used to test if a document
** matches an FTS NEAR expression. ** matches an FTS NEAR expression.
**
** As of March 2012, it also contains a version 1 tokenizer used for testing
** that the sqlite3_tokenizer_module.xLanguage() method is invoked correctly.
*/ */
#include <tcl.h> #include <tcl.h>
#include <string.h> #include <string.h>
#include <assert.h> #include <assert.h>
#ifdef SQLITE_TEST #if defined(SQLITE_TEST)
#if defined(SQLITE_ENABLE_FTS3) || defined(SQLITE_ENABLE_FTS4)
/* Required so that the "ifdef SQLITE_ENABLE_FTS3" below works */ /* Required so that the "ifdef SQLITE_ENABLE_FTS3" below works */
#include "fts3Int.h" #include "fts3Int.h"
@ -158,6 +162,8 @@ static int fts3_near_match_cmd(
Tcl_Obj **apExprToken; Tcl_Obj **apExprToken;
int nExprToken; int nExprToken;
UNUSED_PARAMETER(clientData);
/* Must have 3 or more arguments. */ /* Must have 3 or more arguments. */
if( objc<3 || (objc%2)==0 ){ if( objc<3 || (objc%2)==0 ){
Tcl_WrongNumArgs(interp, 1, objv, "DOCUMENT EXPR ?OPTION VALUE?..."); Tcl_WrongNumArgs(interp, 1, objv, "DOCUMENT EXPR ?OPTION VALUE?...");
@ -311,14 +317,219 @@ static int fts3_configure_incr_load_cmd(
Tcl_SetObjResult(interp, pRet); Tcl_SetObjResult(interp, pRet);
Tcl_DecrRefCount(pRet); Tcl_DecrRefCount(pRet);
#endif #endif
UNUSED_PARAMETER(clientData);
return TCL_OK; return TCL_OK;
} }
#ifdef SQLITE_ENABLE_FTS3
/**************************************************************************
** Beginning of test tokenizer code.
**
** For language 0, this tokenizer is similar to the default 'simple'
** tokenizer. For other languages L, the following:
**
** * Odd numbered languages are case-sensitive. Even numbered
** languages are not.
**
** * Language ids 100 or greater are considered an error.
**
** The implementation assumes that the input contains only ASCII characters
** (i.e. those that may be encoded in UTF-8 using a single byte).
*/
typedef struct test_tokenizer {
sqlite3_tokenizer base;
} test_tokenizer;
typedef struct test_tokenizer_cursor {
sqlite3_tokenizer_cursor base;
const char *aInput; /* Input being tokenized */
int nInput; /* Size of the input in bytes */
int iInput; /* Current offset in aInput */
int iToken; /* Index of next token to be returned */
char *aBuffer; /* Buffer containing current token */
int nBuffer; /* Number of bytes allocated at pToken */
int iLangid; /* Configured language id */
} test_tokenizer_cursor;
static int testTokenizerCreate(
int argc, const char * const *argv,
sqlite3_tokenizer **ppTokenizer
){
test_tokenizer *pNew;
UNUSED_PARAMETER(argc);
UNUSED_PARAMETER(argv);
pNew = sqlite3_malloc(sizeof(test_tokenizer));
if( !pNew ) return SQLITE_NOMEM;
memset(pNew, 0, sizeof(test_tokenizer));
*ppTokenizer = (sqlite3_tokenizer *)pNew;
return SQLITE_OK;
}
static int testTokenizerDestroy(sqlite3_tokenizer *pTokenizer){
test_tokenizer *p = (test_tokenizer *)pTokenizer;
sqlite3_free(p);
return SQLITE_OK;
}
static int testTokenizerOpen(
sqlite3_tokenizer *pTokenizer, /* The tokenizer */
const char *pInput, int nBytes, /* String to be tokenized */
sqlite3_tokenizer_cursor **ppCursor /* OUT: Tokenization cursor */
){
int rc = SQLITE_OK; /* Return code */
test_tokenizer_cursor *pCsr; /* New cursor object */
UNUSED_PARAMETER(pTokenizer);
pCsr = (test_tokenizer_cursor *)sqlite3_malloc(sizeof(test_tokenizer_cursor));
if( pCsr==0 ){
rc = SQLITE_NOMEM;
}else{
memset(pCsr, 0, sizeof(test_tokenizer_cursor));
pCsr->aInput = pInput;
if( nBytes<0 ){
pCsr->nInput = (int)strlen(pInput);
}else{
pCsr->nInput = nBytes;
}
}
*ppCursor = (sqlite3_tokenizer_cursor *)pCsr;
return rc;
}
static int testTokenizerClose(sqlite3_tokenizer_cursor *pCursor){
test_tokenizer_cursor *pCsr = (test_tokenizer_cursor *)pCursor;
sqlite3_free(pCsr->aBuffer);
sqlite3_free(pCsr);
return SQLITE_OK;
}
static int testIsTokenChar(char c){
return (c>='a' && c<='z') || (c>='A' && c<='Z');
}
static int testTolower(char c){
char ret = c;
if( ret>='A' && ret<='Z') ret = ret - ('A'-'a');
return ret;
}
static int testTokenizerNext(
sqlite3_tokenizer_cursor *pCursor, /* Cursor returned by testTokenizerOpen */
const char **ppToken, /* OUT: *ppToken is the token text */
int *pnBytes, /* OUT: Number of bytes in token */
int *piStartOffset, /* OUT: Starting offset of token */
int *piEndOffset, /* OUT: Ending offset of token */
int *piPosition /* OUT: Position integer of token */
){
test_tokenizer_cursor *pCsr = (test_tokenizer_cursor *)pCursor;
int rc = SQLITE_OK;
const char *p;
const char *pEnd;
p = &pCsr->aInput[pCsr->iInput];
pEnd = &pCsr->aInput[pCsr->nInput];
/* Skip past any white-space */
assert( p<=pEnd );
while( p<pEnd && testIsTokenChar(*p)==0 ) p++;
if( p==pEnd ){
rc = SQLITE_DONE;
}else{
/* Advance to the end of the token */
const char *pToken = p;
int nToken;
while( p<pEnd && testIsTokenChar(*p) ) p++;
nToken = (int)(p-pToken);
/* Copy the token into the buffer */
if( nToken>pCsr->nBuffer ){
sqlite3_free(pCsr->aBuffer);
pCsr->aBuffer = sqlite3_malloc(nToken);
}
if( pCsr->aBuffer==0 ){
rc = SQLITE_NOMEM;
}else{
int i;
if( pCsr->iLangid & 0x00000001 ){
for(i=0; i<nToken; i++) pCsr->aBuffer[i] = pToken[i];
}else{
for(i=0; i<nToken; i++) pCsr->aBuffer[i] = testTolower(pToken[i]);
}
pCsr->iToken++;
pCsr->iInput = (int)(p - pCsr->aInput);
*ppToken = pCsr->aBuffer;
*pnBytes = nToken;
*piStartOffset = (int)(pToken - pCsr->aInput);
*piEndOffset = (int)(p - pCsr->aInput);
*piPosition = pCsr->iToken;
}
}
return rc;
}
static int testTokenizerLanguage(
sqlite3_tokenizer_cursor *pCursor,
int iLangid
){
int rc = SQLITE_OK;
test_tokenizer_cursor *pCsr = (test_tokenizer_cursor *)pCursor;
pCsr->iLangid = iLangid;
if( pCsr->iLangid>=100 ){
rc = SQLITE_ERROR;
}
return rc;
}
#endif
static int fts3_test_tokenizer_cmd(
ClientData clientData,
Tcl_Interp *interp,
int objc,
Tcl_Obj *CONST objv[]
){
#ifdef SQLITE_ENABLE_FTS3
static const sqlite3_tokenizer_module testTokenizerModule = {
1,
testTokenizerCreate,
testTokenizerDestroy,
testTokenizerOpen,
testTokenizerClose,
testTokenizerNext,
testTokenizerLanguage
};
const sqlite3_tokenizer_module *pPtr = &testTokenizerModule;
if( objc!=1 ){
Tcl_WrongNumArgs(interp, 1, objv, "");
return TCL_ERROR;
}
Tcl_SetObjResult(interp, Tcl_NewByteArrayObj(
(const unsigned char *)&pPtr, sizeof(sqlite3_tokenizer_module *)
));
#endif
UNUSED_PARAMETER(clientData);
return TCL_OK;
}
/*
** End of tokenizer code.
**************************************************************************/
int Sqlitetestfts3_Init(Tcl_Interp *interp){ int Sqlitetestfts3_Init(Tcl_Interp *interp){
Tcl_CreateObjCommand(interp, "fts3_near_match", fts3_near_match_cmd, 0, 0); Tcl_CreateObjCommand(interp, "fts3_near_match", fts3_near_match_cmd, 0, 0);
Tcl_CreateObjCommand(interp, Tcl_CreateObjCommand(interp,
"fts3_configure_incr_load", fts3_configure_incr_load_cmd, 0, 0 "fts3_configure_incr_load", fts3_configure_incr_load_cmd, 0, 0
); );
Tcl_CreateObjCommand(
interp, "fts3_test_tokenizer", fts3_test_tokenizer_cmd, 0, 0
);
return TCL_OK; return TCL_OK;
} }
#endif /* SQLITE_ENABLE_FTS3 || SQLITE_ENABLE_FTS4 */
#endif /* ifdef SQLITE_TEST */ #endif /* ifdef SQLITE_TEST */

View File

@ -288,11 +288,10 @@ static void testFunc(
goto finish; goto finish;
} }
pTokenizer->pModule = p; pTokenizer->pModule = p;
if( SQLITE_OK!=p->xOpen(pTokenizer, zInput, nInput, &pCsr) ){ if( sqlite3Fts3OpenTokenizer(pTokenizer, 0, zInput, nInput, &pCsr) ){
zErr = "error in xOpen()"; zErr = "error in xOpen()";
goto finish; goto finish;
} }
pCsr->pTokenizer = pTokenizer;
while( SQLITE_OK==p->xNext(pCsr, &zToken, &nToken, &iStart, &iEnd, &iPos) ){ while( SQLITE_OK==p->xNext(pCsr, &zToken, &nToken, &iStart, &iEnd, &iPos) ){
Tcl_ListObjAppendElement(0, pRet, Tcl_NewIntObj(iPos)); Tcl_ListObjAppendElement(0, pRet, Tcl_NewIntObj(iPos));

View File

@ -52,7 +52,7 @@ typedef struct sqlite3_tokenizer_cursor sqlite3_tokenizer_cursor;
struct sqlite3_tokenizer_module { struct sqlite3_tokenizer_module {
/* /*
** Structure version. Should always be set to 0. ** Structure version. Should always be set to 0 or 1.
*/ */
int iVersion; int iVersion;
@ -133,6 +133,15 @@ struct sqlite3_tokenizer_module {
int *piEndOffset, /* OUT: Byte offset of end of token in input buffer */ int *piEndOffset, /* OUT: Byte offset of end of token in input buffer */
int *piPosition /* OUT: Number of tokens returned before this one */ int *piPosition /* OUT: Number of tokens returned before this one */
); );
/***********************************************************************
** Methods below this point are only available if iVersion>=1.
*/
/*
** Configure the language id of a tokenizer cursor.
*/
int (*xLanguageid)(sqlite3_tokenizer_cursor *pCsr, int iLangid);
}; };
struct sqlite3_tokenizer { struct sqlite3_tokenizer {

View File

@ -218,6 +218,7 @@ static const sqlite3_tokenizer_module simpleTokenizerModule = {
simpleOpen, simpleOpen,
simpleClose, simpleClose,
simpleNext, simpleNext,
0,
}; };
/* /*

File diff suppressed because it is too large Load Diff

874
ext/fts3/tool/fts3view.c Normal file
View File

@ -0,0 +1,874 @@
/*
** This program is a debugging and analysis utility that displays
** information about an FTS3 or FTS4 index.
**
** Link this program against the SQLite3 amalgamation with the
** SQLITE_ENABLE_FTS4 compile-time option. Then run it as:
**
** fts3view DATABASE
**
** to get a list of all FTS3/4 tables in DATABASE, or do
**
** fts3view DATABASE TABLE COMMAND ....
**
** to see various aspects of the TABLE table. Type fts3view with no
** arguments for a list of available COMMANDs.
*/
#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "sqlite3.h"
/*
** Extra command-line arguments:
*/
int nExtra;
char **azExtra;
/*
** Look for a command-line argument.
*/
const char *findOption(const char *zName, int hasArg, const char *zDefault){
int i;
const char *zResult = zDefault;
for(i=0; i<nExtra; i++){
const char *z = azExtra[i];
while( z[0]=='-' ) z++;
if( strcmp(z, zName)==0 ){
int j = 1;
if( hasArg==0 || i==nExtra-1 ) j = 0;
zResult = azExtra[i+j];
while( i+j<nExtra ){
azExtra[i] = azExtra[i+j+1];
i++;
}
break;
}
}
return zResult;
}
/*
** Prepare an SQL query
*/
static sqlite3_stmt *prepare(sqlite3 *db, const char *zFormat, ...){
va_list ap;
char *zSql;
sqlite3_stmt *pStmt;
int rc;
va_start(ap, zFormat);
zSql = sqlite3_vmprintf(zFormat, ap);
va_end(ap);
rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0);
if( rc ){
fprintf(stderr, "Error: %s\nSQL: %s\n", sqlite3_errmsg(db), zSql);
exit(1);
}
sqlite3_free(zSql);
return pStmt;
}
/*
** Run an SQL statement
*/
static int runSql(sqlite3 *db, const char *zFormat, ...){
va_list ap;
char *zSql;
int rc;
va_start(ap, zFormat);
zSql = sqlite3_vmprintf(zFormat, ap);
rc = sqlite3_exec(db, zSql, 0, 0, 0);
va_end(ap);
return rc;
}
/*
** Show the table schema
*/
static void showSchema(sqlite3 *db, const char *zTab){
sqlite3_stmt *pStmt;
pStmt = prepare(db,
"SELECT sql FROM sqlite_master"
" WHERE name LIKE '%q%%'"
" ORDER BY 1",
zTab);
while( sqlite3_step(pStmt)==SQLITE_ROW ){
printf("%s;\n", sqlite3_column_text(pStmt, 0));
}
sqlite3_finalize(pStmt);
pStmt = prepare(db, "PRAGMA page_size");
while( sqlite3_step(pStmt)==SQLITE_ROW ){
printf("PRAGMA page_size=%s;\n", sqlite3_column_text(pStmt, 0));
}
sqlite3_finalize(pStmt);
pStmt = prepare(db, "PRAGMA journal_mode");
while( sqlite3_step(pStmt)==SQLITE_ROW ){
printf("PRAGMA journal_mode=%s;\n", sqlite3_column_text(pStmt, 0));
}
sqlite3_finalize(pStmt);
pStmt = prepare(db, "PRAGMA auto_vacuum");
while( sqlite3_step(pStmt)==SQLITE_ROW ){
const char *zType = "???";
switch( sqlite3_column_int(pStmt, 0) ){
case 0: zType = "OFF"; break;
case 1: zType = "FULL"; break;
case 2: zType = "INCREMENTAL"; break;
}
printf("PRAGMA auto_vacuum=%s;\n", zType);
}
sqlite3_finalize(pStmt);
pStmt = prepare(db, "PRAGMA encoding");
while( sqlite3_step(pStmt)==SQLITE_ROW ){
printf("PRAGMA encoding=%s;\n", sqlite3_column_text(pStmt, 0));
}
sqlite3_finalize(pStmt);
}
/*
** Read a 64-bit variable-length integer from memory starting at p[0].
** Return the number of bytes read, or 0 on error.
** The value is stored in *v.
*/
int getVarint(const unsigned char *p, sqlite_int64 *v){
const unsigned char *q = p;
sqlite_uint64 x = 0, y = 1;
while( (*q&0x80)==0x80 && q-(unsigned char *)p<9 ){
x += y * (*q++ & 0x7f);
y <<= 7;
}
x += y * (*q++);
*v = (sqlite_int64) x;
return (int) (q - (unsigned char *)p);
}
/* Show the content of the %_stat table
*/
static void showStat(sqlite3 *db, const char *zTab){
sqlite3_stmt *pStmt;
pStmt = prepare(db, "SELECT id, value FROM '%q_stat'", zTab);
while( sqlite3_step(pStmt)==SQLITE_ROW ){
printf("stat[%d] =", sqlite3_column_int(pStmt, 0));
switch( sqlite3_column_type(pStmt, 1) ){
case SQLITE_INTEGER: {
printf(" %d\n", sqlite3_column_int(pStmt, 1));
break;
}
case SQLITE_BLOB: {
unsigned char *x = (unsigned char*)sqlite3_column_blob(pStmt, 1);
int len = sqlite3_column_bytes(pStmt, 1);
int i = 0;
sqlite3_int64 v;
while( i<len ){
i += getVarint(x, &v);
printf(" %lld", v);
}
printf("\n");
break;
}
}
}
sqlite3_finalize(pStmt);
}
/*
** Report on the vocabulary. This creates an fts4aux table with a random
** name, but deletes it in the end.
*/
static void showVocabulary(sqlite3 *db, const char *zTab){
char *zAux;
sqlite3_uint64 r;
sqlite3_stmt *pStmt;
int nDoc = 0;
int nToken = 0;
int nOccurrence = 0;
int nTop;
int n, i;
sqlite3_randomness(sizeof(r), &r);
zAux = sqlite3_mprintf("viewer_%llx", zTab, r);
runSql(db, "BEGIN");
pStmt = prepare(db, "SELECT count(*) FROM %Q", zTab);
while( sqlite3_step(pStmt)==SQLITE_ROW ){
nDoc = sqlite3_column_int(pStmt, 0);
}
sqlite3_finalize(pStmt);
printf("Number of documents...................... %9d\n", nDoc);
runSql(db, "CREATE VIRTUAL TABLE %s USING fts4aux(%Q)", zAux, zTab);
pStmt = prepare(db,
"SELECT count(*), sum(occurrences) FROM %s WHERE col='*'",
zAux);
while( sqlite3_step(pStmt)==SQLITE_ROW ){
nToken = sqlite3_column_int(pStmt, 0);
nOccurrence = sqlite3_column_int(pStmt, 1);
}
sqlite3_finalize(pStmt);
printf("Total tokens in all documents............ %9d\n", nOccurrence);
printf("Total number of distinct tokens.......... %9d\n", nToken);
if( nToken==0 ) goto end_vocab;
n = 0;
pStmt = prepare(db, "SELECT count(*) FROM %s"
" WHERE col='*' AND occurrences==1", zAux);
while( sqlite3_step(pStmt)==SQLITE_ROW ){
n = sqlite3_column_int(pStmt, 0);
}
sqlite3_finalize(pStmt);
printf("Tokens used exactly once................. %9d %5.2f%%\n",
n, n*100.0/nToken);
n = 0;
pStmt = prepare(db, "SELECT count(*) FROM %s"
" WHERE col='*' AND documents==1", zAux);
while( sqlite3_step(pStmt)==SQLITE_ROW ){
n = sqlite3_column_int(pStmt, 0);
}
sqlite3_finalize(pStmt);
printf("Tokens used in only one document......... %9d %5.2f%%\n",
n, n*100.0/nToken);
if( nDoc>=2000 ){
n = 0;
pStmt = prepare(db, "SELECT count(*) FROM %s"
" WHERE col='*' AND occurrences<=%d", zAux, nDoc/1000);
while( sqlite3_step(pStmt)==SQLITE_ROW ){
n = sqlite3_column_int(pStmt, 0);
}
sqlite3_finalize(pStmt);
printf("Tokens used in 0.1%% or less of docs...... %9d %5.2f%%\n",
n, n*100.0/nToken);
}
if( nDoc>=200 ){
n = 0;
pStmt = prepare(db, "SELECT count(*) FROM %s"
" WHERE col='*' AND occurrences<=%d", zAux, nDoc/100);
while( sqlite3_step(pStmt)==SQLITE_ROW ){
n = sqlite3_column_int(pStmt, 0);
}
sqlite3_finalize(pStmt);
printf("Tokens used in 1%% or less of docs........ %9d %5.2f%%\n",
n, n*100.0/nToken);
}
nTop = atoi(findOption("top", 1, "25"));
printf("The %d most common tokens:\n", nTop);
pStmt = prepare(db,
"SELECT term, documents FROM %s"
" WHERE col='*'"
" ORDER BY documents DESC, term"
" LIMIT %d", zAux, nTop);
i = 0;
while( sqlite3_step(pStmt)==SQLITE_ROW ){
i++;
n = sqlite3_column_int(pStmt, 1);
printf(" %2d. %-30s %9d docs %5.2f%%\n", i,
sqlite3_column_text(pStmt, 0), n, n*100.0/nDoc);
}
sqlite3_finalize(pStmt);
end_vocab:
runSql(db, "ROLLBACK");
sqlite3_free(zAux);
}
/*
** Report on the number and sizes of segments
*/
static void showSegmentStats(sqlite3 *db, const char *zTab){
sqlite3_stmt *pStmt;
int nSeg = 0;
sqlite3_int64 szSeg = 0, mxSeg = 0;
int nIdx = 0;
sqlite3_int64 szIdx = 0, mxIdx = 0;
int nRoot = 0;
sqlite3_int64 szRoot = 0, mxRoot = 0;
sqlite3_int64 mx;
int nLeaf;
int n;
int pgsz;
int mxLevel;
int i;
pStmt = prepare(db,
"SELECT count(*), sum(length(block)), max(length(block))"
" FROM '%q_segments'",
zTab);
while( sqlite3_step(pStmt)==SQLITE_ROW ){
nSeg = sqlite3_column_int(pStmt, 0);
szSeg = sqlite3_column_int64(pStmt, 1);
mxSeg = sqlite3_column_int64(pStmt, 2);
}
sqlite3_finalize(pStmt);
pStmt = prepare(db,
"SELECT count(*), sum(length(block)), max(length(block))"
" FROM '%q_segments' a JOIN '%q_segdir' b"
" WHERE a.blockid BETWEEN b.leaves_end_block+1 AND b.end_block",
zTab, zTab);
while( sqlite3_step(pStmt)==SQLITE_ROW ){
nIdx = sqlite3_column_int(pStmt, 0);
szIdx = sqlite3_column_int64(pStmt, 1);
mxIdx = sqlite3_column_int64(pStmt, 2);
}
sqlite3_finalize(pStmt);
pStmt = prepare(db,
"SELECT count(*), sum(length(root)), max(length(root))"
" FROM '%q_segdir'",
zTab);
while( sqlite3_step(pStmt)==SQLITE_ROW ){
nRoot = sqlite3_column_int(pStmt, 0);
szRoot = sqlite3_column_int64(pStmt, 1);
mxRoot = sqlite3_column_int64(pStmt, 2);
}
sqlite3_finalize(pStmt);
printf("Number of segments....................... %9d\n", nSeg+nRoot);
printf("Number of leaf segments.................. %9d\n", nSeg-nIdx);
printf("Number of index segments................. %9d\n", nIdx);
printf("Number of root segments.................. %9d\n", nRoot);
printf("Total size of all segments............... %9lld\n", szSeg+szRoot);
printf("Total size of all leaf segments.......... %9lld\n", szSeg-szIdx);
printf("Total size of all index segments......... %9lld\n", szIdx);
printf("Total size of all root segments.......... %9lld\n", szRoot);
if( nSeg>0 ){
printf("Average size of all segments............. %11.1f\n",
(double)(szSeg+szRoot)/(double)(nSeg+nRoot));
printf("Average size of leaf segments............ %11.1f\n",
(double)(szSeg-szIdx)/(double)(nSeg-nIdx));
}
if( nIdx>0 ){
printf("Average size of index segments........... %11.1f\n",
(double)szIdx/(double)nIdx);
}
if( nRoot>0 ){
printf("Average size of root segments............ %11.1f\n",
(double)szRoot/(double)nRoot);
}
mx = mxSeg;
if( mx<mxRoot ) mx = mxRoot;
printf("Maximum segment size..................... %9lld\n", mx);
printf("Maximum index segment size............... %9lld\n", mxIdx);
printf("Maximum root segment size................ %9lld\n", mxRoot);
pStmt = prepare(db, "PRAGMA page_size");
pgsz = 1024;
while( sqlite3_step(pStmt)==SQLITE_ROW ){
pgsz = sqlite3_column_int(pStmt, 0);
}
sqlite3_finalize(pStmt);
printf("Database page size....................... %9d\n", pgsz);
pStmt = prepare(db,
"SELECT count(*)"
" FROM '%q_segments' a JOIN '%q_segdir' b"
" WHERE a.blockid BETWEEN b.start_block AND b.leaves_end_block"
" AND length(a.block)>%d",
zTab, zTab, pgsz-45);
n = 0;
while( sqlite3_step(pStmt)==SQLITE_ROW ){
n = sqlite3_column_int(pStmt, 0);
}
sqlite3_finalize(pStmt);
nLeaf = nSeg - nIdx;
printf("Leaf segments larger than %5d bytes.... %9d %5.2f%%\n",
pgsz-45, n, n*100.0/nLeaf);
pStmt = prepare(db, "SELECT max(level%%1024) FROM '%q_segdir'", zTab);
mxLevel = 0;
while( sqlite3_step(pStmt)==SQLITE_ROW ){
mxLevel = sqlite3_column_int(pStmt, 0);
}
sqlite3_finalize(pStmt);
for(i=0; i<=mxLevel; i++){
pStmt = prepare(db,
"SELECT count(*), sum(len), avg(len), max(len), sum(len>%d),"
" count(distinct idx)"
" FROM (SELECT length(a.block) AS len, idx"
" FROM '%q_segments' a JOIN '%q_segdir' b"
" WHERE (a.blockid BETWEEN b.start_block"
" AND b.leaves_end_block)"
" AND (b.level%%1024)==%d)",
pgsz-45, zTab, zTab, i);
if( sqlite3_step(pStmt)==SQLITE_ROW
&& (nLeaf = sqlite3_column_int(pStmt, 0))>0
){
int nIdx = sqlite3_column_int(pStmt, 5);
sqlite3_int64 sz;
printf("For level %d:\n", i);
printf(" Number of indexes...................... %9d\n", nIdx);
printf(" Number of leaf segments................ %9d\n", nLeaf);
if( nIdx>1 ){
printf(" Average leaf segments per index........ %11.1f\n",
(double)nLeaf/(double)nIdx);
}
printf(" Total size of all leaf segments........ %9lld\n",
(sz = sqlite3_column_int64(pStmt, 1)));
printf(" Average size of leaf segments.......... %11.1f\n",
sqlite3_column_double(pStmt, 2));
if( nIdx>1 ){
printf(" Average leaf segment size per index.... %11.1f\n",
(double)sz/(double)nIdx);
}
printf(" Maximum leaf segment size.............. %9lld\n",
sqlite3_column_int64(pStmt, 3));
n = sqlite3_column_int(pStmt, 4);
printf(" Leaf segments larger than %5d bytes.. %9d %5.2f%%\n",
pgsz-45, n, n*100.0/nLeaf);
}
sqlite3_finalize(pStmt);
}
}
/*
** Print a single "tree" line of the segdir map output.
*/
static void printTreeLine(sqlite3_int64 iLower, sqlite3_int64 iUpper){
printf(" tree %9lld", iLower);
if( iUpper>iLower ){
printf(" thru %9lld (%lld blocks)", iUpper, iUpper-iLower+1);
}
printf("\n");
}
/*
** Check to see if the block of a %_segments entry is NULL.
*/
static int isNullSegment(sqlite3 *db, const char *zTab, sqlite3_int64 iBlockId){
sqlite3_stmt *pStmt;
int rc = 1;
pStmt = prepare(db, "SELECT block IS NULL FROM '%q_segments'"
" WHERE blockid=%lld", zTab, iBlockId);
if( sqlite3_step(pStmt)==SQLITE_ROW ){
rc = sqlite3_column_int(pStmt, 0);
}
sqlite3_finalize(pStmt);
return rc;
}
/*
** Show a map of segments derived from the %_segdir table.
*/
static void showSegdirMap(sqlite3 *db, const char *zTab){
int mxIndex, iIndex;
sqlite3_stmt *pStmt = 0;
sqlite3_stmt *pStmt2 = 0;
int prevLevel;
pStmt = prepare(db, "SELECT max(level/1024) FROM '%q_segdir'", zTab);
if( sqlite3_step(pStmt)==SQLITE_ROW ){
mxIndex = sqlite3_column_int(pStmt, 0);
}else{
mxIndex = 0;
}
sqlite3_finalize(pStmt);
printf("Number of inverted indices............... %3d\n", mxIndex+1);
pStmt = prepare(db,
"SELECT level, idx, start_block, leaves_end_block, end_block, rowid"
" FROM '%q_segdir'"
" WHERE level/1024==?"
" ORDER BY level DESC, idx",
zTab);
pStmt2 = prepare(db,
"SELECT blockid FROM '%q_segments'"
" WHERE blockid BETWEEN ? AND ? ORDER BY blockid",
zTab);
for(iIndex=0; iIndex<=mxIndex; iIndex++){
if( mxIndex>0 ){
printf("**************************** Index %d "
"****************************\n", iIndex);
}
sqlite3_bind_int(pStmt, 1, iIndex);
prevLevel = -1;
while( sqlite3_step(pStmt)==SQLITE_ROW ){
int iLevel = sqlite3_column_int(pStmt, 0)%1024;
int iIdx = sqlite3_column_int(pStmt, 1);
sqlite3_int64 iStart = sqlite3_column_int64(pStmt, 2);
sqlite3_int64 iLEnd = sqlite3_column_int64(pStmt, 3);
sqlite3_int64 iEnd = sqlite3_column_int64(pStmt, 4);
char rtag[20];
if( iLevel!=prevLevel ){
printf("level %2d idx %2d", iLevel, iIdx);
prevLevel = iLevel;
}else{
printf(" idx %2d", iIdx);
}
sqlite3_snprintf(sizeof(rtag), rtag, "r%lld",
sqlite3_column_int64(pStmt,5));
printf(" root %9s\n", rtag);
if( iLEnd>iStart ){
sqlite3_int64 iLower, iPrev, iX;
if( iLEnd+1<=iEnd ){
sqlite3_bind_int64(pStmt2, 1, iLEnd+1);
sqlite3_bind_int64(pStmt2, 2, iEnd);
iLower = -1;
while( sqlite3_step(pStmt2)==SQLITE_ROW ){
iX = sqlite3_column_int64(pStmt2, 0);
if( iLower<0 ){
iLower = iPrev = iX;
}else if( iX==iPrev+1 ){
iPrev = iX;
}else{
printTreeLine(iLower, iPrev);
iLower = iPrev = iX;
}
}
sqlite3_reset(pStmt2);
if( iLower>=0 ){
if( iLower==iPrev && iLower==iEnd
&& isNullSegment(db,zTab,iLower)
){
printf(" null %9lld\n", iLower);
}else{
printTreeLine(iLower, iPrev);
}
}
}
printf(" leaves %9lld thru %9lld (%lld blocks)\n",
iStart, iLEnd, iLEnd - iStart + 1);
}
}
sqlite3_reset(pStmt);
}
sqlite3_finalize(pStmt);
sqlite3_finalize(pStmt2);
}
/*
** Decode a single segment block and display the results on stdout.
*/
static void decodeSegment(
const unsigned char *aData, /* Content to print */
int nData /* Number of bytes of content */
){
sqlite3_int64 iChild;
sqlite3_int64 iPrefix;
sqlite3_int64 nTerm;
sqlite3_int64 n;
sqlite3_int64 iDocsz;
int iHeight;
int i = 0;
int cnt = 0;
char zTerm[1000];
i += getVarint(aData, &n);
iHeight = (int)n;
printf("height: %d\n", iHeight);
if( iHeight>0 ){
i += getVarint(aData+i, &iChild);
printf("left-child: %lld\n", iChild);
}
while( i<nData ){
if( (cnt++)>0 ){
i += getVarint(aData+i, &iPrefix);
}else{
iPrefix = 0;
}
i += getVarint(aData+i, &nTerm);
if( iPrefix+nTerm+1 >= sizeof(zTerm) ){
fprintf(stderr, "term to long\n");
exit(1);
}
memcpy(zTerm+iPrefix, aData+i, nTerm);
zTerm[iPrefix+nTerm] = 0;
i += nTerm;
if( iHeight==0 ){
i += getVarint(aData+i, &iDocsz);
printf("term: %-25s doclist %7lld bytes offset %d\n", zTerm, iDocsz, i);
i += iDocsz;
}else{
printf("term: %-25s child %lld\n", zTerm, ++iChild);
}
}
}
/*
** Print a a blob as hex and ascii.
*/
static void printBlob(
const unsigned char *aData, /* Content to print */
int nData /* Number of bytes of content */
){
int i, j;
const char *zOfstFmt;
const int perLine = 16;
if( (nData&~0xfff)==0 ){
zOfstFmt = " %03x: ";
}else if( (nData&~0xffff)==0 ){
zOfstFmt = " %04x: ";
}else if( (nData&~0xfffff)==0 ){
zOfstFmt = " %05x: ";
}else if( (nData&~0xffffff)==0 ){
zOfstFmt = " %06x: ";
}else{
zOfstFmt = " %08x: ";
}
for(i=0; i<nData; i += perLine){
fprintf(stdout, zOfstFmt, i);
for(j=0; j<perLine; j++){
if( i+j>nData ){
fprintf(stdout, " ");
}else{
fprintf(stdout,"%02x ", aData[i+j]);
}
}
for(j=0; j<perLine; j++){
if( i+j>nData ){
fprintf(stdout, " ");
}else{
fprintf(stdout,"%c", isprint(aData[i+j]) ? aData[i+j] : '.');
}
}
fprintf(stdout,"\n");
}
}
/*
** Convert text to a 64-bit integer
*/
static sqlite3_int64 atoi64(const char *z){
sqlite3_int64 v = 0;
while( z[0]>='0' && z[0]<='9' ){
v = v*10 + z[0] - '0';
z++;
}
return v;
}
/*
** Return a prepared statement which, when stepped, will return in its
** first column the blob associated with segment zId. If zId begins with
** 'r' then it is a rowid of a %_segdir entry. Otherwise it is a
** %_segment entry.
*/
static sqlite3_stmt *prepareToGetSegment(
sqlite3 *db, /* The database */
const char *zTab, /* The FTS3/4 table name */
const char *zId /* ID of the segment to open */
){
sqlite3_stmt *pStmt;
if( zId[0]=='r' ){
pStmt = prepare(db, "SELECT root FROM '%q_segdir' WHERE rowid=%lld",
zTab, atoi64(zId+1));
}else{
pStmt = prepare(db, "SELECT block FROM '%q_segments' WHERE blockid=%lld",
zTab, atoi64(zId));
}
return pStmt;
}
/*
** Print the content of a segment or of the root of a segdir. The segment
** or root is identified by azExtra[0]. If the first character of azExtra[0]
** is 'r' then the remainder is the integer rowid of the %_segdir entry.
** If the first character of azExtra[0] is not 'r' then, then all of
** azExtra[0] is an integer which is the block number.
**
** If the --raw option is present in azExtra, then a hex dump is provided.
** Otherwise a decoding is shown.
*/
static void showSegment(sqlite3 *db, const char *zTab){
const unsigned char *aData;
int nData;
sqlite3_stmt *pStmt;
pStmt = prepareToGetSegment(db, zTab, azExtra[0]);
if( sqlite3_step(pStmt)!=SQLITE_ROW ){
sqlite3_finalize(pStmt);
return;
}
nData = sqlite3_column_bytes(pStmt, 0);
aData = sqlite3_column_blob(pStmt, 0);
printf("Segment %s of size %d bytes:\n", azExtra[0], nData);
if( findOption("raw", 0, 0)!=0 ){
printBlob(aData, nData);
}else{
decodeSegment(aData, nData);
}
sqlite3_finalize(pStmt);
}
/*
** Decode a single doclist and display the results on stdout.
*/
static void decodeDoclist(
const unsigned char *aData, /* Content to print */
int nData /* Number of bytes of content */
){
sqlite3_int64 iPrevDocid = 0;
sqlite3_int64 iDocid;
sqlite3_int64 iPos;
sqlite3_int64 iPrevPos = 0;
sqlite3_int64 iCol;
int i = 0;
while( i<nData ){
i += getVarint(aData+i, &iDocid);
printf("docid %lld col0", iDocid+iPrevDocid);
iPrevDocid += iDocid;
iPrevPos = 0;
while( 1 ){
i += getVarint(aData+i, &iPos);
if( iPos==1 ){
i += getVarint(aData+i, &iCol);
printf(" col%lld", iCol);
iPrevPos = 0;
}else if( iPos==0 ){
printf("\n");
break;
}else{
iPrevPos += iPos - 2;
printf(" %lld", iPrevPos);
}
}
}
}
/*
** Print the content of a doclist. The segment or segdir-root is
** identified by azExtra[0]. If the first character of azExtra[0]
** is 'r' then the remainder is the integer rowid of the %_segdir entry.
** If the first character of azExtra[0] is not 'r' then, then all of
** azExtra[0] is an integer which is the block number. The offset
** into the segment is identified by azExtra[1]. The size of the doclist
** is azExtra[2].
**
** If the --raw option is present in azExtra, then a hex dump is provided.
** Otherwise a decoding is shown.
*/
static void showDoclist(sqlite3 *db, const char *zTab){
const unsigned char *aData;
sqlite3_int64 offset, nData;
sqlite3_stmt *pStmt;
offset = atoi64(azExtra[1]);
nData = atoi64(azExtra[2]);
pStmt = prepareToGetSegment(db, zTab, azExtra[0]);
if( sqlite3_step(pStmt)!=SQLITE_ROW ){
sqlite3_finalize(pStmt);
return;
}
aData = sqlite3_column_blob(pStmt, 0);
printf("Doclist at %s offset %lld of size %lld bytes:\n",
azExtra[0], offset, nData);
if( findOption("raw", 0, 0)!=0 ){
printBlob(aData+offset, nData);
}else{
decodeDoclist(aData+offset, nData);
}
sqlite3_finalize(pStmt);
}
/*
** Show the top N largest segments
*/
static void listBigSegments(sqlite3 *db, const char *zTab){
int nTop, i;
sqlite3_stmt *pStmt;
sqlite3_int64 sz;
sqlite3_int64 id;
nTop = atoi(findOption("top", 1, "25"));
printf("The %d largest segments:\n", nTop);
pStmt = prepare(db,
"SELECT blockid, length(block) AS len FROM '%q_segments'"
" ORDER BY 2 DESC, 1"
" LIMIT %d", zTab, nTop);
i = 0;
while( sqlite3_step(pStmt)==SQLITE_ROW ){
i++;
id = sqlite3_column_int64(pStmt, 0);
sz = sqlite3_column_int64(pStmt, 1);
printf(" %2d. %9lld size %lld\n", i, id, sz);
}
sqlite3_finalize(pStmt);
}
static void usage(const char *argv0){
fprintf(stderr, "Usage: %s DATABASE\n"
" or: %s DATABASE FTS3TABLE ARGS...\n", argv0, argv0);
fprintf(stderr,
"ARGS:\n"
" big-segments [--top N] show the largest segments\n"
" doclist BLOCKID OFFSET SIZE [--raw] Decode a doclist\n"
" schema FTS table schema\n"
" segdir directory of segments\n"
" segment BLOCKID [--raw] content of a segment\n"
" segment-stats info on segment sizes\n"
" stat the %%_stat table\n"
" vocabulary [--top N] document vocabulary\n"
);
exit(1);
}
int main(int argc, char **argv){
sqlite3 *db;
int rc;
const char *zTab;
const char *zCmd;
if( argc<2 ) usage(argv[0]);
rc = sqlite3_open(argv[1], &db);
if( rc ){
fprintf(stderr, "Cannot open %s\n", argv[1]);
exit(1);
}
if( argc==2 ){
sqlite3_stmt *pStmt;
int cnt = 0;
pStmt = prepare(db, "SELECT b.sql"
" FROM sqlite_master a, sqlite_master b"
" WHERE a.name GLOB '*_segdir'"
" AND b.name=substr(a.name,1,length(a.name)-7)"
" ORDER BY 1");
while( sqlite3_step(pStmt)==SQLITE_ROW ){
cnt++;
printf("%s;\n", sqlite3_column_text(pStmt, 0));
}
sqlite3_finalize(pStmt);
if( cnt==0 ){
printf("/* No FTS3/4 tables found in database %s */\n", argv[1]);
}
return 0;
}
if( argc<4 ) usage(argv[0]);
zTab = argv[2];
zCmd = argv[3];
nExtra = argc-4;
azExtra = argv+4;
if( strcmp(zCmd,"big-segments")==0 ){
listBigSegments(db, zTab);
}else if( strcmp(zCmd,"doclist")==0 ){
if( argc<7 ) usage(argv[0]);
showDoclist(db, zTab);
}else if( strcmp(zCmd,"schema")==0 ){
showSchema(db, zTab);
}else if( strcmp(zCmd,"segdir")==0 ){
showSegdirMap(db, zTab);
}else if( strcmp(zCmd,"segment")==0 ){
if( argc<5 ) usage(argv[0]);
showSegment(db, zTab);
}else if( strcmp(zCmd,"segment-stats")==0 ){
showSegmentStats(db, zTab);
}else if( strcmp(zCmd,"stat")==0 ){
showStat(db, zTab);
}else if( strcmp(zCmd,"vocabulary")==0 ){
showVocabulary(db, zTab);
}else{
usage(argv[0]);
}
return 0;
}

View File

@ -182,6 +182,19 @@ struct Rtree {
#define RTREE_COORD_REAL32 0 #define RTREE_COORD_REAL32 0
#define RTREE_COORD_INT32 1 #define RTREE_COORD_INT32 1
/*
** If SQLITE_RTREE_INT_ONLY is defined, then this virtual table will
** only deal with integer coordinates. No floating point operations
** will be done.
*/
#ifdef SQLITE_RTREE_INT_ONLY
typedef sqlite3_int64 RtreeDValue; /* High accuracy coordinate */
typedef int RtreeValue; /* Low accuracy coordinate */
#else
typedef double RtreeDValue; /* High accuracy coordinate */
typedef float RtreeValue; /* Low accuracy coordinate */
#endif
/* /*
** The minimum number of cells allowed for a node is a third of the ** The minimum number of cells allowed for a node is a third of the
** maximum. In Gutman's notation: ** maximum. In Gutman's notation:
@ -217,20 +230,25 @@ struct RtreeCursor {
}; };
union RtreeCoord { union RtreeCoord {
float f; RtreeValue f;
int i; int i;
}; };
/* /*
** The argument is an RtreeCoord. Return the value stored within the RtreeCoord ** The argument is an RtreeCoord. Return the value stored within the RtreeCoord
** formatted as a double. This macro assumes that local variable pRtree points ** formatted as a RtreeDValue (double or int64). This macro assumes that local
** to the Rtree structure associated with the RtreeCoord. ** variable pRtree points to the Rtree structure associated with the
** RtreeCoord.
*/ */
#define DCOORD(coord) ( \ #ifdef SQLITE_RTREE_INT_ONLY
(pRtree->eCoordType==RTREE_COORD_REAL32) ? \ # define DCOORD(coord) ((RtreeDValue)coord.i)
((double)coord.f) : \ #else
((double)coord.i) \ # define DCOORD(coord) ( \
) (pRtree->eCoordType==RTREE_COORD_REAL32) ? \
((double)coord.f) : \
((double)coord.i) \
)
#endif
/* /*
** A search constraint. ** A search constraint.
@ -238,8 +256,8 @@ union RtreeCoord {
struct RtreeConstraint { struct RtreeConstraint {
int iCoord; /* Index of constrained coordinate */ int iCoord; /* Index of constrained coordinate */
int op; /* Constraining operation */ int op; /* Constraining operation */
double rValue; /* Constraint value. */ RtreeDValue rValue; /* Constraint value. */
int (*xGeom)(sqlite3_rtree_geometry *, int, double *, int *); int (*xGeom)(sqlite3_rtree_geometry*, int, RtreeDValue*, int*);
sqlite3_rtree_geometry *pGeom; /* Constraint callback argument for a MATCH */ sqlite3_rtree_geometry *pGeom; /* Constraint callback argument for a MATCH */
}; };
@ -287,10 +305,10 @@ struct RtreeCell {
*/ */
struct RtreeMatchArg { struct RtreeMatchArg {
u32 magic; /* Always RTREE_GEOMETRY_MAGIC */ u32 magic; /* Always RTREE_GEOMETRY_MAGIC */
int (*xGeom)(sqlite3_rtree_geometry *, int, double *, int *); int (*xGeom)(sqlite3_rtree_geometry *, int, RtreeDValue*, int *);
void *pContext; void *pContext;
int nParam; int nParam;
double aParam[1]; RtreeDValue aParam[1];
}; };
/* /*
@ -302,7 +320,7 @@ struct RtreeMatchArg {
** the geometry callback function). ** the geometry callback function).
*/ */
struct RtreeGeomCallback { struct RtreeGeomCallback {
int (*xGeom)(sqlite3_rtree_geometry *, int, double *, int *); int (*xGeom)(sqlite3_rtree_geometry*, int, RtreeDValue*, int*);
void *pContext; void *pContext;
}; };
@ -868,7 +886,7 @@ static int testRtreeGeom(
int *pbRes /* OUT: Test result */ int *pbRes /* OUT: Test result */
){ ){
int i; int i;
double aCoord[RTREE_MAX_DIMENSIONS*2]; RtreeDValue aCoord[RTREE_MAX_DIMENSIONS*2];
int nCoord = pRtree->nDim*2; int nCoord = pRtree->nDim*2;
assert( pConstraint->op==RTREE_MATCH ); assert( pConstraint->op==RTREE_MATCH );
@ -898,8 +916,8 @@ static int testRtreeCell(Rtree *pRtree, RtreeCursor *pCursor, int *pbEof){
nodeGetCell(pRtree, pCursor->pNode, pCursor->iCell, &cell); nodeGetCell(pRtree, pCursor->pNode, pCursor->iCell, &cell);
for(ii=0; bRes==0 && ii<pCursor->nConstraint; ii++){ for(ii=0; bRes==0 && ii<pCursor->nConstraint; ii++){
RtreeConstraint *p = &pCursor->aConstraint[ii]; RtreeConstraint *p = &pCursor->aConstraint[ii];
double cell_min = DCOORD(cell.aCoord[(p->iCoord>>1)*2]); RtreeDValue cell_min = DCOORD(cell.aCoord[(p->iCoord>>1)*2]);
double cell_max = DCOORD(cell.aCoord[(p->iCoord>>1)*2+1]); RtreeDValue cell_max = DCOORD(cell.aCoord[(p->iCoord>>1)*2+1]);
assert(p->op==RTREE_LE || p->op==RTREE_LT || p->op==RTREE_GE assert(p->op==RTREE_LE || p->op==RTREE_LT || p->op==RTREE_GE
|| p->op==RTREE_GT || p->op==RTREE_EQ || p->op==RTREE_MATCH || p->op==RTREE_GT || p->op==RTREE_EQ || p->op==RTREE_MATCH
@ -951,7 +969,7 @@ static int testRtreeEntry(Rtree *pRtree, RtreeCursor *pCursor, int *pbEof){
nodeGetCell(pRtree, pCursor->pNode, pCursor->iCell, &cell); nodeGetCell(pRtree, pCursor->pNode, pCursor->iCell, &cell);
for(ii=0; ii<pCursor->nConstraint; ii++){ for(ii=0; ii<pCursor->nConstraint; ii++){
RtreeConstraint *p = &pCursor->aConstraint[ii]; RtreeConstraint *p = &pCursor->aConstraint[ii];
double coord = DCOORD(cell.aCoord[p->iCoord]); RtreeDValue coord = DCOORD(cell.aCoord[p->iCoord]);
int res; int res;
assert(p->op==RTREE_LE || p->op==RTREE_LT || p->op==RTREE_GE assert(p->op==RTREE_LE || p->op==RTREE_LT || p->op==RTREE_GE
|| p->op==RTREE_GT || p->op==RTREE_EQ || p->op==RTREE_MATCH || p->op==RTREE_GT || p->op==RTREE_EQ || p->op==RTREE_MATCH
@ -1149,9 +1167,12 @@ static int rtreeColumn(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int i){
}else{ }else{
RtreeCoord c; RtreeCoord c;
nodeGetCoord(pRtree, pCsr->pNode, pCsr->iCell, i-1, &c); nodeGetCoord(pRtree, pCsr->pNode, pCsr->iCell, i-1, &c);
#ifndef SQLITE_RTREE_INT_ONLY
if( pRtree->eCoordType==RTREE_COORD_REAL32 ){ if( pRtree->eCoordType==RTREE_COORD_REAL32 ){
sqlite3_result_double(ctx, c.f); sqlite3_result_double(ctx, c.f);
}else{ }else
#endif
{
assert( pRtree->eCoordType==RTREE_COORD_INT32 ); assert( pRtree->eCoordType==RTREE_COORD_INT32 );
sqlite3_result_int(ctx, c.i); sqlite3_result_int(ctx, c.i);
} }
@ -1198,7 +1219,7 @@ static int deserializeGeometry(sqlite3_value *pValue, RtreeConstraint *pCons){
/* Check that the blob is roughly the right size. */ /* Check that the blob is roughly the right size. */
nBlob = sqlite3_value_bytes(pValue); nBlob = sqlite3_value_bytes(pValue);
if( nBlob<(int)sizeof(RtreeMatchArg) if( nBlob<(int)sizeof(RtreeMatchArg)
|| ((nBlob-sizeof(RtreeMatchArg))%sizeof(double))!=0 || ((nBlob-sizeof(RtreeMatchArg))%sizeof(RtreeDValue))!=0
){ ){
return SQLITE_ERROR; return SQLITE_ERROR;
} }
@ -1212,7 +1233,7 @@ static int deserializeGeometry(sqlite3_value *pValue, RtreeConstraint *pCons){
memcpy(p, sqlite3_value_blob(pValue), nBlob); memcpy(p, sqlite3_value_blob(pValue), nBlob);
if( p->magic!=RTREE_GEOMETRY_MAGIC if( p->magic!=RTREE_GEOMETRY_MAGIC
|| nBlob!=(int)(sizeof(RtreeMatchArg) + (p->nParam-1)*sizeof(double)) || nBlob!=(int)(sizeof(RtreeMatchArg) + (p->nParam-1)*sizeof(RtreeDValue))
){ ){
sqlite3_free(pGeom); sqlite3_free(pGeom);
return SQLITE_ERROR; return SQLITE_ERROR;
@ -1284,7 +1305,11 @@ static int rtreeFilter(
break; break;
} }
}else{ }else{
#ifdef SQLITE_RTREE_INT_ONLY
p->rValue = sqlite3_value_int64(argv[ii]);
#else
p->rValue = sqlite3_value_double(argv[ii]); p->rValue = sqlite3_value_double(argv[ii]);
#endif
} }
} }
} }
@ -1418,11 +1443,11 @@ static int rtreeBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
/* /*
** Return the N-dimensional volumn of the cell stored in *p. ** Return the N-dimensional volumn of the cell stored in *p.
*/ */
static float cellArea(Rtree *pRtree, RtreeCell *p){ static RtreeDValue cellArea(Rtree *pRtree, RtreeCell *p){
float area = 1.0; RtreeDValue area = (RtreeDValue)1;
int ii; int ii;
for(ii=0; ii<(pRtree->nDim*2); ii+=2){ for(ii=0; ii<(pRtree->nDim*2); ii+=2){
area = (float)(area * (DCOORD(p->aCoord[ii+1]) - DCOORD(p->aCoord[ii]))); area = (area * (DCOORD(p->aCoord[ii+1]) - DCOORD(p->aCoord[ii])));
} }
return area; return area;
} }
@ -1431,11 +1456,11 @@ static float cellArea(Rtree *pRtree, RtreeCell *p){
** Return the margin length of cell p. The margin length is the sum ** Return the margin length of cell p. The margin length is the sum
** of the objects size in each dimension. ** of the objects size in each dimension.
*/ */
static float cellMargin(Rtree *pRtree, RtreeCell *p){ static RtreeDValue cellMargin(Rtree *pRtree, RtreeCell *p){
float margin = 0.0; RtreeDValue margin = (RtreeDValue)0;
int ii; int ii;
for(ii=0; ii<(pRtree->nDim*2); ii+=2){ for(ii=0; ii<(pRtree->nDim*2); ii+=2){
margin += (float)(DCOORD(p->aCoord[ii+1]) - DCOORD(p->aCoord[ii])); margin += (DCOORD(p->aCoord[ii+1]) - DCOORD(p->aCoord[ii]));
} }
return margin; return margin;
} }
@ -1480,8 +1505,8 @@ static int cellContains(Rtree *pRtree, RtreeCell *p1, RtreeCell *p2){
/* /*
** Return the amount cell p would grow by if it were unioned with pCell. ** Return the amount cell p would grow by if it were unioned with pCell.
*/ */
static float cellGrowth(Rtree *pRtree, RtreeCell *p, RtreeCell *pCell){ static RtreeDValue cellGrowth(Rtree *pRtree, RtreeCell *p, RtreeCell *pCell){
float area; RtreeDValue area;
RtreeCell cell; RtreeCell cell;
memcpy(&cell, p, sizeof(RtreeCell)); memcpy(&cell, p, sizeof(RtreeCell));
area = cellArea(pRtree, &cell); area = cellArea(pRtree, &cell);
@ -1490,7 +1515,7 @@ static float cellGrowth(Rtree *pRtree, RtreeCell *p, RtreeCell *pCell){
} }
#if VARIANT_RSTARTREE_CHOOSESUBTREE || VARIANT_RSTARTREE_SPLIT #if VARIANT_RSTARTREE_CHOOSESUBTREE || VARIANT_RSTARTREE_SPLIT
static float cellOverlap( static RtreeDValue cellOverlap(
Rtree *pRtree, Rtree *pRtree,
RtreeCell *p, RtreeCell *p,
RtreeCell *aCell, RtreeCell *aCell,
@ -1498,7 +1523,7 @@ static float cellOverlap(
int iExclude int iExclude
){ ){
int ii; int ii;
float overlap = 0.0; RtreeDValue overlap = 0.0;
for(ii=0; ii<nCell; ii++){ for(ii=0; ii<nCell; ii++){
#if VARIANT_RSTARTREE_CHOOSESUBTREE #if VARIANT_RSTARTREE_CHOOSESUBTREE
if( ii!=iExclude ) if( ii!=iExclude )
@ -1508,10 +1533,9 @@ static float cellOverlap(
#endif #endif
{ {
int jj; int jj;
float o = 1.0; RtreeDValue o = (RtreeDValue)1;
for(jj=0; jj<(pRtree->nDim*2); jj+=2){ for(jj=0; jj<(pRtree->nDim*2); jj+=2){
double x1; RtreeDValue x1, x2;
double x2;
x1 = MAX(DCOORD(p->aCoord[jj]), DCOORD(aCell[ii].aCoord[jj])); x1 = MAX(DCOORD(p->aCoord[jj]), DCOORD(aCell[ii].aCoord[jj]));
x2 = MIN(DCOORD(p->aCoord[jj+1]), DCOORD(aCell[ii].aCoord[jj+1])); x2 = MIN(DCOORD(p->aCoord[jj+1]), DCOORD(aCell[ii].aCoord[jj+1]));
@ -1520,7 +1544,7 @@ static float cellOverlap(
o = 0.0; o = 0.0;
break; break;
}else{ }else{
o = o * (float)(x2-x1); o = o * (x2-x1);
} }
} }
overlap += o; overlap += o;
@ -1531,7 +1555,7 @@ static float cellOverlap(
#endif #endif
#if VARIANT_RSTARTREE_CHOOSESUBTREE #if VARIANT_RSTARTREE_CHOOSESUBTREE
static float cellOverlapEnlargement( static RtreeDValue cellOverlapEnlargement(
Rtree *pRtree, Rtree *pRtree,
RtreeCell *p, RtreeCell *p,
RtreeCell *pInsert, RtreeCell *pInsert,
@ -1539,12 +1563,11 @@ static float cellOverlapEnlargement(
int nCell, int nCell,
int iExclude int iExclude
){ ){
double before; RtreeDValue before, after;
double after;
before = cellOverlap(pRtree, p, aCell, nCell, iExclude); before = cellOverlap(pRtree, p, aCell, nCell, iExclude);
cellUnion(pRtree, p, pInsert); cellUnion(pRtree, p, pInsert);
after = cellOverlap(pRtree, p, aCell, nCell, iExclude); after = cellOverlap(pRtree, p, aCell, nCell, iExclude);
return (float)(after-before); return (after-before);
} }
#endif #endif
@ -1568,11 +1591,11 @@ static int ChooseLeaf(
int iCell; int iCell;
sqlite3_int64 iBest = 0; sqlite3_int64 iBest = 0;
float fMinGrowth = 0.0; RtreeDValue fMinGrowth = 0.0;
float fMinArea = 0.0; RtreeDValue fMinArea = 0.0;
#if VARIANT_RSTARTREE_CHOOSESUBTREE #if VARIANT_RSTARTREE_CHOOSESUBTREE
float fMinOverlap = 0.0; RtreeDValue fMinOverlap = 0.0;
float overlap; RtreeDValue overlap;
#endif #endif
int nCell = NCELL(pNode); int nCell = NCELL(pNode);
@ -1603,8 +1626,8 @@ static int ChooseLeaf(
*/ */
for(iCell=0; iCell<nCell; iCell++){ for(iCell=0; iCell<nCell; iCell++){
int bBest = 0; int bBest = 0;
float growth; RtreeDValue growth;
float area; RtreeDValue area;
nodeGetCell(pRtree, pNode, iCell, &cell); nodeGetCell(pRtree, pNode, iCell, &cell);
growth = cellGrowth(pRtree, &cell, pCell); growth = cellGrowth(pRtree, &cell, pCell);
area = cellArea(pRtree, &cell); area = cellArea(pRtree, &cell);
@ -1731,7 +1754,7 @@ static void LinearPickSeeds(
int i; int i;
int iLeftSeed = 0; int iLeftSeed = 0;
int iRightSeed = 1; int iRightSeed = 1;
float maxNormalInnerWidth = 0.0; RtreeDValue maxNormalInnerWidth = (RtreeDValue)0;
/* Pick two "seed" cells from the array of cells. The algorithm used /* Pick two "seed" cells from the array of cells. The algorithm used
** here is the LinearPickSeeds algorithm from Gutman[1984]. The ** here is the LinearPickSeeds algorithm from Gutman[1984]. The
@ -1739,18 +1762,18 @@ static void LinearPickSeeds(
** variables iLeftSeek and iRightSeed. ** variables iLeftSeek and iRightSeed.
*/ */
for(i=0; i<pRtree->nDim; i++){ for(i=0; i<pRtree->nDim; i++){
float x1 = DCOORD(aCell[0].aCoord[i*2]); RtreeDValue x1 = DCOORD(aCell[0].aCoord[i*2]);
float x2 = DCOORD(aCell[0].aCoord[i*2+1]); RtreeDValue x2 = DCOORD(aCell[0].aCoord[i*2+1]);
float x3 = x1; RtreeDValue x3 = x1;
float x4 = x2; RtreeDValue x4 = x2;
int jj; int jj;
int iCellLeft = 0; int iCellLeft = 0;
int iCellRight = 0; int iCellRight = 0;
for(jj=1; jj<nCell; jj++){ for(jj=1; jj<nCell; jj++){
float left = DCOORD(aCell[jj].aCoord[i*2]); RtreeDValue left = DCOORD(aCell[jj].aCoord[i*2]);
float right = DCOORD(aCell[jj].aCoord[i*2+1]); RtreeDValue right = DCOORD(aCell[jj].aCoord[i*2+1]);
if( left<x1 ) x1 = left; if( left<x1 ) x1 = left;
if( right>x4 ) x4 = right; if( right>x4 ) x4 = right;
@ -1765,7 +1788,7 @@ static void LinearPickSeeds(
} }
if( x4!=x1 ){ if( x4!=x1 ){
float normalwidth = (x3 - x2) / (x4 - x1); RtreeDValue normalwidth = (x3 - x2) / (x4 - x1);
if( normalwidth>maxNormalInnerWidth ){ if( normalwidth>maxNormalInnerWidth ){
iLeftSeed = iCellLeft; iLeftSeed = iCellLeft;
iRightSeed = iCellRight; iRightSeed = iCellRight;
@ -1794,13 +1817,13 @@ static RtreeCell *QuadraticPickNext(
#define FABS(a) ((a)<0.0?-1.0*(a):(a)) #define FABS(a) ((a)<0.0?-1.0*(a):(a))
int iSelect = -1; int iSelect = -1;
float fDiff; RtreeDValue fDiff;
int ii; int ii;
for(ii=0; ii<nCell; ii++){ for(ii=0; ii<nCell; ii++){
if( aiUsed[ii]==0 ){ if( aiUsed[ii]==0 ){
float left = cellGrowth(pRtree, pLeftBox, &aCell[ii]); RtreeDValue left = cellGrowth(pRtree, pLeftBox, &aCell[ii]);
float right = cellGrowth(pRtree, pLeftBox, &aCell[ii]); RtreeDValue right = cellGrowth(pRtree, pLeftBox, &aCell[ii]);
float diff = FABS(right-left); RtreeDValue diff = FABS(right-left);
if( iSelect<0 || diff>fDiff ){ if( iSelect<0 || diff>fDiff ){
fDiff = diff; fDiff = diff;
iSelect = ii; iSelect = ii;
@ -1827,13 +1850,13 @@ static void QuadraticPickSeeds(
int iLeftSeed = 0; int iLeftSeed = 0;
int iRightSeed = 1; int iRightSeed = 1;
float fWaste = 0.0; RtreeDValue fWaste = 0.0;
for(ii=0; ii<nCell; ii++){ for(ii=0; ii<nCell; ii++){
for(jj=ii+1; jj<nCell; jj++){ for(jj=ii+1; jj<nCell; jj++){
float right = cellArea(pRtree, &aCell[jj]); RtreeDValue right = cellArea(pRtree, &aCell[jj]);
float growth = cellGrowth(pRtree, &aCell[ii], &aCell[jj]); RtreeDValue growth = cellGrowth(pRtree, &aCell[ii], &aCell[jj]);
float waste = growth - right; RtreeDValue waste = growth - right;
if( waste>fWaste ){ if( waste>fWaste ){
iLeftSeed = ii; iLeftSeed = ii;
@ -1868,7 +1891,7 @@ static void QuadraticPickSeeds(
static void SortByDistance( static void SortByDistance(
int *aIdx, int *aIdx,
int nIdx, int nIdx,
float *aDistance, RtreeDValue *aDistance,
int *aSpare int *aSpare
){ ){
if( nIdx>1 ){ if( nIdx>1 ){
@ -1894,8 +1917,8 @@ static void SortByDistance(
aIdx[iLeft+iRight] = aLeft[iLeft]; aIdx[iLeft+iRight] = aLeft[iLeft];
iLeft++; iLeft++;
}else{ }else{
float fLeft = aDistance[aLeft[iLeft]]; RtreeDValue fLeft = aDistance[aLeft[iLeft]];
float fRight = aDistance[aRight[iRight]]; RtreeDValue fRight = aDistance[aRight[iRight]];
if( fLeft<fRight ){ if( fLeft<fRight ){
aIdx[iLeft+iRight] = aLeft[iLeft]; aIdx[iLeft+iRight] = aLeft[iLeft];
iLeft++; iLeft++;
@ -1911,8 +1934,8 @@ static void SortByDistance(
{ {
int jj; int jj;
for(jj=1; jj<nIdx; jj++){ for(jj=1; jj<nIdx; jj++){
float left = aDistance[aIdx[jj-1]]; RtreeDValue left = aDistance[aIdx[jj-1]];
float right = aDistance[aIdx[jj]]; RtreeDValue right = aDistance[aIdx[jj]];
assert( left<=right ); assert( left<=right );
} }
} }
@ -1955,10 +1978,10 @@ static void SortByDimension(
memcpy(aSpare, aLeft, sizeof(int)*nLeft); memcpy(aSpare, aLeft, sizeof(int)*nLeft);
aLeft = aSpare; aLeft = aSpare;
while( iLeft<nLeft || iRight<nRight ){ while( iLeft<nLeft || iRight<nRight ){
double xleft1 = DCOORD(aCell[aLeft[iLeft]].aCoord[iDim*2]); RtreeDValue xleft1 = DCOORD(aCell[aLeft[iLeft]].aCoord[iDim*2]);
double xleft2 = DCOORD(aCell[aLeft[iLeft]].aCoord[iDim*2+1]); RtreeDValue xleft2 = DCOORD(aCell[aLeft[iLeft]].aCoord[iDim*2+1]);
double xright1 = DCOORD(aCell[aRight[iRight]].aCoord[iDim*2]); RtreeDValue xright1 = DCOORD(aCell[aRight[iRight]].aCoord[iDim*2]);
double xright2 = DCOORD(aCell[aRight[iRight]].aCoord[iDim*2+1]); RtreeDValue xright2 = DCOORD(aCell[aRight[iRight]].aCoord[iDim*2+1]);
if( (iLeft!=nLeft) && ((iRight==nRight) if( (iLeft!=nLeft) && ((iRight==nRight)
|| (xleft1<xright1) || (xleft1<xright1)
|| (xleft1==xright1 && xleft2<xright2) || (xleft1==xright1 && xleft2<xright2)
@ -1976,10 +1999,10 @@ static void SortByDimension(
{ {
int jj; int jj;
for(jj=1; jj<nIdx; jj++){ for(jj=1; jj<nIdx; jj++){
float xleft1 = aCell[aIdx[jj-1]].aCoord[iDim*2]; RtreeDValue xleft1 = aCell[aIdx[jj-1]].aCoord[iDim*2];
float xleft2 = aCell[aIdx[jj-1]].aCoord[iDim*2+1]; RtreeDValue xleft2 = aCell[aIdx[jj-1]].aCoord[iDim*2+1];
float xright1 = aCell[aIdx[jj]].aCoord[iDim*2]; RtreeDValue xright1 = aCell[aIdx[jj]].aCoord[iDim*2];
float xright2 = aCell[aIdx[jj]].aCoord[iDim*2+1]; RtreeDValue xright2 = aCell[aIdx[jj]].aCoord[iDim*2+1];
assert( xleft1<=xright1 && (xleft1<xright1 || xleft2<=xright2) ); assert( xleft1<=xright1 && (xleft1<xright1 || xleft2<=xright2) );
} }
} }
@ -2006,7 +2029,7 @@ static int splitNodeStartree(
int iBestDim = 0; int iBestDim = 0;
int iBestSplit = 0; int iBestSplit = 0;
float fBestMargin = 0.0; RtreeDValue fBestMargin = 0.0;
int nByte = (pRtree->nDim+1)*(sizeof(int*)+nCell*sizeof(int)); int nByte = (pRtree->nDim+1)*(sizeof(int*)+nCell*sizeof(int));
@ -2027,9 +2050,9 @@ static int splitNodeStartree(
} }
for(ii=0; ii<pRtree->nDim; ii++){ for(ii=0; ii<pRtree->nDim; ii++){
float margin = 0.0; RtreeDValue margin = 0.0;
float fBestOverlap = 0.0; RtreeDValue fBestOverlap = 0.0;
float fBestArea = 0.0; RtreeDValue fBestArea = 0.0;
int iBestLeft = 0; int iBestLeft = 0;
int nLeft; int nLeft;
@ -2041,8 +2064,8 @@ static int splitNodeStartree(
RtreeCell left; RtreeCell left;
RtreeCell right; RtreeCell right;
int kk; int kk;
float overlap; RtreeDValue overlap;
float area; RtreeDValue area;
memcpy(&left, &aCell[aaSorted[ii][0]], sizeof(RtreeCell)); memcpy(&left, &aCell[aaSorted[ii][0]], sizeof(RtreeCell));
memcpy(&right, &aCell[aaSorted[ii][nCell-1]], sizeof(RtreeCell)); memcpy(&right, &aCell[aaSorted[ii][nCell-1]], sizeof(RtreeCell));
@ -2125,7 +2148,7 @@ static int splitNodeGuttman(
for(i=nCell-2; i>0; i--){ for(i=nCell-2; i>0; i--){
RtreeCell *pNext; RtreeCell *pNext;
pNext = PickNext(pRtree, aCell, nCell, pBboxLeft, pBboxRight, aiUsed); pNext = PickNext(pRtree, aCell, nCell, pBboxLeft, pBboxRight, aiUsed);
float diff = RtreeDValue diff =
cellGrowth(pRtree, pBboxLeft, pNext) - cellGrowth(pRtree, pBboxLeft, pNext) -
cellGrowth(pRtree, pBboxRight, pNext) cellGrowth(pRtree, pBboxRight, pNext)
; ;
@ -2458,32 +2481,34 @@ static int Reinsert(
int *aOrder; int *aOrder;
int *aSpare; int *aSpare;
RtreeCell *aCell; RtreeCell *aCell;
float *aDistance; RtreeDValue *aDistance;
int nCell; int nCell;
float aCenterCoord[RTREE_MAX_DIMENSIONS]; RtreeDValue aCenterCoord[RTREE_MAX_DIMENSIONS];
int iDim; int iDim;
int ii; int ii;
int rc = SQLITE_OK; int rc = SQLITE_OK;
int n;
memset(aCenterCoord, 0, sizeof(float)*RTREE_MAX_DIMENSIONS); memset(aCenterCoord, 0, sizeof(RtreeDValue)*RTREE_MAX_DIMENSIONS);
nCell = NCELL(pNode)+1; nCell = NCELL(pNode)+1;
n = (nCell+1)&(~1);
/* Allocate the buffers used by this operation. The allocation is /* Allocate the buffers used by this operation. The allocation is
** relinquished before this function returns. ** relinquished before this function returns.
*/ */
aCell = (RtreeCell *)sqlite3_malloc(nCell * ( aCell = (RtreeCell *)sqlite3_malloc(n * (
sizeof(RtreeCell) + /* aCell array */ sizeof(RtreeCell) + /* aCell array */
sizeof(int) + /* aOrder array */ sizeof(int) + /* aOrder array */
sizeof(int) + /* aSpare array */ sizeof(int) + /* aSpare array */
sizeof(float) /* aDistance array */ sizeof(RtreeDValue) /* aDistance array */
)); ));
if( !aCell ){ if( !aCell ){
return SQLITE_NOMEM; return SQLITE_NOMEM;
} }
aOrder = (int *)&aCell[nCell]; aOrder = (int *)&aCell[n];
aSpare = (int *)&aOrder[nCell]; aSpare = (int *)&aOrder[n];
aDistance = (float *)&aSpare[nCell]; aDistance = (RtreeDValue *)&aSpare[n];
for(ii=0; ii<nCell; ii++){ for(ii=0; ii<nCell; ii++){
if( ii==(nCell-1) ){ if( ii==(nCell-1) ){
@ -2493,19 +2518,19 @@ static int Reinsert(
} }
aOrder[ii] = ii; aOrder[ii] = ii;
for(iDim=0; iDim<pRtree->nDim; iDim++){ for(iDim=0; iDim<pRtree->nDim; iDim++){
aCenterCoord[iDim] += (float)DCOORD(aCell[ii].aCoord[iDim*2]); aCenterCoord[iDim] += DCOORD(aCell[ii].aCoord[iDim*2]);
aCenterCoord[iDim] += (float)DCOORD(aCell[ii].aCoord[iDim*2+1]); aCenterCoord[iDim] += DCOORD(aCell[ii].aCoord[iDim*2+1]);
} }
} }
for(iDim=0; iDim<pRtree->nDim; iDim++){ for(iDim=0; iDim<pRtree->nDim; iDim++){
aCenterCoord[iDim] = (float)(aCenterCoord[iDim]/((float)nCell*2.0)); aCenterCoord[iDim] = (aCenterCoord[iDim]/(nCell*(RtreeDValue)2));
} }
for(ii=0; ii<nCell; ii++){ for(ii=0; ii<nCell; ii++){
aDistance[ii] = 0.0; aDistance[ii] = 0.0;
for(iDim=0; iDim<pRtree->nDim; iDim++){ for(iDim=0; iDim<pRtree->nDim; iDim++){
float coord = (float)(DCOORD(aCell[ii].aCoord[iDim*2+1]) - RtreeDValue coord = (DCOORD(aCell[ii].aCoord[iDim*2+1]) -
DCOORD(aCell[ii].aCoord[iDim*2])); DCOORD(aCell[ii].aCoord[iDim*2]));
aDistance[ii] += (coord-aCenterCoord[iDim])*(coord-aCenterCoord[iDim]); aDistance[ii] += (coord-aCenterCoord[iDim])*(coord-aCenterCoord[iDim]);
} }
} }
@ -2747,16 +2772,19 @@ static int rtreeUpdate(
/* Populate the cell.aCoord[] array. The first coordinate is azData[3]. */ /* Populate the cell.aCoord[] array. The first coordinate is azData[3]. */
assert( nData==(pRtree->nDim*2 + 3) ); assert( nData==(pRtree->nDim*2 + 3) );
#ifndef SQLITE_RTREE_INT_ONLY
if( pRtree->eCoordType==RTREE_COORD_REAL32 ){ if( pRtree->eCoordType==RTREE_COORD_REAL32 ){
for(ii=0; ii<(pRtree->nDim*2); ii+=2){ for(ii=0; ii<(pRtree->nDim*2); ii+=2){
cell.aCoord[ii].f = (float)sqlite3_value_double(azData[ii+3]); cell.aCoord[ii].f = (RtreeValue)sqlite3_value_double(azData[ii+3]);
cell.aCoord[ii+1].f = (float)sqlite3_value_double(azData[ii+4]); cell.aCoord[ii+1].f = (RtreeValue)sqlite3_value_double(azData[ii+4]);
if( cell.aCoord[ii].f>cell.aCoord[ii+1].f ){ if( cell.aCoord[ii].f>cell.aCoord[ii+1].f ){
rc = SQLITE_CONSTRAINT; rc = SQLITE_CONSTRAINT;
goto constraint; goto constraint;
} }
} }
}else{ }else
#endif
{
for(ii=0; ii<(pRtree->nDim*2); ii+=2){ for(ii=0; ii<(pRtree->nDim*2); ii+=2){
cell.aCoord[ii].i = sqlite3_value_int(azData[ii+3]); cell.aCoord[ii].i = sqlite3_value_int(azData[ii+3]);
cell.aCoord[ii+1].i = sqlite3_value_int(azData[ii+4]); cell.aCoord[ii+1].i = sqlite3_value_int(azData[ii+4]);
@ -3056,8 +3084,8 @@ static int rtreeInit(
sqlite3_vtab_config(db, SQLITE_VTAB_CONSTRAINT_SUPPORT, 1); sqlite3_vtab_config(db, SQLITE_VTAB_CONSTRAINT_SUPPORT, 1);
/* Allocate the sqlite3_vtab structure */ /* Allocate the sqlite3_vtab structure */
nDb = strlen(argv[1]); nDb = (int)strlen(argv[1]);
nName = strlen(argv[2]); nName = (int)strlen(argv[2]);
pRtree = (Rtree *)sqlite3_malloc(sizeof(Rtree)+nDb+nName+2); pRtree = (Rtree *)sqlite3_malloc(sizeof(Rtree)+nDb+nName+2);
if( !pRtree ){ if( !pRtree ){
return SQLITE_NOMEM; return SQLITE_NOMEM;
@ -3152,10 +3180,16 @@ static void rtreenode(sqlite3_context *ctx, int nArg, sqlite3_value **apArg){
nodeGetCell(&tree, &node, ii, &cell); nodeGetCell(&tree, &node, ii, &cell);
sqlite3_snprintf(512-nCell,&zCell[nCell],"%lld", cell.iRowid); sqlite3_snprintf(512-nCell,&zCell[nCell],"%lld", cell.iRowid);
nCell = strlen(zCell); nCell = (int)strlen(zCell);
for(jj=0; jj<tree.nDim*2; jj++){ for(jj=0; jj<tree.nDim*2; jj++){
sqlite3_snprintf(512-nCell,&zCell[nCell]," %f",(double)cell.aCoord[jj].f); #ifndef SQLITE_RTREE_INT_ONLY
nCell = strlen(zCell); sqlite3_snprintf(512-nCell,&zCell[nCell], " %f",
(double)cell.aCoord[jj].f);
#else
sqlite3_snprintf(512-nCell,&zCell[nCell], " %d",
cell.aCoord[jj].i);
#endif
nCell = (int)strlen(zCell);
} }
if( zText ){ if( zText ){
@ -3196,7 +3230,11 @@ int sqlite3RtreeInit(sqlite3 *db){
rc = sqlite3_create_function(db, "rtreedepth", 1, utf8, 0,rtreedepth, 0, 0); rc = sqlite3_create_function(db, "rtreedepth", 1, utf8, 0,rtreedepth, 0, 0);
} }
if( rc==SQLITE_OK ){ if( rc==SQLITE_OK ){
#ifdef SQLITE_RTREE_INT_ONLY
void *c = (void *)RTREE_COORD_INT32;
#else
void *c = (void *)RTREE_COORD_REAL32; void *c = (void *)RTREE_COORD_REAL32;
#endif
rc = sqlite3_create_module_v2(db, "rtree", &rtreeModule, c, 0); rc = sqlite3_create_module_v2(db, "rtree", &rtreeModule, c, 0);
} }
if( rc==SQLITE_OK ){ if( rc==SQLITE_OK ){
@ -3230,7 +3268,7 @@ static void geomCallback(sqlite3_context *ctx, int nArg, sqlite3_value **aArg){
RtreeMatchArg *pBlob; RtreeMatchArg *pBlob;
int nBlob; int nBlob;
nBlob = sizeof(RtreeMatchArg) + (nArg-1)*sizeof(double); nBlob = sizeof(RtreeMatchArg) + (nArg-1)*sizeof(RtreeDValue);
pBlob = (RtreeMatchArg *)sqlite3_malloc(nBlob); pBlob = (RtreeMatchArg *)sqlite3_malloc(nBlob);
if( !pBlob ){ if( !pBlob ){
sqlite3_result_error_nomem(ctx); sqlite3_result_error_nomem(ctx);
@ -3241,7 +3279,11 @@ static void geomCallback(sqlite3_context *ctx, int nArg, sqlite3_value **aArg){
pBlob->pContext = pGeomCtx->pContext; pBlob->pContext = pGeomCtx->pContext;
pBlob->nParam = nArg; pBlob->nParam = nArg;
for(i=0; i<nArg; i++){ for(i=0; i<nArg; i++){
#ifdef SQLITE_RTREE_INT_ONLY
pBlob->aParam[i] = sqlite3_value_int64(aArg[i]);
#else
pBlob->aParam[i] = sqlite3_value_double(aArg[i]); pBlob->aParam[i] = sqlite3_value_double(aArg[i]);
#endif
} }
sqlite3_result_blob(ctx, pBlob, nBlob, doSqlite3Free); sqlite3_result_blob(ctx, pBlob, nBlob, doSqlite3Free);
} }
@ -3253,7 +3295,7 @@ static void geomCallback(sqlite3_context *ctx, int nArg, sqlite3_value **aArg){
int sqlite3_rtree_geometry_callback( int sqlite3_rtree_geometry_callback(
sqlite3 *db, sqlite3 *db,
const char *zGeom, const char *zGeom,
int (*xGeom)(sqlite3_rtree_geometry *, int, double *, int *), int (*xGeom)(sqlite3_rtree_geometry *, int, RtreeDValue *, int *),
void *pContext void *pContext
){ ){
RtreeGeomCallback *pGeomCtx; /* Context object for new user-function */ RtreeGeomCallback *pGeomCtx; /* Context object for new user-function */

View File

@ -104,6 +104,18 @@ for {set nCol 1} {$nCol<[llength $cols]} {incr nCol} {
catchsql { DROP TABLE t1 } catchsql { DROP TABLE t1 }
} }
# Like execsql except display output as integer where that can be
# done without loss of information.
#
proc execsql_intout {sql} {
set out {}
foreach term [execsql $sql] {
regsub {\.0$} $term {} term
lappend out $term
}
return $out
}
# Test that it is possible to open an existing database that contains # Test that it is possible to open an existing database that contains
# r-tree tables. # r-tree tables.
# #
@ -117,8 +129,8 @@ do_test rtree-1.4.1 {
do_test rtree-1.4.2 { do_test rtree-1.4.2 {
db close db close
sqlite3 db test.db sqlite3 db test.db
execsql { SELECT * FROM t1 ORDER BY ii } execsql_intout { SELECT * FROM t1 ORDER BY ii }
} {1 5.0 10.0 2 15.0 20.0} } {1 5 10 2 15 20}
do_test rtree-1.4.3 { do_test rtree-1.4.3 {
execsql { DROP TABLE t1 } execsql { DROP TABLE t1 }
} {} } {}
@ -127,12 +139,12 @@ do_test rtree-1.4.3 {
# column names. # column names.
# #
do_test rtree-1.5.1 { do_test rtree-1.5.1 {
execsql { execsql_intout {
CREATE VIRTUAL TABLE t1 USING rtree("the key", "x dim.", "x2'dim"); CREATE VIRTUAL TABLE t1 USING rtree("the key", "x dim.", "x2'dim");
INSERT INTO t1 VALUES(1, 2, 3); INSERT INTO t1 VALUES(1, 2, 3);
SELECT "the key", "x dim.", "x2'dim" FROM t1; SELECT "the key", "x dim.", "x2'dim" FROM t1;
} }
} {1 2.0 3.0} } {1 2 3}
do_test rtree-1.5.1 { do_test rtree-1.5.1 {
execsql { DROP TABLE t1 } execsql { DROP TABLE t1 }
} {} } {}
@ -161,8 +173,8 @@ do_test rtree-2.1.1 {
do_test rtree-2.1.2 { do_test rtree-2.1.2 {
execsql { INSERT INTO t1 VALUES(NULL, 1, 3, 2, 4) } execsql { INSERT INTO t1 VALUES(NULL, 1, 3, 2, 4) }
execsql { SELECT * FROM t1 } execsql_intout { SELECT * FROM t1 }
} {1 1.0 3.0 2.0 4.0} } {1 1 3 2 4}
do_test rtree-2.1.3 { do_test rtree-2.1.3 {
execsql { INSERT INTO t1 VALUES(NULL, 1, 3, 2, 4) } execsql { INSERT INTO t1 VALUES(NULL, 1, 3, 2, 4) }
execsql { SELECT rowid FROM t1 ORDER BY rowid } execsql { SELECT rowid FROM t1 ORDER BY rowid }
@ -201,17 +213,17 @@ do_test rtree-3.1.1 {
} }
} {} } {}
do_test rtree-3.1.2 { do_test rtree-3.1.2 {
execsql { execsql_intout {
INSERT INTO t1 VALUES(5, 1, 3, 2, 4); INSERT INTO t1 VALUES(5, 1, 3, 2, 4);
SELECT * FROM t1; SELECT * FROM t1;
} }
} {5 1.0 3.0 2.0 4.0} } {5 1 3 2 4}
do_test rtree-3.1.3 { do_test rtree-3.1.3 {
execsql { execsql_intout {
INSERT INTO t1 VALUES(6, 2, 6, 4, 8); INSERT INTO t1 VALUES(6, 2, 6, 4, 8);
SELECT * FROM t1; SELECT * FROM t1;
} }
} {5 1.0 3.0 2.0 4.0 6 2.0 6.0 4.0 8.0} } {5 1 3 2 4 6 2 6 4 8}
# Test the constraint on the coordinates (c[i]<=c[i+1] where (i%2==0)): # Test the constraint on the coordinates (c[i]<=c[i+1] where (i%2==0)):
do_test rtree-3.2.1 { do_test rtree-3.2.1 {
@ -228,25 +240,25 @@ do_test rtree-5.1.1 {
execsql { CREATE VIRTUAL TABLE t2 USING rtree(ii, x1, x2) } execsql { CREATE VIRTUAL TABLE t2 USING rtree(ii, x1, x2) }
} {} } {}
do_test rtree-5.1.2 { do_test rtree-5.1.2 {
execsql { execsql_intout {
INSERT INTO t2 VALUES(1, 10, 20); INSERT INTO t2 VALUES(1, 10, 20);
INSERT INTO t2 VALUES(2, 30, 40); INSERT INTO t2 VALUES(2, 30, 40);
INSERT INTO t2 VALUES(3, 50, 60); INSERT INTO t2 VALUES(3, 50, 60);
SELECT * FROM t2 ORDER BY ii; SELECT * FROM t2 ORDER BY ii;
} }
} {1 10.0 20.0 2 30.0 40.0 3 50.0 60.0} } {1 10 20 2 30 40 3 50 60}
do_test rtree-5.1.3 { do_test rtree-5.1.3 {
execsql { execsql_intout {
DELETE FROM t2 WHERE ii=2; DELETE FROM t2 WHERE ii=2;
SELECT * FROM t2 ORDER BY ii; SELECT * FROM t2 ORDER BY ii;
} }
} {1 10.0 20.0 3 50.0 60.0} } {1 10 20 3 50 60}
do_test rtree-5.1.4 { do_test rtree-5.1.4 {
execsql { execsql_intout {
DELETE FROM t2 WHERE ii=1; DELETE FROM t2 WHERE ii=1;
SELECT * FROM t2 ORDER BY ii; SELECT * FROM t2 ORDER BY ii;
} }
} {3 50.0 60.0} } {3 50 60}
do_test rtree-5.1.5 { do_test rtree-5.1.5 {
execsql { execsql {
DELETE FROM t2 WHERE ii=3; DELETE FROM t2 WHERE ii=3;
@ -264,16 +276,16 @@ do_test rtree-6.1.1 {
execsql { CREATE VIRTUAL TABLE t3 USING rtree(ii, x1, x2, y1, y2) } execsql { CREATE VIRTUAL TABLE t3 USING rtree(ii, x1, x2, y1, y2) }
} {} } {}
do_test rtree-6.1.2 { do_test rtree-6.1.2 {
execsql { execsql_intout {
INSERT INTO t3 VALUES(1, 2, 3, 4, 5); INSERT INTO t3 VALUES(1, 2, 3, 4, 5);
UPDATE t3 SET x2=5; UPDATE t3 SET x2=5;
SELECT * FROM t3; SELECT * FROM t3;
} }
} {1 2.0 5.0 4.0 5.0} } {1 2 5 4 5}
do_test rtree-6.1.3 { do_test rtree-6.1.3 {
execsql { UPDATE t3 SET ii = 2 } execsql { UPDATE t3 SET ii = 2 }
execsql { SELECT * FROM t3 } execsql_intout { SELECT * FROM t3 }
} {2 2.0 5.0 4.0 5.0} } {2 2 5 4 5}
#---------------------------------------------------------------------------- #----------------------------------------------------------------------------
# Test cases rtree-7.* test rename operations. # Test cases rtree-7.* test rename operations.
@ -286,29 +298,29 @@ do_test rtree-7.1.1 {
} {} } {}
do_test rtree-7.1.2 { do_test rtree-7.1.2 {
execsql { ALTER TABLE t4 RENAME TO t5 } execsql { ALTER TABLE t4 RENAME TO t5 }
execsql { SELECT * FROM t5 } execsql_intout { SELECT * FROM t5 }
} {1 2.0 3.0 4.0 5.0 6.0 7.0} } {1 2 3 4 5 6 7}
do_test rtree-7.1.3 { do_test rtree-7.1.3 {
db close db close
sqlite3 db test.db sqlite3 db test.db
execsql { SELECT * FROM t5 } execsql_intout { SELECT * FROM t5 }
} {1 2.0 3.0 4.0 5.0 6.0 7.0} } {1 2 3 4 5 6 7}
do_test rtree-7.1.4 { do_test rtree-7.1.4 {
execsql { ALTER TABLE t5 RENAME TO 'raisara "one"'''} execsql { ALTER TABLE t5 RENAME TO 'raisara "one"'''}
execsql { SELECT * FROM "raisara ""one""'" } execsql_intout { SELECT * FROM "raisara ""one""'" }
} {1 2.0 3.0 4.0 5.0 6.0 7.0} } {1 2 3 4 5 6 7}
do_test rtree-7.1.5 { do_test rtree-7.1.5 {
execsql { SELECT * FROM 'raisara "one"''' } execsql_intout { SELECT * FROM 'raisara "one"''' }
} {1 2.0 3.0 4.0 5.0 6.0 7.0} } {1 2 3 4 5 6 7}
do_test rtree-7.1.6 { do_test rtree-7.1.6 {
execsql { ALTER TABLE "raisara ""one""'" RENAME TO "abc 123" } execsql { ALTER TABLE "raisara ""one""'" RENAME TO "abc 123" }
execsql { SELECT * FROM "abc 123" } execsql_intout { SELECT * FROM "abc 123" }
} {1 2.0 3.0 4.0 5.0 6.0 7.0} } {1 2 3 4 5 6 7}
do_test rtree-7.1.7 { do_test rtree-7.1.7 {
db close db close
sqlite3 db test.db sqlite3 db test.db
execsql { SELECT * FROM "abc 123" } execsql_intout { SELECT * FROM "abc 123" }
} {1 2.0 3.0 4.0 5.0 6.0 7.0} } {1 2 3 4 5 6 7}
# An error midway through a rename operation. # An error midway through a rename operation.
do_test rtree-7.2.1 { do_test rtree-7.2.1 {
@ -318,8 +330,8 @@ do_test rtree-7.2.1 {
catchsql { ALTER TABLE "abc 123" RENAME TO t4 } catchsql { ALTER TABLE "abc 123" RENAME TO t4 }
} {1 {SQL logic error or missing database}} } {1 {SQL logic error or missing database}}
do_test rtree-7.2.2 { do_test rtree-7.2.2 {
execsql { SELECT * FROM "abc 123" } execsql_intout { SELECT * FROM "abc 123" }
} {1 2.0 3.0 4.0 5.0 6.0 7.0} } {1 2 3 4 5 6 7}
do_test rtree-7.2.3 { do_test rtree-7.2.3 {
execsql { execsql {
DROP TABLE t4_node; DROP TABLE t4_node;
@ -330,13 +342,13 @@ do_test rtree-7.2.3 {
do_test rtree-7.2.4 { do_test rtree-7.2.4 {
db close db close
sqlite3 db test.db sqlite3 db test.db
execsql { SELECT * FROM "abc 123" } execsql_intout { SELECT * FROM "abc 123" }
} {1 2.0 3.0 4.0 5.0 6.0 7.0} } {1 2 3 4 5 6 7}
do_test rtree-7.2.5 { do_test rtree-7.2.5 {
execsql { DROP TABLE t4_rowid } execsql { DROP TABLE t4_rowid }
execsql { ALTER TABLE "abc 123" RENAME TO t4 } execsql { ALTER TABLE "abc 123" RENAME TO t4 }
execsql { SELECT * FROM t4 } execsql_intout { SELECT * FROM t4 }
} {1 2.0 3.0 4.0 5.0 6.0 7.0} } {1 2 3 4 5 6 7}
#---------------------------------------------------------------------------- #----------------------------------------------------------------------------

View File

@ -27,21 +27,38 @@ if {[info exists G(isquick)] && $G(isquick)} {
set ::NROW 250 set ::NROW 250
} }
# Return a floating point number between -X and X. ifcapable !rtree_int_only {
# # Return a floating point number between -X and X.
proc rand {X} { #
return [expr {int((rand()-0.5)*1024.0*$X)/512.0}] proc rand {X} {
} return [expr {int((rand()-0.5)*1024.0*$X)/512.0}]
}
# Return a positive floating point number less than or equal to X
# # Return a positive floating point number less than or equal to X
proc randincr {X} { #
while 1 { proc randincr {X} {
set r [expr {int(rand()*$X*32.0)/32.0}] while 1 {
if {$r>0.0} {return $r} set r [expr {int(rand()*$X*32.0)/32.0}]
if {$r>0.0} {return $r}
}
}
} else {
# For rtree_int_only, return an number between -X and X.
#
proc rand {X} {
return [expr {int((rand()-0.5)*2*$X)}]
}
# Return a positive integer less than or equal to X
#
proc randincr {X} {
while 1 {
set r [expr {int(rand()*$X)+1}]
if {$r>0} {return $r}
}
} }
} }
# Scramble the $inlist into a random order. # Scramble the $inlist into a random order.
# #
proc scramble {inlist} { proc scramble {inlist} {

View File

@ -49,9 +49,11 @@ do_test rtree5-1.6 {
do_test rtree5-1.7 { do_test rtree5-1.7 {
execsql { SELECT count(*) FROM t1 WHERE x1==5 } execsql { SELECT count(*) FROM t1 WHERE x1==5 }
} {1} } {1}
do_test rtree5-1.8 { ifcapable !rtree_int_only {
execsql { SELECT count(*) FROM t1 WHERE x1==5.2 } do_test rtree5-1.8 {
} {0} execsql { SELECT count(*) FROM t1 WHERE x1==5.2 }
} {0}
}
do_test rtree5-1.9 { do_test rtree5-1.9 {
execsql { SELECT count(*) FROM t1 WHERE x1==5.0 } execsql { SELECT count(*) FROM t1 WHERE x1==5.0 }
} {1} } {1}

View File

@ -16,7 +16,7 @@ if {![info exists testdir]} {
} }
source $testdir/tester.tcl source $testdir/tester.tcl
ifcapable !rtree { ifcapable {!rtree || rtree_int_only} {
finish_test finish_test
return return
} }

View File

@ -24,6 +24,18 @@ ifcapable !rtree||!vacuum {
return return
} }
# Like execsql except display output as integer where that can be
# done without loss of information.
#
proc execsql_intout {sql} {
set out {}
foreach term [execsql $sql] {
regsub {\.0$} $term {} term
lappend out $term
}
return $out
}
do_test rtree7-1.1 { do_test rtree7-1.1 {
execsql { execsql {
PRAGMA page_size = 1024; PRAGMA page_size = 1024;
@ -32,27 +44,27 @@ do_test rtree7-1.1 {
} }
} {} } {}
do_test rtree7-1.2 { do_test rtree7-1.2 {
execsql { SELECT * FROM rt } execsql_intout { SELECT * FROM rt }
} {1 1.0 2.0 3.0 4.0} } {1 1 2 3 4}
do_test rtree7-1.3 { do_test rtree7-1.3 {
execsql { execsql_intout {
PRAGMA page_size = 2048; PRAGMA page_size = 2048;
VACUUM; VACUUM;
SELECT * FROM rt; SELECT * FROM rt;
} }
} {1 1.0 2.0 3.0 4.0} } {1 1 2 3 4}
do_test rtree7-1.4 { do_test rtree7-1.4 {
for {set i 2} {$i <= 51} {incr i} { for {set i 2} {$i <= 51} {incr i} {
execsql { INSERT INTO rt VALUES($i, 1, 2, 3, 4) } execsql { INSERT INTO rt VALUES($i, 1, 2, 3, 4) }
} }
execsql { SELECT sum(x1), sum(x2), sum(y1), sum(y2) FROM rt } execsql_intout { SELECT sum(x1), sum(x2), sum(y1), sum(y2) FROM rt }
} {51.0 102.0 153.0 204.0} } {51 102 153 204}
do_test rtree7-1.5 { do_test rtree7-1.5 {
execsql { execsql_intout {
PRAGMA page_size = 512; PRAGMA page_size = 512;
VACUUM; VACUUM;
SELECT sum(x1), sum(x2), sum(y1), sum(y2) FROM rt SELECT sum(x1), sum(x2), sum(y1), sum(y2) FROM rt
} }
} {51.0 102.0 153.0 204.0} } {51 102 153 204}
finish_test finish_test

View File

@ -17,6 +17,7 @@ if {![info exists testdir]} {
} }
source $testdir/tester.tcl source $testdir/tester.tcl
ifcapable !rtree { finish_test ; return } ifcapable !rtree { finish_test ; return }
ifcapable rtree_int_only { finish_test; return }
register_cube_geom db register_cube_geom db

View File

@ -18,17 +18,30 @@ if {![info exists testdir]} {
source $testdir/tester.tcl source $testdir/tester.tcl
ifcapable !rtree { finish_test ; return } ifcapable !rtree { finish_test ; return }
do_test rtreeB-1.1 { ifcapable rtree_int_only {
db eval { do_test rtreeB-1.1-intonly {
CREATE VIRTUAL TABLE t1 USING rtree(ii, x0, y0, x1, y1); db eval {
INSERT INTO t1 VALUES(1073741824, 0.0, 0.0, 100.0, 100.0); CREATE VIRTUAL TABLE t1 USING rtree(ii, x0, y0, x1, y1);
INSERT INTO t1 VALUES(2147483646, 0.0, 0.0, 200.0, 200.0); INSERT INTO t1 VALUES(1073741824, 0.0, 0.0, 100.0, 100.0);
INSERT INTO t1 VALUES(4294967296, 0.0, 0.0, 300.0, 300.0); INSERT INTO t1 VALUES(2147483646, 0.0, 0.0, 200.0, 200.0);
INSERT INTO t1 VALUES(8589934592, 20.0, 20.0, 150.0, 150.0); INSERT INTO t1 VALUES(4294967296, 0.0, 0.0, 300.0, 300.0);
INSERT INTO t1 VALUES(9223372036854775807, 150, 150, 400, 400); INSERT INTO t1 VALUES(8589934592, 20.0, 20.0, 150.0, 150.0);
SELECT rtreenode(2, data) FROM t1_node; INSERT INTO t1 VALUES(9223372036854775807, 150, 150, 400, 400);
} SELECT rtreenode(2, data) FROM t1_node;
} {{{1073741824 0.000000 0.000000 100.000000 100.000000} {2147483646 0.000000 0.000000 200.000000 200.000000} {4294967296 0.000000 0.000000 300.000000 300.000000} {8589934592 20.000000 20.000000 150.000000 150.000000} {9223372036854775807 150.000000 150.000000 400.000000 400.000000}}} }
} {{{1073741824 0 0 100 100} {2147483646 0 0 200 200} {4294967296 0 0 300 300} {8589934592 20 20 150 150} {9223372036854775807 150 150 400 400}}}
} else {
do_test rtreeB-1.1 {
db eval {
CREATE VIRTUAL TABLE t1 USING rtree(ii, x0, y0, x1, y1);
INSERT INTO t1 VALUES(1073741824, 0.0, 0.0, 100.0, 100.0);
INSERT INTO t1 VALUES(2147483646, 0.0, 0.0, 200.0, 200.0);
INSERT INTO t1 VALUES(4294967296, 0.0, 0.0, 300.0, 300.0);
INSERT INTO t1 VALUES(8589934592, 20.0, 20.0, 150.0, 150.0);
INSERT INTO t1 VALUES(9223372036854775807, 150, 150, 400, 400);
SELECT rtreenode(2, data) FROM t1_node;
}
} {{{1073741824 0.000000 0.000000 100.000000 100.000000} {2147483646 0.000000 0.000000 200.000000 200.000000} {4294967296 0.000000 0.000000 300.000000 300.000000} {8589934592 20.000000 20.000000 150.000000 150.000000} {9223372036854775807 150.000000 150.000000 400.000000 400.000000}}}
}
finish_test finish_test

View File

@ -31,7 +31,11 @@ typedef struct sqlite3_rtree_geometry sqlite3_rtree_geometry;
int sqlite3_rtree_geometry_callback( int sqlite3_rtree_geometry_callback(
sqlite3 *db, sqlite3 *db,
const char *zGeom, const char *zGeom,
int (*xGeom)(sqlite3_rtree_geometry *, int nCoord, double *aCoord, int *pRes), #ifdef SQLITE_RTREE_INT_ONLY
int (*xGeom)(sqlite3_rtree_geometry*, int n, sqlite3_int64 *a, int *pRes),
#else
int (*xGeom)(sqlite3_rtree_geometry*, int n, double *a, int *pRes),
#endif
void *pContext void *pContext
); );

View File

@ -601,6 +601,7 @@ clean:
rm -f mkkeywordhash mkkeywordhash.exe keywordhash.h rm -f mkkeywordhash mkkeywordhash.exe keywordhash.h
rm -f $(PUBLISH) rm -f $(PUBLISH)
rm -f *.da *.bb *.bbg gmon.out rm -f *.da *.bb *.bbg gmon.out
rm -rf quota2a quota2b quota2c
rm -rf tsrc target_source rm -rf tsrc target_source
rm -f testloadext.dll libtestloadext.so rm -f testloadext.dll libtestloadext.so
rm -f amalgamation-testfixture amalgamation-testfixture.exe rm -f amalgamation-testfixture amalgamation-testfixture.exe

412
manifest
View File

@ -1,37 +1,29 @@
C Version\s3.7.10 C Version\s3.7.12
D 2012-01-16T13:28:40.069 D 2012-05-14T01:41:23.304
F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
F Makefile.in 3f79a373e57c3b92dabf76f40b065e719d31ac34 F Makefile.in 2f37e468503dbe79d35c9f6dffcf3fae1ae9ec20
F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
F Makefile.msc dcad80fa69f17d46fe6778ba873fc108ca16298d F Makefile.msc 7849a871b6cdb20fd51baee6bbe5965a03326be4
F Makefile.vxworks 1deb39c8bb047296c30161ffa10c1b5423e632f9 F Makefile.vxworks 3b7fe7a0571fdadc61363ebc1b23732d2d6363ca
F README cd04a36fbc7ea56932a4052d7d0b7f09f27c33d6 F README cd04a36fbc7ea56932a4052d7d0b7f09f27c33d6
F VERSION af03cd6400f9d71d38bdb7a9d66a1aefdc2f3e0d F VERSION f9313d88cb77df8617059a88eb382291321ef6bc
F aclocal.m4 a5c22d164aff7ed549d53a90fa56d56955281f50 F aclocal.m4 a5c22d164aff7ed549d53a90fa56d56955281f50
F addopcodes.awk 17dc593f791f874d2c23a0f9360850ded0286531 F addopcodes.awk 17dc593f791f874d2c23a0f9360850ded0286531
F art/2005osaward.gif 0d1851b2a7c1c9d0ccce545f3e14bca42d7fd248
F art/SQLite.eps 9b43cc99cfd2be687d386faea6862ea68d6a72b2
F art/SQLite.gif 1bbb94484963f1382e27e1c5e86dd0c1061eba2b
F art/SQLiteLogo3.tiff b9e6bf022ae939bc986cddb8ab99583ca1b02cb3
F art/SQLite_big.gif 2b8e4603b91ba2a2c7062a82ff570d945034bb30
F art/nocopy.gif 716aa07d4bb7250d4e75756073bf8ef9f56bec8f
F art/powered_by_sqlite.gif 7fbcd7d3675391fd3d21672c14c05f5999eb60d1
F art/sqlite370.eps aa97a671332b432a54e1d74ff5e8775be34200c2 F art/sqlite370.eps aa97a671332b432a54e1d74ff5e8775be34200c2
F art/sqlite370.ico af56c1d00fee7cd4753e8631ed60703ed0fc6e90 F art/sqlite370.ico af56c1d00fee7cd4753e8631ed60703ed0fc6e90
F art/sqlite370.jpg d512473dae7e378a67e28ff96a34da7cb331def2 F art/sqlite370.jpg d512473dae7e378a67e28ff96a34da7cb331def2
F art/src_logo.gif 9341ef09f0e53cd44c0c9b6fc3c16f7f3d6c2ad9
F config.guess 226d9a188c6196f3033ffc651cbc9dcee1a42977 F config.guess 226d9a188c6196f3033ffc651cbc9dcee1a42977
F config.h.in 31cc8c4943f56e60c4aa4fba929c9d4c70e418b4 F config.h.in 0921066a13130082764ab4ab6456f7b5bebe56de
F config.sub 9ebe4c3b3dab6431ece34f16828b594fb420da55 F config.sub 9ebe4c3b3dab6431ece34f16828b594fb420da55
F configure 5c1cf496e63a233d2abcccf7a3570ba84d5dce01 x F configure eb9e5e7f4c1601b5acf674a724e1a778481d2835 x
F configure.ac 75323bdac56fb0e69f6a3fc5b23f24359550b9d9 F configure.ac 9ee886c21c095b3272137b1553ae416c8b8c8557
F contrib/sqlitecon.tcl 210a913ad63f9f991070821e599d600bd913e0ad F contrib/sqlitecon.tcl 210a913ad63f9f991070821e599d600bd913e0ad
F doc/lemon.html 3091574143dd3415669b6745843ff8d011d33549 F doc/lemon.html 3091574143dd3415669b6745843ff8d011d33549
F doc/pager-invariants.txt 870107036470d7c419e93768676fae2f8749cf9e F doc/pager-invariants.txt 870107036470d7c419e93768676fae2f8749cf9e
F doc/vfs-shm.txt e101f27ea02a8387ce46a05be2b1a902a021d37a F doc/vfs-shm.txt e101f27ea02a8387ce46a05be2b1a902a021d37a
F ext/README.txt 913a7bd3f4837ab14d7e063304181787658b14e1 F ext/README.txt 913a7bd3f4837ab14d7e063304181787658b14e1
F ext/async/README.txt 0c541f418b14b415212264cbaaf51c924ec62e5b F ext/async/README.txt 0c541f418b14b415212264cbaaf51c924ec62e5b
F ext/async/sqlite3async.c a7c6078c82c0bac3b7bea95bc52d5ce7ed58083a F ext/async/sqlite3async.c 733a9f21b1066f44ff07b9c0da973b1e483d1e0c
F ext/async/sqlite3async.h a21e1252deb14a2c211f0e165c4b9122a8f1f344 F ext/async/sqlite3async.h a21e1252deb14a2c211f0e165c4b9122a8f1f344
F ext/fts1/README.txt 20ac73b006a70bcfd80069bdaf59214b6cf1db5e F ext/fts1/README.txt 20ac73b006a70bcfd80069bdaf59214b6cf1db5e
F ext/fts1/ft_hash.c 3927bd880e65329bdc6f506555b228b28924921b F ext/fts1/ft_hash.c 3927bd880e65329bdc6f506555b228b28924921b
@ -63,49 +55,50 @@ F ext/fts3/README.content fdc666a70d5257a64fee209f97cf89e0e6e32b51
F ext/fts3/README.syntax a19711dc5458c20734b8e485e75fb1981ec2427a F ext/fts3/README.syntax a19711dc5458c20734b8e485e75fb1981ec2427a
F ext/fts3/README.tokenizers 998756696647400de63d5ba60e9655036cb966e9 F ext/fts3/README.tokenizers 998756696647400de63d5ba60e9655036cb966e9
F ext/fts3/README.txt 8c18f41574404623b76917b9da66fcb0ab38328d F ext/fts3/README.txt 8c18f41574404623b76917b9da66fcb0ab38328d
F ext/fts3/fts3.c bd570b99f1f65b17d587361a421d7f2f28082aa0 F ext/fts3/fts3.c a7adf6747d1fdd627ecd421c1709996741ca6693
F ext/fts3/fts3.h 3a10a0af180d502cecc50df77b1b22df142817fe F ext/fts3/fts3.h 3a10a0af180d502cecc50df77b1b22df142817fe
F ext/fts3/fts3Int.h def7a900f98c5ab5fa4772e922bfa219d5097f05 F ext/fts3/fts3Int.h aca752b99c15ee738f5bcf0910eafb9e4aeb1b97
F ext/fts3/fts3_aux.c 0ebfa7b86cf8ff6a0861605fcc63b83ec1b70691 F ext/fts3/fts3_aux.c 5205182bd8f372782597888156404766edf5781e
F ext/fts3/fts3_expr.c f5df26bddf46a5916b2a5f80c4027996e92b7b15 F ext/fts3/fts3_expr.c dbc7ba4c3a6061adde0f38ed8e9b349568299551
F ext/fts3/fts3_hash.c 8dd2d06b66c72c628c2732555a32bc0943114914 F ext/fts3/fts3_hash.c 8dd2d06b66c72c628c2732555a32bc0943114914
F ext/fts3/fts3_hash.h 8331fb2206c609f9fc4c4735b9ab5ad6137c88ec F ext/fts3/fts3_hash.h 8331fb2206c609f9fc4c4735b9ab5ad6137c88ec
F ext/fts3/fts3_icu.c 6c8f395cdf9e1e3afa7fadb7e523dbbf381c6dfa F ext/fts3/fts3_icu.c 62ec177c55f6a5c6e994dd3e5fd3194b4045c347
F ext/fts3/fts3_porter.c b7e5276f9f0a5fc7018b6fa55ce0f31f269ef881 F ext/fts3/fts3_porter.c a465b49fcb8249a755792f87516eff182efa42b3
F ext/fts3/fts3_snippet.c 1f9ee6a8e0e242649645968dcec4deb253d86c2a F ext/fts3/fts3_snippet.c bf67520ae9d2352a65368ed101729ff701c08808
F ext/fts3/fts3_term.c a5457992723455a58804cb75c8cbd8978db5c2ef F ext/fts3/fts3_term.c a521f75132f9a495bdca1bdd45949b3191c52763
F ext/fts3/fts3_test.c 24fa13f330db011500acb95590da9eee24951894 F ext/fts3/fts3_test.c 348f7d08cae05285794e23dc4fe8b8fdf66e264a
F ext/fts3/fts3_tokenizer.c 9ff7ec66ae3c5c0340fa081958e64f395c71a106 F ext/fts3/fts3_tokenizer.c 3da7254a9881f7e270ab28e2004e0d22b3212bce
F ext/fts3/fts3_tokenizer.h 13ffd9fcb397fec32a05ef5cd9e0fa659bf3dbd3 F ext/fts3/fts3_tokenizer.h 66dec98e365854b6cd2d54f1a96bb6d428fc5a68
F ext/fts3/fts3_tokenizer1.c 0dde8f307b8045565cf63797ba9acfaff1c50c68 F ext/fts3/fts3_tokenizer1.c 5c98225a53705e5ee34824087478cf477bdb7004
F ext/fts3/fts3_write.c fdf0c99830360146ec7128150271c8c014a8fef7 F ext/fts3/fts3_write.c cd4af00b3b0512b4d76177a267fcaafab44cbce4
F ext/fts3/fts3speed.tcl b54caf6a18d38174f1a6e84219950d85e98bb1e9 F ext/fts3/fts3speed.tcl b54caf6a18d38174f1a6e84219950d85e98bb1e9
F ext/fts3/mkfts3amal.tcl 252ecb7fe6467854f2aa237bf2c390b74e71f100 F ext/fts3/mkfts3amal.tcl 252ecb7fe6467854f2aa237bf2c390b74e71f100
F ext/fts3/tool/fts3view.c 6cfc5b67a5f0e09c0d698f9fd012c784bfaa9197
F ext/icu/README.txt bf8461d8cdc6b8f514c080e4e10dc3b2bbdfefa9 F ext/icu/README.txt bf8461d8cdc6b8f514c080e4e10dc3b2bbdfefa9
F ext/icu/icu.c eb9ae1d79046bd7871aa97ee6da51eb770134b5a F ext/icu/icu.c eb9ae1d79046bd7871aa97ee6da51eb770134b5a
F ext/icu/sqliteicu.h 728867a802baa5a96de7495e9689a8e01715ef37 F ext/icu/sqliteicu.h 728867a802baa5a96de7495e9689a8e01715ef37
F ext/rtree/README 6315c0d73ebf0ec40dedb5aa0e942bc8b54e3761 F ext/rtree/README 6315c0d73ebf0ec40dedb5aa0e942bc8b54e3761
F ext/rtree/rtree.c b92ab2e91e35c4964644647322813419c65fe1ce F ext/rtree/rtree.c 73502e5336162fdc8f5d1c4bdd4ec6b1299c2f2a
F ext/rtree/rtree.h 834dbcb82dc85b2481cde6a07cdadfddc99e9b9e F ext/rtree/rtree.h 834dbcb82dc85b2481cde6a07cdadfddc99e9b9e
F ext/rtree/rtree1.test 28e1b8da4da98093ce3210187434dd760a8d89d8 F ext/rtree/rtree1.test e474a2b5eff231496dbd073fe67e5fbaf7f444c9
F ext/rtree/rtree2.test acbb3a4ce0f4fbc2c304d2b4b784cfa161856bba F ext/rtree/rtree2.test acbb3a4ce0f4fbc2c304d2b4b784cfa161856bba
F ext/rtree/rtree3.test a494da55c30ee0bc9b01a91c80c81b387b22d2dc F ext/rtree/rtree3.test a494da55c30ee0bc9b01a91c80c81b387b22d2dc
F ext/rtree/rtree4.test 0061e6f464fd3dc6a79f82454c5a1c3dadbe42af F ext/rtree/rtree4.test c8fe384f60ebd49540a5fecc990041bf452eb6e0
F ext/rtree/rtree5.test ce3d7ccae2cfd9d2e1052b462424964c9bdcda12 F ext/rtree/rtree5.test 9a229678a00f40e6aedb40cb3a07ec5444af892c
F ext/rtree/rtree6.test 0b380bd9af93f3bc496eef42502a336f58949c1b F ext/rtree/rtree6.test 3ff9113b4a872fa935309e3511cd9b7cdb4d2472
F ext/rtree/rtree7.test bcb647b42920b3b5d025846689147778485cc318 F ext/rtree/rtree7.test 1fa710b9e6bf997a0c1a537b81be7bb6fded1971
F ext/rtree/rtree8.test 9772e16da71e17e02bdebf0a5188590f289ab37d F ext/rtree/rtree8.test 9772e16da71e17e02bdebf0a5188590f289ab37d
F ext/rtree/rtree9.test df9843d1a9195249c8d3b4ea6aedda2d5c73e9c2 F ext/rtree/rtree9.test d86ebf08ff6328895613ed577dd8a2a37c472c34
F ext/rtree/rtreeA.test ace05e729a36e342d40cf94e9efc7b4723d9dcdf F ext/rtree/rtreeA.test ace05e729a36e342d40cf94e9efc7b4723d9dcdf
F ext/rtree/rtreeB.test b1916a9cecb86b02529c4cc5a546e8d6e7ff10da F ext/rtree/rtreeB.test 983e567b49b5dca165940f66b87e161aa30e82b2
F ext/rtree/rtree_perf.tcl 6c18c1f23cd48e0f948930c98dfdd37dfccb5195 F ext/rtree/rtree_perf.tcl 6c18c1f23cd48e0f948930c98dfdd37dfccb5195
F ext/rtree/rtree_util.tcl 06aab2ed5b826545bf215fff90ecb9255a8647ea F ext/rtree/rtree_util.tcl 06aab2ed5b826545bf215fff90ecb9255a8647ea
F ext/rtree/sqlite3rtree.h 1af0899c63a688e272d69d8e746f24e76f10a3f0 F ext/rtree/sqlite3rtree.h c34c1e41d1ab80bb8ad09aae402c9c956871a765
F ext/rtree/tkt3363.test 142ab96eded44a3615ec79fba98c7bde7d0f96de F ext/rtree/tkt3363.test 142ab96eded44a3615ec79fba98c7bde7d0f96de
F ext/rtree/viewrtree.tcl eea6224b3553599ae665b239bd827e182b466024 F ext/rtree/viewrtree.tcl eea6224b3553599ae665b239bd827e182b466024
F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x
F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8 F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8
F main.mk ac48970ca7506c9034f5c7b2212111fbeb0a1aaa F main.mk a80771d44176a0c744d9d4e048497e7ed0b4040d
F mkdll.sh 7d09b23c05d56532e9d44a50868eb4b12ff4f74a F mkdll.sh 7d09b23c05d56532e9d44a50868eb4b12ff4f74a
F mkextu.sh 416f9b7089d80e5590a29692c9d9280a10dbad9f F mkextu.sh 416f9b7089d80e5590a29692c9d9280a10dbad9f
F mkextw.sh 4123480947681d9b434a5e7b1ee08135abe409ac F mkextw.sh 4123480947681d9b434a5e7b1ee08135abe409ac
@ -118,39 +111,39 @@ F spec.template 86a4a43b99ebb3e75e6b9a735d5fd293a24e90ca
F sqlite.pc.in 42b7bf0d02e08b9e77734a47798d1a55a9e0716b F sqlite.pc.in 42b7bf0d02e08b9e77734a47798d1a55a9e0716b
F sqlite3.1 6be1ad09113570e1fc8dcaff84c9b0b337db5ffc F sqlite3.1 6be1ad09113570e1fc8dcaff84c9b0b337db5ffc
F sqlite3.pc.in ae6f59a76e862f5c561eb32a380228a02afc3cad F sqlite3.pc.in ae6f59a76e862f5c561eb32a380228a02afc3cad
F src/alter.c ac80a0f31189f8b4a524ebf661e47e84536ee7f5 F src/alter.c 149cc80d9257971b0bff34e58fb2263e01998289
F src/analyze.c f32ff304da413851eefa562b04e61ff6cb88248b F src/analyze.c 70c46504c0d2543ea5cdca01140b2cd3e1d886e7
F src/attach.c 12c6957996908edc31c96d7c68d4942c2474405f F src/attach.c 12c6957996908edc31c96d7c68d4942c2474405f
F src/auth.c 523da7fb4979469955d822ff9298352d6b31de34 F src/auth.c 523da7fb4979469955d822ff9298352d6b31de34
F src/backup.c e9538bad2d4a4fcd4308f1aed7cb18a0fbc968f9 F src/backup.c 6be23a344d3301ae38e92fddb3a33b91c309fce4
F src/bitvec.c af50f1c8c0ff54d6bdb7a80e2fceca5a93670bef F src/bitvec.c af50f1c8c0ff54d6bdb7a80e2fceca5a93670bef
F src/btmutex.c 976f45a12e37293e32cae0281b15a21d48a8aaa7 F src/btmutex.c 976f45a12e37293e32cae0281b15a21d48a8aaa7
F src/btree.c a65816cc000bdd421772986e64c88c9035331332 F src/btree.c df800f10896bc2ddaa1125c532d6e7a7b9efc532
F src/btree.h f5d775cd6cfc7ac32a2535b70e8d2af48ef5f2ce F src/btree.h 48a013f8964f12d944d90e4700df47b72dd6d923
F src/btreeInt.h 6c9960645c431c9456ca56498f43a2b3bf1fa8c2 F src/btreeInt.h 38a639c0542c29fe8331a221c4aed0cb8686249e
F src/build.c 8e2a4dedad860fed982270ef43968505f35ec57f F src/build.c 987c6933ea170e443dc6a79d52f8d2506206b12b
F src/callback.c 0425c6320730e6d3981acfb9202c1bed9016ad1a F src/callback.c 0cb4228cdcd827dcc5def98fb099edcc9142dbcd
F src/complete.c dc1d136c0feee03c2f7550bafc0d29075e36deac F src/complete.c dc1d136c0feee03c2f7550bafc0d29075e36deac
F src/ctime.c a9c26822515f81ec21588cbb482ca6724be02e33 F src/ctime.c a9c26822515f81ec21588cbb482ca6724be02e33
F src/date.c 067a81c9942c497aafd2c260e13add8a7d0c7dd4 F src/date.c 067a81c9942c497aafd2c260e13add8a7d0c7dd4
F src/delete.c 51d32f0a9c880663e54ce309f52e40c325d5e112 F src/delete.c 4c20ea4f6213b3bc1c6a510586864b679946e05e
F src/expr.c 537591e95eac74af783e4eb033954fb218cf398e F src/expr.c eefabaa4a3dc67309a754eb0eab1a163ff4c2bf3
F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb
F src/fkey.c 657212460bf5cfd3ae607d12ea62092844c227b5 F src/fkey.c 657212460bf5cfd3ae607d12ea62092844c227b5
F src/func.c 6261ce00aad9c63cd5b4219249b05683979060e9 F src/func.c c6b3c94320253a35bda43fb69cc292618e3285d6
F src/global.c 4cfdca5cb0edd33c4d021baec4ede958cb2c793b F src/global.c 4cfdca5cb0edd33c4d021baec4ede958cb2c793b
F src/hash.c 458488dcc159c301b8e7686280ab209f1fb915af F src/hash.c 458488dcc159c301b8e7686280ab209f1fb915af
F src/hash.h 2894c932d84d9f892d4b4023a75e501f83050970 F src/hash.h 2894c932d84d9f892d4b4023a75e501f83050970
F src/hwtime.h d32741c8f4df852c7d959236615444e2b1063b08 F src/hwtime.h d32741c8f4df852c7d959236615444e2b1063b08
F src/insert.c d7c69718acbb92e10e4b121da7bed13903342962 F src/insert.c 0bbffe75c254c62a5686ab5e7f88e29235e16174
F src/journal.c 552839e54d1bf76fb8f7abe51868b66acacf6a0e F src/journal.c 552839e54d1bf76fb8f7abe51868b66acacf6a0e
F src/legacy.c a199d7683d60cef73089e892409113e69c23a99f F src/legacy.c a199d7683d60cef73089e892409113e69c23a99f
F src/lempar.c 0ee69fca0be54cd93939df98d2aca4ca46f44416 F src/lempar.c 0ee69fca0be54cd93939df98d2aca4ca46f44416
F src/loadext.c f20382fbaeec832438a1ba7797bee3d3c8a6d51d F src/loadext.c f20382fbaeec832438a1ba7797bee3d3c8a6d51d
F src/main.c e60abee4a7ca3da31b67745ccf02b8d29f138614 F src/main.c 91458c713e9b7f8dbc98d79e78f1150f0ca9c2a1
F src/malloc.c 15afac5e59b6584efe072e9933aefb4230e74f97 F src/malloc.c 15afac5e59b6584efe072e9933aefb4230e74f97
F src/mem0.c 6a55ebe57c46ca1a7d98da93aaa07f99f1059645 F src/mem0.c 6a55ebe57c46ca1a7d98da93aaa07f99f1059645
F src/mem1.c 7998e7003a3047e323c849a26dda004debc04d03 F src/mem1.c b3677415e69603d6a0e7c5410a1b3731d55beda1
F src/mem2.c e307323e86b5da1853d7111b68fd6b84ad6f09cf F src/mem2.c e307323e86b5da1853d7111b68fd6b84ad6f09cf
F src/mem3.c 61c9d47b792908c532ca3a62b999cf21795c6534 F src/mem3.c 61c9d47b792908c532ca3a62b999cf21795c6534
F src/mem5.c c2c63b7067570b00bf33d751c39af24182316f7f F src/mem5.c c2c63b7067570b00bf33d751c39af24182316f7f
@ -163,102 +156,103 @@ F src/mutex_unix.c c3a4e00f96ba068a8dbef34084465979aaf369cc
F src/mutex_w32.c 5e54f3ba275bcb5d00248b8c23107df2e2f73e33 F src/mutex_w32.c 5e54f3ba275bcb5d00248b8c23107df2e2f73e33
F src/notify.c 976dd0f6171d4588e89e874fcc765e92914b6d30 F src/notify.c 976dd0f6171d4588e89e874fcc765e92914b6d30
F src/os.c e1acdc09ff3ac2412945cca9766e2dcf4675f31c F src/os.c e1acdc09ff3ac2412945cca9766e2dcf4675f31c
F src/os.h a2219c3b05ce31230bb000fdc4f1a542b33ee649 F src/os.h 59beba555b65a450bd1d804220532971d4299f60
F src/os_common.h 92815ed65f805560b66166e3583470ff94478f04 F src/os_common.h 92815ed65f805560b66166e3583470ff94478f04
F src/os_os2.c 4a75888ba3dfc820ad5e8177025972d74d7f2440 F src/os_os2.c 4a75888ba3dfc820ad5e8177025972d74d7f2440
F src/os_unix.c 657672fab2580a84116c140b36ee3d6b6fc75b4e F src/os_unix.c 424d46e0edab969293c2223f09923b2178171f47
F src/os_win.c 5ac061ae1326a71500cee578ed0fd9113b4f6a37 F src/os_win.c 412d6434133c7c81dc48b7702f3ea5e61c309e5c
F src/pager.c 4d58c983d9f4d34bc2d48e4280361ccaeecd03c5 F src/pager.c bb5635dde0b152797836d1c72275284724bb563c
F src/pager.h 5cd760857707529b403837d813d86b68938d6183 F src/pager.h ef1eaf8593e78f73885c1dfac27ad83bee23bdc5
F src/parse.y fabb2e7047417d840e6fdb3ef0988a86849a08ba F src/parse.y f29df90bd3adc64b33114ab1de9fb7768fcf2099
F src/pcache.c f8043b433a57aba85384a531e3937a804432a346 F src/pcache.c f8043b433a57aba85384a531e3937a804432a346
F src/pcache.h b1d8775a9bddf44e65edb0d20bfc57a4982f840f F src/pcache.h 1b5dcc3dc8103d03e625b177023ee67764fa6b7c
F src/pcache1.c 281822d22265245b19f908cb3f5df725f7e11b06 F src/pcache1.c b30b1c35908346ecc43d8d9d17f2ddf6817f8f60
F src/pragma.c fb979b7b5103ad0db1b72bcf349c83f5dec62574 F src/pragma.c e708b3bb5704605816f617e0b1d63a5488060715
F src/prepare.c ec4989f7f480544bdc4192fe663470d2a2d7d61e F src/prepare.c ec4989f7f480544bdc4192fe663470d2a2d7d61e
F src/printf.c 7ffb4ebb8b341f67e049695ba031da717b3d2699 F src/printf.c 7ffb4ebb8b341f67e049695ba031da717b3d2699
F src/random.c cd4a67b3953b88019f8cd4ccd81394a8ddfaba50 F src/random.c cd4a67b3953b88019f8cd4ccd81394a8ddfaba50
F src/resolve.c 3d3e80a98f203ac6b9329e9621e29eda85ddfd40 F src/resolve.c 748e75299faff345f34f0e5bd02a2bac8aa69fcd
F src/rowset.c 69afa95a97c524ba6faf3805e717b5b7ae85a697 F src/rowset.c f6a49f3e9579428024662f6e2931832511f831a1
F src/select.c a1d075db66a0ea42807353501b62997969e5be79 F src/select.c d7b9018b7dd2e821183d69477ab55c39b8272335
F src/shell.c aa4183d4a5243d8110b1d3d77faa4aea7e9c9c2d F src/shell.c 04399b2f9942bd02ed5ffee3b84bcdb39c52a1e6
F src/sqlite.h.in 53516617d2945a411d028674d7fa20dd394b9ec0 F src/sqlite.h.in 4f4d4792f6fb00387c877af013cb09d955643f12
F src/sqlite3ext.h 6904f4aadf976f95241311fbffb00823075d9477 F src/sqlite3ext.h 6904f4aadf976f95241311fbffb00823075d9477
F src/sqliteInt.h b8fdd9c39c8d7f5c794f4ea917293d9c75b9aff2 F src/sqliteInt.h c5e917c4f1453f3972b1fd0c81105dfe4f09cc32
F src/sqliteLimit.h 164b0e6749d31e0daa1a4589a169d31c0dec7b3d F src/sqliteLimit.h 164b0e6749d31e0daa1a4589a169d31c0dec7b3d
F src/status.c 4568e72dfd36b6a5911f93457364deb072e0b03a F src/status.c 35939e7e03abf1b7577ce311f48f682c40de3208
F src/table.c 2cd62736f845d82200acfa1287e33feb3c15d62e F src/table.c 2cd62736f845d82200acfa1287e33feb3c15d62e
F src/tclsqlite.c 2aeb69958965dad0842d5ea1456f1a958ef296e6 F src/tclsqlite.c fe5406573e1527957e00dcaf51edd9d8bd31b918
F src/test1.c 1b1e514e85ffe7152b02cba38bd0a1ce8cd56113 F src/test1.c a808bfa548a501b513579bdbaf83901c98e059c9
F src/test2.c 711555927f1f7e8db9aab86b512bc6934a774abe F src/test2.c 4178056dd1e7d70f954ad8a1e3edb71a2a784daf
F src/test3.c 91d3f1a09cfae3533ef17d8b484a160f3d1f1a21 F src/test3.c 3c3c2407fa6ec7a19e24ae23f7cb439d0275a60d
F src/test4.c d1e5a5e904d4b444cf572391fdcb017638e36ff7 F src/test4.c d1e5a5e904d4b444cf572391fdcb017638e36ff7
F src/test5.c e1a19845625144caf038031234a12185e40d315c F src/test5.c a6d1ac55ac054d0b2b8f37b5e655b6c92645a013
F src/test6.c cf6ab27a59e1ab64b011bb251ba600131e803e59 F src/test6.c 417e1e214734393c24a8ee80b41485a9c4169123
F src/test7.c 2e0781754905c8adc3268d8f0967e7633af58843 F src/test7.c 2e0781754905c8adc3268d8f0967e7633af58843
F src/test8.c 99f70341d6ec480313775127f4cd14b4a47db557 F src/test8.c 5ecbffe6712da81d5d10454e9d77d6c5bac95fe8
F src/test9.c bea1e8cf52aa93695487badedd6e1886c321ea60 F src/test9.c bea1e8cf52aa93695487badedd6e1886c321ea60
F src/test_async.c 0612a752896fad42d55c3999a5122af10dcf22ad F src/test_async.c 0612a752896fad42d55c3999a5122af10dcf22ad
F src/test_autoext.c 30e7bd98ab6d70a62bb9ba572e4c7df347fe645e F src/test_autoext.c 30e7bd98ab6d70a62bb9ba572e4c7df347fe645e
F src/test_backup.c c129c91127e9b46e335715ae2e75756e25ba27de F src/test_backup.c c129c91127e9b46e335715ae2e75756e25ba27de
F src/test_btree.c 47cd771250f09cdc6e12dda5bc71bc0b3abc96e2 F src/test_btree.c 47cd771250f09cdc6e12dda5bc71bc0b3abc96e2
F src/test_config.c a036a69b550ebc477ab9ca2b37269201f888436e F src/test_config.c 0de329e736eb4aa5845069bed630e5c72f012264
F src/test_demovfs.c 20a4975127993f4959890016ae9ce5535a880094 F src/test_demovfs.c 20a4975127993f4959890016ae9ce5535a880094
F src/test_devsym.c e7498904e72ba7491d142d5c83b476c4e76993bc F src/test_devsym.c e7498904e72ba7491d142d5c83b476c4e76993bc
F src/test_func.c 6232d722a4ddb193035aa13a03796bf57d6c12fd F src/test_func.c 090f2c3339e85c2c964435f99aed6f3da9d59525
F src/test_fuzzer.c f884f6f32e8513d34248d6e1ac8a32047fead254 F src/test_fuzzer.c 1d26aa965120420bc14807da29d4d4541bfa6148
F src/test_hexio.c c4773049603151704a6ab25ac5e936b5109caf5a F src/test_hexio.c abfdecb6fa58c354623978efceb088ca18e379cd
F src/test_init.c 3cbad7ce525aec925f8fda2192d576d47f0d478a F src/test_init.c 3cbad7ce525aec925f8fda2192d576d47f0d478a
F src/test_intarray.c d879bbf8e4ce085ab966d1f3c896a7c8b4f5fc99 F src/test_intarray.c d879bbf8e4ce085ab966d1f3c896a7c8b4f5fc99
F src/test_intarray.h 489edb9068bb926583445cb02589344961054207 F src/test_intarray.h 489edb9068bb926583445cb02589344961054207
F src/test_journal.c a6a6baf343f79b942331f13378d045e7e270ae64 F src/test_journal.c f5c0a05b7b3d5930db769b5ee6c3766dc2221a64
F src/test_loadext.c df586c27176e3c2cb2e099c78da67bf14379a56e F src/test_loadext.c df586c27176e3c2cb2e099c78da67bf14379a56e
F src/test_malloc.c 8d416f29ad8573f32601f6056c9d2b17472e9ad5 F src/test_malloc.c 3f5903a1528fd32fe4c472a3bd0259128d8faaef
F src/test_multiplex.c afab2c9d292677ab31e0dd4b3dde2420fb655c5f F src/test_multiplex.c 66dcfca001ee22f04ef31ad353772ed05a017e53
F src/test_multiplex.h e99c571bc4968b7a9363b661481f3934bfead61d F src/test_multiplex.h e99c571bc4968b7a9363b661481f3934bfead61d
F src/test_mutex.c a6bd7b9cf6e19d989e31392b06ac8d189f0d573e F src/test_mutex.c a6bd7b9cf6e19d989e31392b06ac8d189f0d573e
F src/test_onefile.c 40cf9e212a377a6511469384a64b01e6e34b2eec F src/test_onefile.c 0396f220561f3b4eedc450cef26d40c593c69a25
F src/test_osinst.c 6abf0a37ce831120c4ef1b913afdd813e7ac1a73 F src/test_osinst.c 90a845c8183013d80eccb1f29e8805608516edba
F src/test_pcache.c a5cd24730cb43c5b18629043314548c9169abb00 F src/test_pcache.c a5cd24730cb43c5b18629043314548c9169abb00
F src/test_quota.c b4a6519417d87870e7ef5838dbf3cae164dcc28d F src/test_quota.c 9d6be9cd3bb132df2b964193b6a4ec850f50a210
F src/test_quota.h 9ffa1d3ad6d0a6a24e8670ea64b909c717ec3358 F src/test_quota.h ee5da2ae7f84d1c8e0e0e2ab33f01d69f10259b5
F src/test_rtree.c 6d06306e29946dc36f528a3a2cdc3add794656f1 F src/test_rtree.c aba603c949766c4193f1068b91c787f57274e0d9
F src/test_schema.c 8c06ef9ddb240c7a0fcd31bc221a6a2aade58bf0 F src/test_schema.c 8c06ef9ddb240c7a0fcd31bc221a6a2aade58bf0
F src/test_server.c 2f99eb2837dfa06a4aacf24af24c6affdf66a84f F src/test_server.c 2f99eb2837dfa06a4aacf24af24c6affdf66a84f
F src/test_stat.c 80271ad7d776a79babe0e025bb3a1bfcd3a3cfb1 F src/test_spellfix.c 495535f3eb57acdc384572da570e869bb1834bf4
F src/test_stat.c d1569c7a4839f13e80187e2c26b2ab4da2d03935
F src/test_superlock.c 2b97936ca127d13962c3605dbc9a4ef269c424cd F src/test_superlock.c 2b97936ca127d13962c3605dbc9a4ef269c424cd
F src/test_syscall.c a992d8c80ea91fbf21fb2dd570db40e77dd7e6ae F src/test_syscall.c a992d8c80ea91fbf21fb2dd570db40e77dd7e6ae
F src/test_tclvar.c f4dc67d5f780707210d6bb0eb6016a431c04c7fa F src/test_tclvar.c f4dc67d5f780707210d6bb0eb6016a431c04c7fa
F src/test_thread.c 35022393dd54d147b998b6b7f7e945b01114d666 F src/test_thread.c e286f2173563f2a1747c24bcda6b9d030bf4f4e4
F src/test_vfs.c 07157a0bbfe161cb5e32cad2079abd26cd611c4b F src/test_vfs.c 9d934e111021d56c629efc73a796648c9519ad12
F src/test_vfstrace.c 065c7270a614254b2c68fbc7ba8d1fb1d5cbc823 F src/test_vfstrace.c 6b28adb2a0e8ecd0f2e3581482e1f658b11b4067
F src/test_wholenumber.c 6129adfbe7c7444f2e60cc785927f3aa74e12290 F src/test_wholenumber.c 3d2b9ed1505c40ad5c5ca2ad16ae7a289d6cc251
F src/test_wsd.c 41cadfd9d97fe8e3e4e44f61a4a8ccd6f7ca8fe9 F src/test_wsd.c 41cadfd9d97fe8e3e4e44f61a4a8ccd6f7ca8fe9
F src/tokenize.c 1e86210d3976717a19238ea7b047fac481fe8c12 F src/tokenize.c 1e86210d3976717a19238ea7b047fac481fe8c12
F src/trigger.c ee7e178fb9188f44b532cebd449a7c1df90fb684 F src/trigger.c ee7e178fb9188f44b532cebd449a7c1df90fb684
F src/update.c d3076782c887c10e882996550345da9c4c9f9dea F src/update.c d3076782c887c10e882996550345da9c4c9f9dea
F src/utf.c 890c67dcfcc7a74623c95baac7535aadfe265e84 F src/utf.c 890c67dcfcc7a74623c95baac7535aadfe265e84
F src/util.c 9e07bd67dfafe9c75b1da78c87ba030cebbb5388 F src/util.c 4f6cfad661b2e3454b0cdd5b1b9d39a54942d0e3
F src/vacuum.c 0c0ba2242355c6048d65e2b333abe0f7c06348fa F src/vacuum.c bfd53f9bd20a8fdb70b0fa8e77182b866875c0d8
F src/vdbe.c 64e3fc0bb56c54ceac3cc1d1e65257bfda21550e F src/vdbe.c e1d26b98288889c22f00cf4851ec351ee67ad8b9
F src/vdbe.h 18f581cac1f4339ec3299f3e0cc6e11aec654cdb F src/vdbe.h 18f581cac1f4339ec3299f3e0cc6e11aec654cdb
F src/vdbeInt.h ef9b8584b23b033894a0804dc6b90196c6779fb9 F src/vdbeInt.h 6ff4180a05683566a8835d12f7ec504b22932c82
F src/vdbeapi.c 3662b6a468a2a4605a15dfab313baa6dff81ad91 F src/vdbeapi.c 3662b6a468a2a4605a15dfab313baa6dff81ad91
F src/vdbeaux.c eb13a6917ed7455b5b49236fe5cfb3d3c3e4c57b F src/vdbeaux.c d52c8a424fdd4b1d5cf1ac93cc7cd20da023ec5c
F src/vdbeblob.c 32f2a4899d67f69634ea4dd93e3f651936d732cb F src/vdbeblob.c 32f2a4899d67f69634ea4dd93e3f651936d732cb
F src/vdbemem.c 4f7d25d5ea2e2040254095b8f6de07f8dbbadf80 F src/vdbemem.c cb55e84b8e2c15704968ee05f0fae25883299b74
F src/vdbesort.c 468d43c057063e54da4f1988b38b4f46d60e7790 F src/vdbesort.c b25814d385895544ebc8118245c8311ded7f81c9
F src/vdbetrace.c d6e50e04e1ec498150e519058f617d91b8f5c843 F src/vdbetrace.c d6e50e04e1ec498150e519058f617d91b8f5c843
F src/vtab.c e9318d88feac85be8e27ee783ac8f5397933fc8a F src/vtab.c ae657b1c22cff43863458e768a44f915c07bc0e4
F src/wal.c 5f7bcc0610af759953defd769eacebfd98a22bc8 F src/wal.c 7bb3ad807afc7973406c805d5157ec7a2f65e146
F src/wal.h eaa00b9a403ddda2b56d01b7afc19ef600f9363f F src/wal.h 29c197540b19044e6cd73487017e5e47a1d3dac6
F src/walker.c 3112bb3afe1d85dc52317cb1d752055e9a781f8f F src/walker.c 3112bb3afe1d85dc52317cb1d752055e9a781f8f
F src/where.c af623942514571895818b9b7ae11db95ae3b3d88 F src/where.c 24c7494d8875ead994b4dfe5461340c27fd424ca
F test/8_3_names.test 631ea964a3edb091cf73c3b540f6bcfdb36ce823 F test/8_3_names.test 631ea964a3edb091cf73c3b540f6bcfdb36ce823
F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2 F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2
F test/alias.test 4529fbc152f190268a15f9384a5651bbbabc9d87 F test/alias.test 4529fbc152f190268a15f9384a5651bbbabc9d87
F test/all.test 52fc8dee494092031a556911d404ca30a749a30b F test/all.test 52fc8dee494092031a556911d404ca30a749a30b
F test/alter.test 66f5818f9848c4f22de022a345fae25bcd30f8fb F test/alter.test 57d96ec9b320bd07af77567034488dcb6642c748
F test/alter2.test 7ea05c7d92ac99349a802ef7ada17294dd647060 F test/alter2.test 7ea05c7d92ac99349a802ef7ada17294dd647060
F test/alter3.test 49c9d9fba2b8fcdce2dedeca97bbf1f369cc548d F test/alter3.test 49c9d9fba2b8fcdce2dedeca97bbf1f369cc548d
F test/alter4.test b2debc14d8cbe4c1d12ccd6a41eef88a8c1f15d5 F test/alter4.test b2debc14d8cbe4c1d12ccd6a41eef88a8c1f15d5
@ -288,16 +282,17 @@ F test/autoindex1.test 058d0b331ae6840a61bbee910d8cbae27bfd5991
F test/autovacuum.test fcaf4616ae5bb18098db1cb36262565e5c841c3c F test/autovacuum.test fcaf4616ae5bb18098db1cb36262565e5c841c3c
F test/autovacuum_ioerr2.test 8a367b224183ad801e0e24dcb7d1501f45f244b4 F test/autovacuum_ioerr2.test 8a367b224183ad801e0e24dcb7d1501f45f244b4
F test/avtrans.test 0252654f4295ddda3b2cce0e894812259e655a85 F test/avtrans.test 0252654f4295ddda3b2cce0e894812259e655a85
F test/backcompat.test 71eeb75ea567c060774c4e8db4b0e703f21c7677 F test/backcompat.test bccbc64769d9c755ad65ee7c2f7336b86e3cc0c8
F test/backup.test 6970614b002b056ae5bab5b76559905e02b6f0b2 F test/backup.test 717346db953e9e435c2a94916e4af177330d60d3
F test/backup2.test 34986ef926ea522911a51dfdb2f8e99b7b75ebcf F test/backup2.test 34986ef926ea522911a51dfdb2f8e99b7b75ebcf
F test/backup_ioerr.test 40d208bc9224b666ee3ed423f49bc9062a36a9d0 F test/backup_ioerr.test 40d208bc9224b666ee3ed423f49bc9062a36a9d0
F test/backup_malloc.test 7162d604ec2b4683c4b3799a48657fb8b5e2d450 F test/backup_malloc.test 7162d604ec2b4683c4b3799a48657fb8b5e2d450
F test/badutf.test d5360fc31f643d37a973ab0d8b4fb85799c3169f F test/badutf.test d5360fc31f643d37a973ab0d8b4fb85799c3169f
F test/badutf2.test f5bc7f2d280670ecd79b9cf4f0f1760c607fe51f F test/badutf2.test f5bc7f2d280670ecd79b9cf4f0f1760c607fe51f
F test/bc_common.tcl 5c8689cc6d2fb44b7c0968ae4f85eb26d50022fa
F test/between.test 16b1776c6323faadb097a52d673e8e3d8be7d070 F test/between.test 16b1776c6323faadb097a52d673e8e3d8be7d070
F test/bigfile.test a8ec8073a20207456dab01a29ad9cde42b0dd103 F test/bigfile.test 82dfe93ee7eb9e2e05641afa2b39ffd947a92ff1
F test/bigfile2.test f8e83eca9abef60692a34255a2ebcb96aff897fc F test/bigfile2.test 852f948cb492aadab45b58f4d2f3b0832a115cb0
F test/bigrow.test f0aeb7573dcb8caaafea76454be3ade29b7fc747 F test/bigrow.test f0aeb7573dcb8caaafea76454be3ade29b7fc747
F test/bind.test 3c7b320969000c441a70952b0b15938fbb66237c F test/bind.test 3c7b320969000c441a70952b0b15938fbb66237c
F test/bindxfer.test efecd12c580c14df5f4ad3b3e83c667744a4f7e0 F test/bindxfer.test efecd12c580c14df5f4ad3b3e83c667744a4f7e0
@ -312,15 +307,15 @@ F test/boundary3.test 56ef82096b4329aca2be74fa1e2b0f762ea0eb45
F test/boundary4.tcl 0bb4b1a94f4fc5ae59b79b9a2b7a140c405e2983 F test/boundary4.tcl 0bb4b1a94f4fc5ae59b79b9a2b7a140c405e2983
F test/boundary4.test 89e02fa66397b8a325d5eb102b5806f961f8ec4b F test/boundary4.test 89e02fa66397b8a325d5eb102b5806f961f8ec4b
F test/busy.test 76b4887f8b9160ba903c1ac22e8ff406ad6ae2f0 F test/busy.test 76b4887f8b9160ba903c1ac22e8ff406ad6ae2f0
F test/cache.test 754baab2f18089fc9bcba7afaeb4dc907c6c6de2 F test/cache.test f64136b0893c293d0b910ed057b3b711249099a7
F test/capi2.test 835d4cee9f542ea50fa8d01f3fe6de80b0627360 F test/capi2.test 835d4cee9f542ea50fa8d01f3fe6de80b0627360
F test/capi3.test 7200dff6acb17b9a4b6f9918f554eaae04968ddd F test/capi3.test 8dedb0050610e9ff95cd9d487beb0ce5f33a31ee
F test/capi3b.test efb2b9cfd127efa84433cd7a2d72ce0454ae0dc4 F test/capi3b.test efb2b9cfd127efa84433cd7a2d72ce0454ae0dc4
F test/capi3c.test ccf0acf045dbacd09f6229aa4efed670aaba76a9 F test/capi3c.test 01f197d73f4d4d66316483662f475cab7ab5bd60
F test/capi3d.test 17b57ca28be3e37e14c2ba8f787d292d84b724a1 F test/capi3d.test 17b57ca28be3e37e14c2ba8f787d292d84b724a1
F test/capi3e.test f7408dda65c92b9056199fdc180f893015f83dde F test/capi3e.test f7408dda65c92b9056199fdc180f893015f83dde
F test/cast.test 4c275cbdc8202d6f9c54a3596701719868ac7dc3 F test/cast.test 4c275cbdc8202d6f9c54a3596701719868ac7dc3
F test/check.test db2b29d557544347d28e25b8406f5d5ecc3d1bc3 F test/check.test 193f47ed43a8d29aca12b30cd30ceb105fbe710d
F test/coalesce.test cee0dccb9fbd2d494b77234bccf9dc6c6786eb91 F test/coalesce.test cee0dccb9fbd2d494b77234bccf9dc6c6786eb91
F test/collate1.test e3eaa48c21e150814be1a7b852d2a8af24458d04 F test/collate1.test e3eaa48c21e150814be1a7b852d2a8af24458d04
F test/collate2.test 04cebe4a033be319d6ddbb3bbc69464e01700b49 F test/collate2.test 04cebe4a033be319d6ddbb3bbc69464e01700b49
@ -355,7 +350,7 @@ F test/crash.test 519dc29f6fea151f015a23236e555239353946eb
F test/crash2.test 5b14d4eb58b880e231361d3b609b216acda86651 F test/crash2.test 5b14d4eb58b880e231361d3b609b216acda86651
F test/crash3.test 8f5de9d32ab9ab95475a9efe7f47a940aa889418 F test/crash3.test 8f5de9d32ab9ab95475a9efe7f47a940aa889418
F test/crash4.test fe2821baf37168dc59dd733dcf7dba2a401487bc F test/crash4.test fe2821baf37168dc59dd733dcf7dba2a401487bc
F test/crash5.test 69226a1b948d8961395b7ad2a1df084c212ce8cf F test/crash5.test 13b9ca94e048194bca070e26160ce76b406c56be
F test/crash6.test 4c56f1e40d0291e1110790a99807aa875b1647ba F test/crash6.test 4c56f1e40d0291e1110790a99807aa875b1647ba
F test/crash7.test 6c6a369af266af2ef50ab34df8f94d719065e2c1 F test/crash7.test 6c6a369af266af2ef50ab34df8f94d719065e2c1
F test/crash8.test 38767cb504bbe491de6be4a7006b154973a2309f F test/crash8.test 38767cb504bbe491de6be4a7006b154973a2309f
@ -364,8 +359,8 @@ F test/createtab.test b5de160630b209c4b8925bdcbbaf48cc90b67fe8
F test/cse.test 277350a26264495e86b1785f34d2d0c8600e021c F test/cse.test 277350a26264495e86b1785f34d2d0c8600e021c
F test/ctime.test 7bd009071e242aac4f18521581536b652b789a47 F test/ctime.test 7bd009071e242aac4f18521581536b652b789a47
F test/date.test a18a2ce81add84b17b06559e82ad7bb91bc6ddff F test/date.test a18a2ce81add84b17b06559e82ad7bb91bc6ddff
F test/dbstatus.test 3e978f8bdb2362a36a4be63c36a59f542c4cc2a3 F test/dbstatus.test 207e5b63fcb7b9c3bb8e1fdf38ebd4654ad0e54b
F test/dbstatus2.test dc57b0d9610851c0ff58a8e1b5b191678398b72a F test/dbstatus2.test b1de8250fde1f3474d6b86f0e89de38d84794f56
F test/default.test 6faf23ccb300114924353007795aa9a8ec0aa9dc F test/default.test 6faf23ccb300114924353007795aa9a8ec0aa9dc
F test/delete.test a065b05d2ebf60fd16639c579a4adfb7c381c701 F test/delete.test a065b05d2ebf60fd16639c579a4adfb7c381c701
F test/delete2.test 3a03f2cca1f9a67ec469915cb8babd6485db43fa F test/delete2.test 3a03f2cca1f9a67ec469915cb8babd6485db43fa
@ -374,28 +369,28 @@ F test/descidx1.test 533dcbda614b0463b0ea029527fd27e5a9ab2d66
F test/descidx2.test 9f1a0c83fd57f8667c82310ca21b30a350888b5d F test/descidx2.test 9f1a0c83fd57f8667c82310ca21b30a350888b5d
F test/descidx3.test fe720e8b37d59f4cef808b0bf4e1b391c2e56b6f F test/descidx3.test fe720e8b37d59f4cef808b0bf4e1b391c2e56b6f
F test/diskfull.test 106391384780753ea6896b7b4f005d10e9866b6e F test/diskfull.test 106391384780753ea6896b7b4f005d10e9866b6e
F test/distinct.test df5b11ad606439129c88720a86787bc9ca181f31 F test/distinct.test da36612d05b9ed17e0425d4bfd7ab978d28a7e46
F test/distinctagg.test 1a6ef9c87a58669438fc771450d7a72577417376 F test/distinctagg.test 1a6ef9c87a58669438fc771450d7a72577417376
F test/e_createtable.test 48598b15e8fe6554d301e7b65a10c9851f177e84 F test/e_createtable.test 48598b15e8fe6554d301e7b65a10c9851f177e84
F test/e_delete.test ec168cd4b08d681e6d5847f462203755ad647532 F test/e_delete.test 89aa84d3d1bd284a0689ede04bce10226a5aeaa5
F test/e_droptrigger.test afd5c4d27dec607f5997a66bf7e2498a082cb235 F test/e_droptrigger.test afd5c4d27dec607f5997a66bf7e2498a082cb235
F test/e_dropview.test 583411e470458c5d76148542cfb5a5fa84c8f93e F test/e_dropview.test 583411e470458c5d76148542cfb5a5fa84c8f93e
F test/e_expr.test 4e4399006b3d1ab333721b8e386cabb9fb6d5a89 F test/e_expr.test 5489424d3d9a452ac3701cdf4b680ae31a157894
F test/e_fkey.test 38039b840ab19331000b0f0eb1d82baa7208a67a F test/e_fkey.test 057eed81a41a2b21b1790032f4e8aaba0b2b0e17
F test/e_fts3.test 5c02288842e4f941896fd44afdef564dd5fc1459 F test/e_fts3.test 5c02288842e4f941896fd44afdef564dd5fc1459
F test/e_insert.test 234242b71855af8e8a9b1e141c3533f6d43d8645 F test/e_insert.test c6ac239a97cb16dfbd0c16496f8cd871b4068c0c
F test/e_reindex.test dfedfc32c5a282b0596c6537cbcd4217fbb1a216 F test/e_reindex.test dfedfc32c5a282b0596c6537cbcd4217fbb1a216
F test/e_resolve.test dcce9308fb13b934ce29591105d031d3e14fbba6 F test/e_resolve.test dcce9308fb13b934ce29591105d031d3e14fbba6
F test/e_select.test 6f488c22851c1c280ed74e16a9a1430706a3a8cb F test/e_select.test f5d4b81205701deacfae42051ae200969c41d2c0
F test/e_select2.test 5c3d3da19c7b3e90ae444579db2b70098599ab92 F test/e_select2.test 5c3d3da19c7b3e90ae444579db2b70098599ab92
F test/e_update.test dba988a4d079156549a40854074ba4890b0a4577 F test/e_update.test 161d5dc6a3ed9dd08f5264d13e20735d7a89f00c
F test/e_uri.test 6f35b491f80dac005c8144f38b2dfb4d96483596 F test/e_uri.test e8b894474fdfe7b18b0c9cb2d911270de2ad64ce
F test/e_vacuum.test 5296e25ef871965bac010b9da083dd7e4734526e F test/e_vacuum.test 331da289ae186656cf5f2eb27f577a89c0c221af
F test/enc.test e54531cd6bf941ee6760be041dff19a104c7acea F test/enc.test e54531cd6bf941ee6760be041dff19a104c7acea
F test/enc2.test 796c59832e2b9a52842f382ffda8f3e989db03ad F test/enc2.test 796c59832e2b9a52842f382ffda8f3e989db03ad
F test/enc3.test 90683ad0e6ea587b9d5542ca93568af9a9858c40 F test/enc3.test 90683ad0e6ea587b9d5542ca93568af9a9858c40
F test/enc4.test c8f1ce3618508fd0909945beb8b8831feef2c020 F test/enc4.test c8f1ce3618508fd0909945beb8b8831feef2c020
F test/eqp.test f14fadd76da53405e9885e2431cacf7191d83cdb F test/eqp.test 6a389bba6ea113fd5179515001be788a38d53ec7
F test/errmsg.test 3bb606db9d040cc6854459f8f5e5a2bcd9b7fd2a F test/errmsg.test 3bb606db9d040cc6854459f8f5e5a2bcd9b7fd2a
F test/eval.test bc269c365ba877554948441e91ad5373f9f91be3 F test/eval.test bc269c365ba877554948441e91ad5373f9f91be3
F test/exclusive.test a1b324cb21834a490cd052d409d34789cfef57cb F test/exclusive.test a1b324cb21834a490cd052d409d34789cfef57cb
@ -404,7 +399,7 @@ F test/exec.test e949714dc127eaa5ecc7d723efec1ec27118fdd7
F test/exists.test 8f7b27b61c2fbe5822f0a1f899c715d14e416e30 F test/exists.test 8f7b27b61c2fbe5822f0a1f899c715d14e416e30
F test/expr.test 67c9fd6f8f829e239dc8b0f4a08a73c08b09196d F test/expr.test 67c9fd6f8f829e239dc8b0f4a08a73c08b09196d
F test/fallocate.test b5d34437bd7ab01d41b1464b8117aefd4d32160e F test/fallocate.test b5d34437bd7ab01d41b1464b8117aefd4d32160e
F test/filectrl.test 4eb0178956ca25a756e6d79711a90fec7157b454 F test/filectrl.test f0327bd804d9c7bd048fa7a151c5eab8e27df42b
F test/filefmt.test ffa17b5aebc3eb4b1e3be1ccb5ee906ffbd97f6e F test/filefmt.test ffa17b5aebc3eb4b1e3be1ccb5ee906ffbd97f6e
F test/fkey1.test 01c7de578e11747e720c2d9aeef27f239853c4da F test/fkey1.test 01c7de578e11747e720c2d9aeef27f239853c4da
F test/fkey2.test 080969fe219b3b082b0e097ac18c6af2e5b0631f F test/fkey2.test 080969fe219b3b082b0e097ac18c6af2e5b0631f
@ -448,7 +443,7 @@ F test/fts2q.test b2fbbe038b7a31a52a6079b215e71226d8c6a682
F test/fts2r.test b154c30b63061d8725e320fba1a39e2201cadd5e F test/fts2r.test b154c30b63061d8725e320fba1a39e2201cadd5e
F test/fts2token.test d8070b241a15ff13592a9ae4a8b7c171af6f445a F test/fts2token.test d8070b241a15ff13592a9ae4a8b7c171af6f445a
F test/fts3.test 672a040ea57036fb4b6fdc09027c18d7d24ab654 F test/fts3.test 672a040ea57036fb4b6fdc09027c18d7d24ab654
F test/fts3_common.tcl 4d8eec9db565fed9098f45c378f28e1657802011 F test/fts3_common.tcl 99cf6659b87c0f74f55963c2aea03b3a7d66ceb0
F test/fts3aa.test 909d5f530d30a8e36b9328d67285eae6537c79c0 F test/fts3aa.test 909d5f530d30a8e36b9328d67285eae6537c79c0
F test/fts3ab.test 09aeaa162aee6513d9ff336b6932211008b9d1f9 F test/fts3ab.test 09aeaa162aee6513d9ff336b6932211008b9d1f9
F test/fts3ac.test 636ed7486043055d4f126a0e385f2d5a82ebbf63 F test/fts3ac.test 636ed7486043055d4f126a0e385f2d5a82ebbf63
@ -465,7 +460,7 @@ F test/fts3am.test 218aa6ba0dfc50c7c16b2022aac5c6be593d08d8
F test/fts3an.test a49ccadc07a2f7d646ec1b81bc09da2d85a85b18 F test/fts3an.test a49ccadc07a2f7d646ec1b81bc09da2d85a85b18
F test/fts3ao.test e7b80272efcced57d1d087a9da5c690dd7c21fd9 F test/fts3ao.test e7b80272efcced57d1d087a9da5c690dd7c21fd9
F test/fts3atoken.test 402ef2f7c2fb4b3d4fa0587df6441c1447e799b3 F test/fts3atoken.test 402ef2f7c2fb4b3d4fa0587df6441c1447e799b3
F test/fts3auto.test c1a30b37002b7c764a96937fbc71065b73d69494 F test/fts3auto.test b39f3f51227aea145eae6638690355dbdf9abf18
F test/fts3aux1.test 0b02743955d56fc0d4d66236a26177bd1b726de0 F test/fts3aux1.test 0b02743955d56fc0d4d66236a26177bd1b726de0
F test/fts3b.test e93bbb653e52afde110ad53bbd793f14fe7a8984 F test/fts3b.test e93bbb653e52afde110ad53bbd793f14fe7a8984
F test/fts3c.test fc723a9cf10b397fdfc2b32e73c53c8b1ec02958 F test/fts3c.test fc723a9cf10b397fdfc2b32e73c53c8b1ec02958
@ -475,7 +470,7 @@ F test/fts3corrupt.test 7b0f91780ca36118d73324ec803187208ad33b32
F test/fts3corrupt2.test 6d96efae2f8a6af3eeaf283aba437e6d0e5447ba F test/fts3corrupt2.test 6d96efae2f8a6af3eeaf283aba437e6d0e5447ba
F test/fts3cov.test e0fb00d8b715ddae4a94c305992dfc3ef70353d7 F test/fts3cov.test e0fb00d8b715ddae4a94c305992dfc3ef70353d7
F test/fts3d.test bf640d79722b720fa1c81834c48cdaa45d531b1a F test/fts3d.test bf640d79722b720fa1c81834c48cdaa45d531b1a
F test/fts3defer.test 2ea3fa028f8d9523f9c33dd8acc4555d567ea4ac F test/fts3defer.test 6c2707be1b05b9790ba8ff91d3391d5fb425269e
F test/fts3defer2.test 35867d33ba6db03f6c73bd6f5fc333ae14f68c81 F test/fts3defer2.test 35867d33ba6db03f6c73bd6f5fc333ae14f68c81
F test/fts3drop.test 1b906e293d6773812587b3dc458cb9e8f3f0c297 F test/fts3drop.test 1b906e293d6773812587b3dc458cb9e8f3f0c297
F test/fts3e.test 1f6c6ac9cc8b772ca256e6b22aaeed50c9350851 F test/fts3e.test 1f6c6ac9cc8b772ca256e6b22aaeed50c9350851
@ -488,14 +483,20 @@ F test/fts3malloc.test b86ea33db9e8c58c0c2f8027a9fcadaf6a1568be
F test/fts3matchinfo.test 6507fe1c342e542300d65ea637d4110eccf894e6 F test/fts3matchinfo.test 6507fe1c342e542300d65ea637d4110eccf894e6
F test/fts3near.test 2e318ee434d32babd27c167142e2b94ddbab4844 F test/fts3near.test 2e318ee434d32babd27c167142e2b94ddbab4844
F test/fts3prefix.test b36d4f00b128a51e7b386cc013a874246d9d7dc1 F test/fts3prefix.test b36d4f00b128a51e7b386cc013a874246d9d7dc1
F test/fts3prefix2.test 477ca96e67f60745b7ac931cfa6e9b080c562da5
F test/fts3query.test ef79d31fdb355d094baec1c1b24b60439a1fb8a2 F test/fts3query.test ef79d31fdb355d094baec1c1b24b60439a1fb8a2
F test/fts3rnd.test 1320d8826a845e38a96e769562bf83d7a92a15d0 F test/fts3rnd.test 1320d8826a845e38a96e769562bf83d7a92a15d0
F test/fts3shared.test 8bb266521d7c5495c0ae522bb4d376ad5387d4a2 F test/fts3shared.test 8bb266521d7c5495c0ae522bb4d376ad5387d4a2
F test/fts3snippet.test 8e956051221a34c7daeb504f023cb54d5fa5a8b2 F test/fts3snippet.test 8e956051221a34c7daeb504f023cb54d5fa5a8b2
F test/fts3sort.test 95be0b19d7e41c44b29014f13ea8bddd495fd659 F test/fts3sort.test 95be0b19d7e41c44b29014f13ea8bddd495fd659
F test/fts4aa.test 6e7f90420b837b2c685f3bcbe84c868492d40a68 F test/fts4aa.test 6e7f90420b837b2c685f3bcbe84c868492d40a68
F test/fts4content.test 2624253c7e5a32d0c0d51f776dcd4526f0a51097 F test/fts4check.test 66fa274cab2b615f2fb338b257713aba8fad88a8
F test/func.test 6c5ce11e3a0021ca3c0649234e2d4454c89110ca F test/fts4content.test 17b2360f7d1a9a7e5aa8022783f5c5731b6dfd4f
F test/fts4langid.test 24a6e41063b416bbdf371ff6b4476fa41c194aa7
F test/fts4merge.test c424309743fdd203f8e56a1f1cd7872cd66cc0ee
F test/fts4merge2.test 5faa558d1b672f82b847d2a337465fa745e46891
F test/fts4merge3.test aab02a09f50fe6baaddc2e159c3eabc116d45fc7
F test/func.test 9809b7622d721904a8cc33c1ffb87f46d506ed01
F test/func2.test 772d66227e4e6684b86053302e2d74a2500e1e0f F test/func2.test 772d66227e4e6684b86053302e2d74a2500e1e0f
F test/func3.test 001021e5b88bd02a3b365a5c5fd8f6f49d39744a F test/func3.test 001021e5b88bd02a3b365a5c5fd8f6f49d39744a
F test/fuzz.test 77fd50afc12847af50fcf1941679d90adebadde6 F test/fuzz.test 77fd50afc12847af50fcf1941679d90adebadde6
@ -503,16 +504,18 @@ F test/fuzz2.test 207d0f9d06db3eaf47a6b7bfc835b8e2fc397167
F test/fuzz3.test aec64345184d1662bd30e6a17851ff659d596dc5 F test/fuzz3.test aec64345184d1662bd30e6a17851ff659d596dc5
F test/fuzz_common.tcl a87dfbb88c2a6b08a38e9a070dabd129e617b45b F test/fuzz_common.tcl a87dfbb88c2a6b08a38e9a070dabd129e617b45b
F test/fuzz_malloc.test 328f70aaca63adf29b4c6f06505ed0cf57ca7c26 F test/fuzz_malloc.test 328f70aaca63adf29b4c6f06505ed0cf57ca7c26
F test/fuzzer1.test ddfb04f3bd5cfdda3b1aa15b78d3ad055c9cc50f F test/fuzzer1.test 69cf1036b92fd3b8e1fd65bef4d7ee3f085c28fb
F test/fuzzerfault.test ff2282c81797b6a355f0748d8b54c7287c5d2b25
F test/hook.test 5f3749de6462a6b87b4209b74adf7df5ac2df639 F test/hook.test 5f3749de6462a6b87b4209b74adf7df5ac2df639
F test/icu.test 70df4faca133254c042d02ae342c0a141f2663f4 F test/icu.test 70df4faca133254c042d02ae342c0a141f2663f4
F test/in.test 19b642bb134308980a92249750ea4ce3f6c75c2d F test/in.test 5941096407d8c133b9eff15bd3e666624b6cbde3
F test/in2.test 5d4c61d17493c832f7d2d32bef785119e87bde75 F test/in2.test 5d4c61d17493c832f7d2d32bef785119e87bde75
F test/in3.test 3cbf58c87f4052cee3a58b37b6389777505aa0c0 F test/in3.test 3cbf58c87f4052cee3a58b37b6389777505aa0c0
F test/in4.test 64f3cc1acde1b9161ccdd8e5bde3daefdb5b2617 F test/in4.test 64f3cc1acde1b9161ccdd8e5bde3daefdb5b2617
F test/incrblob.test 3307c04876fe025e10256e3cc8050ab5a84aa27f F test/incrblob.test 26fde912a1e0aff158b3a84ef3b265f046aad3be
F test/incrblob2.test edc3a96e557bd61fb39acc8d2edd43371fbbaa19 F test/incrblob2.test edc3a96e557bd61fb39acc8d2edd43371fbbaa19
F test/incrblob3.test aedbb35ea1b6450c33b98f2b6ed98e5020be8dc7 F test/incrblob3.test aedbb35ea1b6450c33b98f2b6ed98e5020be8dc7
F test/incrblob4.test 09be37d3dd996a31ea6993bba7837ece549414a8
F test/incrblob_err.test d2562d2771ebffd4b3af89ef64c140dd44371597 F test/incrblob_err.test d2562d2771ebffd4b3af89ef64c140dd44371597
F test/incrblobfault.test 917c0292224c64a56ef7215fd633a3a82f805be0 F test/incrblobfault.test 917c0292224c64a56ef7215fd633a3a82f805be0
F test/incrvacuum.test d2a6ddf5e429720b5fe502766af747915ccf6c32 F test/incrvacuum.test d2a6ddf5e429720b5fe502766af747915ccf6c32
@ -525,7 +528,7 @@ F test/index4.test 2983216eb8c86ee62d9ed7cb206b5cc3331c0026
F test/indexedby.test be501e381b82b2f8ab406309ba7aac46e221f4ad F test/indexedby.test be501e381b82b2f8ab406309ba7aac46e221f4ad
F test/indexfault.test 31d4ab9a7d2f6e9616933eb079722362a883eb1d F test/indexfault.test 31d4ab9a7d2f6e9616933eb079722362a883eb1d
F test/init.test 15c823093fdabbf7b531fe22cf037134d09587a7 F test/init.test 15c823093fdabbf7b531fe22cf037134d09587a7
F test/insert.test aef273dd1cee84cc92407469e6bd1b3cdcb76908 F test/insert.test 489aa12a027c83d291f5034a83c8c32e6be1dca2
F test/insert2.test 4f3a04d168c728ed5ec2c88842e772606c7ce435 F test/insert2.test 4f3a04d168c728ed5ec2c88842e772606c7ce435
F test/insert3.test 1b7db95a03ad9c5013fdf7d6722b6cd66ee55e30 F test/insert3.test 1b7db95a03ad9c5013fdf7d6722b6cd66ee55e30
F test/insert4.test 87f6798f31d60c4e177622fcc3663367e6ecbd90 F test/insert4.test 87f6798f31d60c4e177622fcc3663367e6ecbd90
@ -533,9 +536,9 @@ F test/insert5.test 394f96728d1258f406fe5f5aeb0aaf29487c39a6
F test/intarray.test 066b7d7ac38d25bf96f87f1b017bfc687551cdd4 F test/intarray.test 066b7d7ac38d25bf96f87f1b017bfc687551cdd4
F test/interrupt.test 42e7cf98646fd9cb4a3b131a93ed3c50b9e149f1 F test/interrupt.test 42e7cf98646fd9cb4a3b131a93ed3c50b9e149f1
F test/intpkey.test 537669fd535f62632ca64828e435b9e54e8d677f F test/intpkey.test 537669fd535f62632ca64828e435b9e54e8d677f
F test/io.test b278aa8fa609ed0dcc825df31b2d9f526c5a52bd F test/io.test 36d251507d72e92b965fb2f0801c2f0b56335bcf
F test/ioerr.test 40bb2cfcab63fb6aa7424cd97812a84bc16b5fb8 F test/ioerr.test 40bb2cfcab63fb6aa7424cd97812a84bc16b5fb8
F test/ioerr2.test 1b56cb80d5b0726ee3ba325ca175734541e32955 F test/ioerr2.test 9d71166f8466eda510f1af6137bdabaa82b5408d
F test/ioerr3.test d3cec5e1a11ad6d27527d0d38573fbff14c71bdd F test/ioerr3.test d3cec5e1a11ad6d27527d0d38573fbff14c71bdd
F test/ioerr4.test f130fe9e71008577b342b8874d52984bd04ede2c F test/ioerr4.test f130fe9e71008577b342b8874d52984bd04ede2c
F test/ioerr5.test 2edfa4fb0f896f733071303b42224df8bedd9da4 F test/ioerr5.test 2edfa4fb0f896f733071303b42224df8bedd9da4
@ -544,10 +547,10 @@ F test/join2.test f2171c265e57ee298a27e57e7051d22962f9f324
F test/join3.test 6f0c774ff1ba0489e6c88a3e77b9d3528fb4fda0 F test/join3.test 6f0c774ff1ba0489e6c88a3e77b9d3528fb4fda0
F test/join4.test 1a352e4e267114444c29266ce79e941af5885916 F test/join4.test 1a352e4e267114444c29266ce79e941af5885916
F test/join5.test 86675fc2919269aa923c84dd00ee4249b97990fe F test/join5.test 86675fc2919269aa923c84dd00ee4249b97990fe
F test/join6.test bf82cf3f979e9eade83ad0d056a66c5ed71d1901 F test/join6.test cfe6503791ceb0cbb509966740286ec423cbf10b
F test/journal1.test 8b71ef1ed5798bdc0e6eb616d8694e2c2c188d4d F test/journal1.test 8b71ef1ed5798bdc0e6eb616d8694e2c2c188d4d
F test/journal2.test ae06f566c28552c313ded3fee79a6c69e6d049b1 F test/journal2.test ae06f566c28552c313ded3fee79a6c69e6d049b1
F test/journal3.test 6fd28532c88b447db844186bc190523108b6dbb4 F test/journal3.test ff8af941f9e06161d3db1b46bb9f965ff0e7f307
F test/jrnlmode.test 9ee3a78f53d52cca737db69293d15dc41c0cbd36 F test/jrnlmode.test 9ee3a78f53d52cca737db69293d15dc41c0cbd36
F test/jrnlmode2.test 81610545a4e6ed239ea8fa661891893385e23a1d F test/jrnlmode2.test 81610545a4e6ed239ea8fa661891893385e23a1d
F test/jrnlmode3.test 556b447a05be0e0963f4311e95ab1632b11c9eaa F test/jrnlmode3.test 556b447a05be0e0963f4311e95ab1632b11c9eaa
@ -600,17 +603,18 @@ F test/memsubsys2.test 3a1c1a9de48e5726faa85108b02459fae8cb9ee9
F test/minmax.test 722d80816f7e096bf2c04f4111f1a6c1ba65453d F test/minmax.test 722d80816f7e096bf2c04f4111f1a6c1ba65453d
F test/minmax2.test 33504c01a03bd99226144e4b03f7631a274d66e0 F test/minmax2.test 33504c01a03bd99226144e4b03f7631a274d66e0
F test/minmax3.test cc1e8b010136db0d01a6f2a29ba5a9f321034354 F test/minmax3.test cc1e8b010136db0d01a6f2a29ba5a9f321034354
F test/minmax4.test 536a3360470633a177e42fbc19660d146b51daef
F test/misc1.test 55cb2bfbf4a8cd61f4be1effc30426ad41696bff F test/misc1.test 55cb2bfbf4a8cd61f4be1effc30426ad41696bff
F test/misc2.test 00d7de54eda90e237fc9a38b9e5ccc769ebf6d4d F test/misc2.test 00d7de54eda90e237fc9a38b9e5ccc769ebf6d4d
F test/misc3.test fe55130a43e444ee75e2156ff75dc96e964b5738 F test/misc3.test fe55130a43e444ee75e2156ff75dc96e964b5738
F test/misc4.test 9c078510fbfff05a9869a0b6d8b86a623ad2c4f6 F test/misc4.test 9c078510fbfff05a9869a0b6d8b86a623ad2c4f6
F test/misc5.test 528468b26d03303b1f047146e5eefc941b9069f5 F test/misc5.test 528468b26d03303b1f047146e5eefc941b9069f5
F test/misc6.test 953cc693924d88e6117aeba16f46f0bf5abede91 F test/misc6.test 953cc693924d88e6117aeba16f46f0bf5abede91
F test/misc7.test eafaa41b9133d7a2ded4641bbe5f340731d35a52 F test/misc7.test 4337d84e441f36cee62656f9f7ba8bc22a7ca721
F test/misuse.test ba4fb5d1a6101d1c171ea38b3c613d0661c83054 F test/misuse.test ba4fb5d1a6101d1c171ea38b3c613d0661c83054
F test/multiplex.test e08cc7177bd6d85990ee1d71100bb6c684c02256 F test/multiplex.test e08cc7177bd6d85990ee1d71100bb6c684c02256
F test/multiplex2.test 580ca5817c7edbe4cc68fa150609c9473393003a F test/multiplex2.test 580ca5817c7edbe4cc68fa150609c9473393003a
F test/multiplex3.test 15903c343f1eaa4b00998b7ceacfc4987e4ccfe9 F test/multiplex3.test d228f59eac91839a977eac19f21d053f03e4d101
F test/mutex1.test 78b2b9bb320e51d156c4efdb71b99b051e7a4b41 F test/mutex1.test 78b2b9bb320e51d156c4efdb71b99b051e7a4b41
F test/mutex2.test bfeaeac2e73095b2ac32285d2756e3a65e681660 F test/mutex2.test bfeaeac2e73095b2ac32285d2756e3a65e681660
F test/nan.test e9648b9d007c7045242af35e11a984d4b169443a F test/nan.test e9648b9d007c7045242af35e11a984d4b169443a
@ -621,7 +625,7 @@ F test/notnull.test cc7c78340328e6112a13c3e311a9ab3127114347
F test/null.test a8b09b8ed87852742343b33441a9240022108993 F test/null.test a8b09b8ed87852742343b33441a9240022108993
F test/openv2.test 0d3040974bf402e19b7df4b783e447289d7ab394 F test/openv2.test 0d3040974bf402e19b7df4b783e447289d7ab394
F test/oserror.test 50417780d0e0d7cd23cf12a8277bb44024765df3 F test/oserror.test 50417780d0e0d7cd23cf12a8277bb44024765df3
F test/pager1.test 9e9f5f1c6d4df4831dbff213b1262ef94bf72118 F test/pager1.test 31fef8ff6d5cbb4643f430e31756312d361ecfdf
F test/pager2.test 745b911dde3d1f24ae0870bd433dfa83d7c658c1 F test/pager2.test 745b911dde3d1f24ae0870bd433dfa83d7c658c1
F test/pager3.test 3856d9c80839be0668efee1b74811b1b7f7fc95f F test/pager3.test 3856d9c80839be0668efee1b74811b1b7f7fc95f
F test/pagerfault.test 452f2cc23e3bfcfa935f4442aec1da4fe1dc0442 F test/pagerfault.test 452f2cc23e3bfcfa935f4442aec1da4fe1dc0442
@ -631,19 +635,19 @@ F test/pageropt.test 9191867ed19a2b3db6c42d1b36b6fbc657cd1ab0
F test/pagesize.test 1dd51367e752e742f58e861e65ed7390603827a0 F test/pagesize.test 1dd51367e752e742f58e861e65ed7390603827a0
F test/pcache.test 065aa286e722ab24f2e51792c1f093bf60656b16 F test/pcache.test 065aa286e722ab24f2e51792c1f093bf60656b16
F test/pcache2.test a83efe2dec0d392f814bfc998def1d1833942025 F test/pcache2.test a83efe2dec0d392f814bfc998def1d1833942025
F test/permutations.test fa6f0e5f13fe0b1d3f7a7613179b7f7b20028184 F test/permutations.test dbda172249564f43ec556108a704581044c57dbd
F test/pragma.test 7fa35e53085812dac94c2bfcbb02c2a4ad35df5e F test/pragma.test c51c148defe32bf4a419a522f95d26838d5cf677
F test/pragma2.test 3a55f82b954242c642f8342b17dffc8b47472947 F test/pragma2.test 3a55f82b954242c642f8342b17dffc8b47472947
F test/printf.test ec9870c4dce8686a37818e0bf1aba6e6a1863552 F test/printf.test ec9870c4dce8686a37818e0bf1aba6e6a1863552
F test/progress.test 5b075c3c790c7b2a61419bc199db87aaf48b8301 F test/progress.test 5b075c3c790c7b2a61419bc199db87aaf48b8301
F test/ptrchng.test ef1aa72d6cf35a2bbd0869a649b744e9d84977fc F test/ptrchng.test ef1aa72d6cf35a2bbd0869a649b744e9d84977fc
F test/quick.test 1681febc928d686362d50057c642f77a02c62e57 F test/quick.test 1681febc928d686362d50057c642f77a02c62e57
F test/quota-glob.test 32901e9eed6705d68ca3faee2a06b73b57cb3c26 F test/quota-glob.test 32901e9eed6705d68ca3faee2a06b73b57cb3c26
F test/quota.test af47d25c166aa7b33ef25f21bb7f2afb29d82c77 F test/quota.test c2f778dab4c7fb07bcfa962cc5c762f36d8061dc
F test/quota2.test 1b8df088e604f2df573f96e726b5e518cb0cddaa F test/quota2.test bc9fdb2e46aace691c1a01a9cc8d097bd4d7c1ab
F test/quote.test 215897dbe8de1a6f701265836d6601cc6ed103e6 F test/quote.test 215897dbe8de1a6f701265836d6601cc6ed103e6
F test/randexpr1.tcl 40dec52119ed3a2b8b2a773bce24b63a3a746459 F test/randexpr1.tcl 40dec52119ed3a2b8b2a773bce24b63a3a746459
F test/randexpr1.test 1084050991e9ba22c1c10edd8d84673b501cc25a F test/randexpr1.test eda062a97e60f9c38ae8d806b03b0ddf23d796df
F test/rdonly.test c267d050a1d9a6a321de502b737daf28821a518d F test/rdonly.test c267d050a1d9a6a321de502b737daf28821a518d
F test/reindex.test 44edd3966b474468b823d481eafef0c305022254 F test/reindex.test 44edd3966b474468b823d481eafef0c305022254
F test/releasetest.mk 2eced2f9ae701fd0a29e714a241760503ccba25a F test/releasetest.mk 2eced2f9ae701fd0a29e714a241760503ccba25a
@ -652,38 +656,45 @@ F test/rollback.test a1b4784b864331eae8b2a98c189efa2a8b11ff07
F test/rowhash.test 0bc1d31415e4575d10cacf31e1a66b5cc0f8be81 F test/rowhash.test 0bc1d31415e4575d10cacf31e1a66b5cc0f8be81
F test/rowid.test e58e0acef38b527ed1b0b70d3ada588f804af287 F test/rowid.test e58e0acef38b527ed1b0b70d3ada588f804af287
F test/rtree.test 0c8d9dd458d6824e59683c19ab2ffa9ef946f798 F test/rtree.test 0c8d9dd458d6824e59683c19ab2ffa9ef946f798
F test/savepoint.test e575217b07d6a6e895e66f4eda076570815e0027 F test/savepoint.test f5acd87d0c7a5f4ad6c547b47fd18c0e1aeaf048
F test/savepoint2.test 9b8543940572a2f01a18298c3135ad0c9f4f67d7 F test/savepoint2.test 9b8543940572a2f01a18298c3135ad0c9f4f67d7
F test/savepoint3.test e328085853b14898d78ceea00dfe7db18bb6a9ec F test/savepoint3.test e328085853b14898d78ceea00dfe7db18bb6a9ec
F test/savepoint4.test c8f8159ade6d2acd9128be61e1230f1c1edc6cc0 F test/savepoint4.test c8f8159ade6d2acd9128be61e1230f1c1edc6cc0
F test/savepoint5.test 0735db177e0ebbaedc39812c8d065075d563c4fd F test/savepoint5.test 0735db177e0ebbaedc39812c8d065075d563c4fd
F test/savepoint6.test f41279c5e137139fa5c21485773332c7adb98cd7 F test/savepoint6.test f41279c5e137139fa5c21485773332c7adb98cd7
F test/savepoint7.test fbf319a7b2dda089ec5be30a424a0e95f121d423
F test/schema.test 8f7999be894260f151adf15c2c7540f1c6d6a481 F test/schema.test 8f7999be894260f151adf15c2c7540f1c6d6a481
F test/schema2.test 906408621ea881fdb496d878b1822572a34e32c5 F test/schema2.test 906408621ea881fdb496d878b1822572a34e32c5
F test/schema3.test 1bc1008e1f8cb5654b248c55f27249366eb7ed38 F test/schema3.test 1bc1008e1f8cb5654b248c55f27249366eb7ed38
F test/schema4.test e6a66e20cc69f0e306667c08be7fda3d11707dc5 F test/schema4.test e6a66e20cc69f0e306667c08be7fda3d11707dc5
F test/schema5.test 0103e4c0313b3725b5ae5600bdca53006ab53db3
F test/securedel.test 87a2561151af1f1e349071a89fdd77059f50113c F test/securedel.test 87a2561151af1f1e349071a89fdd77059f50113c
F test/select1.test f67ca2dfc05df41c7b86eb32ca409b427a5f43b0 F test/select1.test deba017eed9daa5af33de868676c997e7eebb931
F test/select2.test 352480e0e9c66eda9c3044e412abdf5be0215b56 F test/select2.test 352480e0e9c66eda9c3044e412abdf5be0215b56
F test/select3.test 2ce595f8fb8e2ac10071d3b4e424cadd4634a054 F test/select3.test 2ce595f8fb8e2ac10071d3b4e424cadd4634a054
F test/select4.test 44aa6e7110592e18110b0b9cf5c024d37d23be17 F test/select4.test 00179be44e531fe04c1c3f15df216439dff2519d
F test/select5.test e758b8ef94f69b111df4cb819008856655dcd535 F test/select5.test e758b8ef94f69b111df4cb819008856655dcd535
F test/select6.test cc25a8650cf9a4d4f74e586c45a75f9836516b18 F test/select6.test cc25a8650cf9a4d4f74e586c45a75f9836516b18
F test/select7.test dad6f00f0d49728a879d6eb6451d4752db0b0abe F test/select7.test dad6f00f0d49728a879d6eb6451d4752db0b0abe
F test/select8.test 391de11bdd52339c30580dabbbbe97e3e9a3c79d F test/select8.test 391de11bdd52339c30580dabbbbe97e3e9a3c79d
F test/select9.test 74c0fb2c6eecb0219cbed0cbe3df136f8fbf9343 F test/select9.test c0ca3cd87a8ebb04de2cb1402c77df55d911a0ea
F test/selectA.test 06d1032fa9009314c95394f2ca2e60d9f7ae8532 F test/selectA.test 06d1032fa9009314c95394f2ca2e60d9f7ae8532
F test/selectB.test 954e4e49cf1f896d61794e440669e03a27ceea25 F test/selectB.test 954e4e49cf1f896d61794e440669e03a27ceea25
F test/selectC.test f9bf1bc4581b5b8158caa6e4e4f682acb379fb25 F test/selectC.test 871fb55d884d3de5943c4057ebd22c2459e71977
F test/server1.test 46803bd3fe8b99b30dbc5ff38ffc756f5c13a118 F test/server1.test 46803bd3fe8b99b30dbc5ff38ffc756f5c13a118
F test/shared.test 34945a516532b11182c3eb26e31247eee3c9ae48 F test/shared.test 34945a516532b11182c3eb26e31247eee3c9ae48
F test/shared2.test 8f71d4eb4d5261280de92284df74172545c852cc F test/shared2.test 03eb4a8d372e290107d34b6ce1809919a698e879
F test/shared3.test ebf77f023f4bdaa8f74f65822b559e86ce5c6257 F test/shared3.test ebf77f023f4bdaa8f74f65822b559e86ce5c6257
F test/shared4.test 72d90821e8d2fc918a08f16d32880868d8ee8e9d F test/shared4.test 72d90821e8d2fc918a08f16d32880868d8ee8e9d
F test/shared6.test 866bb4982c45ce216c61ded5e8fde4e7e2f3ffa9 F test/shared6.test 866bb4982c45ce216c61ded5e8fde4e7e2f3ffa9
F test/shared7.test 960760bc8d03e1419e70dea69cf41db62853616e F test/shared7.test 960760bc8d03e1419e70dea69cf41db62853616e
F test/shared_err.test 91e26ec4f3fbe07951967955585137e2f18993de F test/shared_err.test 91e26ec4f3fbe07951967955585137e2f18993de
F test/sharedlock.test ffa0a3c4ac192145b310f1254f8afca4d553eabf F test/sharedlock.test ffa0a3c4ac192145b310f1254f8afca4d553eabf
F test/shell1.test cd9f846702d1d471225a988fee590a153be8192c
F test/shell2.test 037d6ad16e873354195d30bb2dc4b5321788154a
F test/shell3.test 9196c42772d575685e722c92b4b39053c6ebba59
F test/shell4.test aa4eef8118b412d1a01477a53426ece169ea86a9
F test/shell5.test fa2188bbb13fe2d183fd04a5f7b512650c35ef5d
F test/shortread1.test bb591ef20f0fd9ed26d0d12e80eee6d7ac8897a3 F test/shortread1.test bb591ef20f0fd9ed26d0d12e80eee6d7ac8897a3
F test/shrink.test 8c70f62b6e8eb4d54533de6d65bd06b1b9a17868 F test/shrink.test 8c70f62b6e8eb4d54533de6d65bd06b1b9a17868
F test/sidedelete.test f0ad71abe6233e3b153100f3b8d679b19a488329 F test/sidedelete.test f0ad71abe6233e3b153100f3b8d679b19a488329
@ -699,15 +710,15 @@ F test/speed4.test abc0ad3399dcf9703abed2fff8705e4f8e416715
F test/speed4p.explain 6b5f104ebeb34a038b2f714150f51d01143e59aa F test/speed4p.explain 6b5f104ebeb34a038b2f714150f51d01143e59aa
F test/speed4p.test 0e51908951677de5a969b723e03a27a1c45db38b F test/speed4p.test 0e51908951677de5a969b723e03a27a1c45db38b
F test/sqllimits1.test b1aae27cc98eceb845e7f7adf918561256e31298 F test/sqllimits1.test b1aae27cc98eceb845e7f7adf918561256e31298
F test/stat.test 36bc951bdc2beac4224cc54396fd6a7dc65336f4 F test/stat.test 08e8185b3fd5b010c90d7ad82b9dd4ea1cbf14b0
F test/stmt.test 25d64e3dbf9a3ce89558667d7f39d966fe2a71b9 F test/stmt.test 25d64e3dbf9a3ce89558667d7f39d966fe2a71b9
F test/subquery.test b524f57c9574b2c0347045b4510ef795d4686796 F test/subquery.test c5e0d183f1ae6251453338a465b32ae11326e0fa
F test/subquery2.test edcad5c118f0531c2e21bf16a09bbb105252d4cd F test/subquery2.test edcad5c118f0531c2e21bf16a09bbb105252d4cd
F test/subselect.test d24fd8757daf97dafd2e889c73ea4c4272dcf4e4 F test/subselect.test d24fd8757daf97dafd2e889c73ea4c4272dcf4e4
F test/substr.test 18f57c4ca8a598805c4d64e304c418734d843c1a F test/substr.test 18f57c4ca8a598805c4d64e304c418734d843c1a
F test/superlock.test 1cde669f68d2dd37d6c9bd35eee1d95491ae3fc2 F test/superlock.test 1cde669f68d2dd37d6c9bd35eee1d95491ae3fc2
F test/sync.test a34cd43e98b7fb84eabbf38f7ed8f7349b3f3d85 F test/sync.test a34cd43e98b7fb84eabbf38f7ed8f7349b3f3d85
F test/syscall.test 265cda616f56a297406728ee1e74c9b4a93aa6dd F test/syscall.test bea9bf329bff733c791310244617c2a76974e64a
F test/sysfault.test c79441d88d23696fbec7b147dba98d42a04f523f F test/sysfault.test c79441d88d23696fbec7b147dba98d42a04f523f
F test/table.test a59d985ca366e39b17b175f387f9d5db5a18d4e2 F test/table.test a59d985ca366e39b17b175f387f9d5db5a18d4e2
F test/tableapi.test 2674633fa95d80da917571ebdd759a14d9819126 F test/tableapi.test 2674633fa95d80da917571ebdd759a14d9819126
@ -715,7 +726,7 @@ F test/tclsqlite.test 1597d353308531527583481d14d9da52ea8ed0af
F test/tempdb.test 19d0f66e2e3eeffd68661a11c83ba5e6ace9128c F test/tempdb.test 19d0f66e2e3eeffd68661a11c83ba5e6ace9128c
F test/temptable.test 51edd31c65ed1560dd600b1796e8325df96318e2 F test/temptable.test 51edd31c65ed1560dd600b1796e8325df96318e2
F test/temptrigger.test 26670ed7a39cf2296a7f0a9e0a1d7bdb7abe936d F test/temptrigger.test 26670ed7a39cf2296a7f0a9e0a1d7bdb7abe936d
F test/tester.tcl 001051eaf28c1040800f588a64c63e0bd0e1f36b F test/tester.tcl a55e066251bc05e26f9da00e76644ab649b26f3c
F test/thread001.test 7cc2ce08f9cde95964736d11e91f9ab610f82f91 F test/thread001.test 7cc2ce08f9cde95964736d11e91f9ab610f82f91
F test/thread002.test e630504f8a06c00bf8bbe68528774dd96aeb2e58 F test/thread002.test e630504f8a06c00bf8bbe68528774dd96aeb2e58
F test/thread003.test ee4c9efc3b86a6a2767516a37bd64251272560a7 F test/thread003.test ee4c9efc3b86a6a2767516a37bd64251272560a7
@ -727,15 +738,17 @@ F test/thread_common.tcl 334639cadcb9f912bf82aa73f49efd5282e6cadd
F test/threadtest1.c 6029d9c5567db28e6dc908a0c63099c3ba6c383b F test/threadtest1.c 6029d9c5567db28e6dc908a0c63099c3ba6c383b
F test/threadtest2.c ace893054fa134af3fc8d6e7cfecddb8e3acefb9 F test/threadtest2.c ace893054fa134af3fc8d6e7cfecddb8e3acefb9
F test/threadtest3.c 0ed13e09690f6204d7455fac3b0e8ece490f6eef F test/threadtest3.c 0ed13e09690f6204d7455fac3b0e8ece490f6eef
F test/tkt-02a8e81d44.test 58494de77be2cf249228ada3f313fa399821c6ab F test/tkt-02a8e81d44.test 6c80d9c7514e2a42d4918bf87bf6bc54f379110c
F test/tkt-26ff0c2d1e.test 888324e751512972c6e0d1a09df740d8f5aaf660 F test/tkt-26ff0c2d1e.test 888324e751512972c6e0d1a09df740d8f5aaf660
F test/tkt-2a5629202f.test 1ab32e084e9fc3d36be6dee2617530846a0eb0b6
F test/tkt-2d1a5c67d.test b028a811049eb472cb2d3a43fc8ce4f6894eebda F test/tkt-2d1a5c67d.test b028a811049eb472cb2d3a43fc8ce4f6894eebda
F test/tkt-2ea2425d34.test 1cf13e6f75d149b3209a0cb32927a82d3d79fb28 F test/tkt-2ea2425d34.test 1cf13e6f75d149b3209a0cb32927a82d3d79fb28
F test/tkt-31338dca7e.test 1f714c14b6682c5db715e0bda347926a3456f7a9 F test/tkt-31338dca7e.test 1f714c14b6682c5db715e0bda347926a3456f7a9
F test/tkt-313723c356.test c47f8a9330523e6f35698bf4489bcb29609b53ac F test/tkt-313723c356.test c47f8a9330523e6f35698bf4489bcb29609b53ac
F test/tkt-38cb5df375.test 9e9b19857dba0896a8efdaf334d405ba423492f2 F test/tkt-385a5b56b9.test 8eb87c4bbcc3fd4f33d73719de7e9d64973fa196
F test/tkt-38cb5df375.test f3cc8671f1eb604d4ae9cf886ed4366bec656678
F test/tkt-3998683a16.test 6d1d04d551ed1704eb3396ca87bb9ccc8c5c1eb7 F test/tkt-3998683a16.test 6d1d04d551ed1704eb3396ca87bb9ccc8c5c1eb7
F test/tkt-3a77c9714e.test 1675c22a5be71d7fa026e5db5daeeb4dd64f7824 F test/tkt-3a77c9714e.test 32bb28afa8c63fc76e972e996193139b63551ed9
F test/tkt-3fe897352e.test 10de1a67bd5c66b238a4c96abe55531b37bb4f00 F test/tkt-3fe897352e.test 10de1a67bd5c66b238a4c96abe55531b37bb4f00
F test/tkt-4a03edc4c8.test 2865e4edbc075b954daa82f8da7cc973033ec76e F test/tkt-4a03edc4c8.test 2865e4edbc075b954daa82f8da7cc973033ec76e
F test/tkt-54844eea3f.test a12b851128f46a695e4e378cca67409b9b8f5894 F test/tkt-54844eea3f.test a12b851128f46a695e4e378cca67409b9b8f5894
@ -744,24 +757,25 @@ F test/tkt-5e10420e8d.test 904d1687b3c06d43e5b3555bbcf6802e7c0ffd84
F test/tkt-5ee23731f.test 9db6e1d7209dc0794948b260d6f82b2b1de83a9f F test/tkt-5ee23731f.test 9db6e1d7209dc0794948b260d6f82b2b1de83a9f
F test/tkt-752e1646fc.test ea78d88d14fe9866bdd991c634483334639e13bf F test/tkt-752e1646fc.test ea78d88d14fe9866bdd991c634483334639e13bf
F test/tkt-78e04e52ea.test ab52f0c1e2de6e46c910f4cc16b086bba05952b7 F test/tkt-78e04e52ea.test ab52f0c1e2de6e46c910f4cc16b086bba05952b7
F test/tkt-7bbfb7d442.test 8e7658f77d1ccea9d88dc9e255d3ed7fb68f8bdf F test/tkt-7bbfb7d442.test dfa5c8097a8c353ae40705d6cddeb1f99c18b81a
F test/tkt-80ba201079.test a09684db1a0bd55b8838f606adccee456a51ddbf F test/tkt-80ba201079.test 9eb040d81c404f56838a6af93593f42790def63f
F test/tkt-80e031a00f.test 9a154173461a4dbe2de49cda73963e04842d52f7 F test/tkt-80e031a00f.test 9a154173461a4dbe2de49cda73963e04842d52f7
F test/tkt-8454a207b9.test c583a9f814a82a2b5ba95207f55001c9f0cd816c F test/tkt-8454a207b9.test c583a9f814a82a2b5ba95207f55001c9f0cd816c
F test/tkt-91e2e8ba6f.test 08c4f94ae07696b05c9b822da0b4e5337a2f54c5 F test/tkt-91e2e8ba6f.test 08c4f94ae07696b05c9b822da0b4e5337a2f54c5
F test/tkt-94c04eaadb.test be5ea61cb04dfdc047d19b5c5a9e75fa3da67a7f F test/tkt-94c04eaadb.test fa9c71192f7e2ea2d51bf078bc34e8da6088bf71
F test/tkt-9d68c883.test 458f7d82a523d7644b54b497c986378a7d8c8b67 F test/tkt-9d68c883.test 458f7d82a523d7644b54b497c986378a7d8c8b67
F test/tkt-b1d3a2e531.test 610ef582413171b379652663111b1f996d9f8f78 F test/tkt-b1d3a2e531.test 610ef582413171b379652663111b1f996d9f8f78
F test/tkt-b351d95f9.test d14a503c414c5c58fdde3e80f9a3cfef986498c0 F test/tkt-b351d95f9.test d14a503c414c5c58fdde3e80f9a3cfef986498c0
F test/tkt-b72787b1.test e6b62b2b2785c04d0d698d6a603507e384165049 F test/tkt-b72787b1.test a95e8cdad0b98af1853ac7f0afd4ab27b77bf5f3
F test/tkt-bd484a090c.test 60460bf946f79a79712b71f202eda501ca99b898 F test/tkt-bd484a090c.test 60460bf946f79a79712b71f202eda501ca99b898
F test/tkt-bdc6bbbb38.test fc38bb09bdd440e3513a1f5f98fc60a075182d7d
F test/tkt-c48d99d690.test bed446e3513ae10eec1b86fdd186ef750226c408 F test/tkt-c48d99d690.test bed446e3513ae10eec1b86fdd186ef750226c408
F test/tkt-cbd054fa6b.test bd9fb546f63bc0c79d1776978d059fa51c5b1c63 F test/tkt-cbd054fa6b.test bd9fb546f63bc0c79d1776978d059fa51c5b1c63
F test/tkt-d11f09d36e.test fb44f7961aa6d4b632fb7b9768239832210b5fc7 F test/tkt-d11f09d36e.test fb44f7961aa6d4b632fb7b9768239832210b5fc7
F test/tkt-d635236375.test 9d37e988b47d87505bc9445be0ca447002df5d09 F test/tkt-d635236375.test 9d37e988b47d87505bc9445be0ca447002df5d09
F test/tkt-d82e3f3721.test 731359dfdcdb36fea0559cd33fec39dd0ceae8e6 F test/tkt-d82e3f3721.test bcc0dfba658d15bab30fd4a9320c9e35d214ce30
F test/tkt-f3e5abed55.test 669bb076f2ac573c7398ce00f40cd0ca502043a9 F test/tkt-f3e5abed55.test 669bb076f2ac573c7398ce00f40cd0ca502043a9
F test/tkt-f777251dc7a.test 6f24c053bc5cdb7e1e19be9a72c8887cf41d5e87 F test/tkt-f777251dc7a.test af6531446c64bfd268416f07b4df7be7f9c749d2
F test/tkt-f7b4edec.test d998a08ff2b18b7f62edce8e3044317c45efe6c7 F test/tkt-f7b4edec.test d998a08ff2b18b7f62edce8e3044317c45efe6c7
F test/tkt-f973c7ac31.test 1da0ed15ec2c7749fb5ce2828cd69d07153ad9f4 F test/tkt-f973c7ac31.test 1da0ed15ec2c7749fb5ce2828cd69d07153ad9f4
F test/tkt-fa7bf5ec.test 9102dfea58aa371d78969da735f9392c57e2e035 F test/tkt-fa7bf5ec.test 9102dfea58aa371d78969da735f9392c57e2e035
@ -820,7 +834,7 @@ F test/tkt3461.test 228ea328a5a21e8663f80ee3d212a6ad92549a19
F test/tkt3493.test 1686cbde85f8721fc1bdc0ee72f2ef2f63139218 F test/tkt3493.test 1686cbde85f8721fc1bdc0ee72f2ef2f63139218
F test/tkt3508.test d75704db9501625c7f7deec119fcaf1696aefb7d F test/tkt3508.test d75704db9501625c7f7deec119fcaf1696aefb7d
F test/tkt3522.test 22ce2ebbcb04a6be56c0977d405c207967318fd6 F test/tkt3522.test 22ce2ebbcb04a6be56c0977d405c207967318fd6
F test/tkt3527.test ee4af96183579565987e58873a7490bc04934ffb F test/tkt3527.test 9e8f28a706c772d5a7cd1020c946fab6c74e3ae0
F test/tkt3541.test 5dc257bde9bc833ab9cc6844bf170b998dbb950a F test/tkt3541.test 5dc257bde9bc833ab9cc6844bf170b998dbb950a
F test/tkt3554.test f599967f279077bace39220cbe76085c7b423725 F test/tkt3554.test f599967f279077bace39220cbe76085c7b423725
F test/tkt3581.test 1966b7193f1e3f14951cce8c66907ae69454e9a3 F test/tkt3581.test 1966b7193f1e3f14951cce8c66907ae69454e9a3
@ -831,13 +845,13 @@ F test/tkt3731.test 0c5f4cbffe102d43c3b2188af91a9e36348f974b
F test/tkt3757.test 10cd679a88675c880533083fc79ac04324525595 F test/tkt3757.test 10cd679a88675c880533083fc79ac04324525595
F test/tkt3761.test b95ea9c98f21cf91325f18a984887e62caceab33 F test/tkt3761.test b95ea9c98f21cf91325f18a984887e62caceab33
F test/tkt3762.test 2a9f3b03df44ec49ec0cfa8d5da6574c2a7853df F test/tkt3762.test 2a9f3b03df44ec49ec0cfa8d5da6574c2a7853df
F test/tkt3773.test 430b06567ce40285dfd2c4834a2a61816403efeb F test/tkt3773.test 7bca904d2a647a6a4a291bd86d7fd7c73855b789
F test/tkt3791.test a6624b9a80b216a26cf473607f42f3e51898c267 F test/tkt3791.test a6624b9a80b216a26cf473607f42f3e51898c267
F test/tkt3793.test d90ffd75c52413908d15e1c44fc2ea9c80fcc449 F test/tkt3793.test d90ffd75c52413908d15e1c44fc2ea9c80fcc449
F test/tkt3810.test 90fa0635dfa7da9680c8cd3513350a49b3a8ae12 F test/tkt3810.test 90fa0635dfa7da9680c8cd3513350a49b3a8ae12
F test/tkt3824.test 150aa00bb6220672e5f0eb14dc8eaa36750425f0 F test/tkt3824.test 150aa00bb6220672e5f0eb14dc8eaa36750425f0
F test/tkt3832.test 2300d10d57562b89875b72148338ac3e14f8847d F test/tkt3832.test 2300d10d57562b89875b72148338ac3e14f8847d
F test/tkt3838.test d8490365a1c473d214f7878007e543410cbb715f F test/tkt3838.test 292e72489101cd1320d7278dc111c173ebf334d4
F test/tkt3841.test 4659845bc53f809a5932c61c6ce8c5bb9d6b947f F test/tkt3841.test 4659845bc53f809a5932c61c6ce8c5bb9d6b947f
F test/tkt3871.test 43ecbc8d90dc83908e2a454aef345acc9d160c6f F test/tkt3871.test 43ecbc8d90dc83908e2a454aef345acc9d160c6f
F test/tkt3879.test 2ad5bef2c87e9991ce941e054c31abe26ef7fb90 F test/tkt3879.test 2ad5bef2c87e9991ce941e054c31abe26ef7fb90
@ -851,11 +865,11 @@ F test/tkt3997.test a335fa41ca3985660a139df7b734a26ef53284bd
F test/tkt4018.test 7c2c9ba4df489c676a0a7a0e809a1fb9b2185bd1 F test/tkt4018.test 7c2c9ba4df489c676a0a7a0e809a1fb9b2185bd1
F test/tokenize.test ce430a7aed48fc98301611429595883fdfcab5d7 F test/tokenize.test ce430a7aed48fc98301611429595883fdfcab5d7
F test/trace.test 4b36a41a3e9c7842151af6da5998f5080cdad9e5 F test/trace.test 4b36a41a3e9c7842151af6da5998f5080cdad9e5
F test/trace2.test 962175290996d5f06dc4402ca218bbfc7df4cb20 F test/trace2.test c1dc104a8d11a347c870cfea6235e3fc6f6cb06d
F test/trans.test 6e1b4c6a42dba31bd65f8fa5e61a2708e08ddde6 F test/trans.test 6e1b4c6a42dba31bd65f8fa5e61a2708e08ddde6
F test/trans2.test d5337e61de45e66b1fcbf9db833fa8c82e624b22 F test/trans2.test d5337e61de45e66b1fcbf9db833fa8c82e624b22
F test/trans3.test d728abaa318ca364dc370e06576aa7e5fbed7e97 F test/trans3.test 373ac5183cc56be69f48ae44090e7f672939f732
F test/trigger1.test 38c657eaf9907344c9e0bcb16af94a452c6babde F test/trigger1.test de42feb7cd442787d38185ae74f5a1d7afa400cb
F test/trigger2.test 834187beafd1db383af0c659cfa49b0576832816 F test/trigger2.test 834187beafd1db383af0c659cfa49b0576832816
F test/trigger3.test d2c60d8be271c355d61727411e753181e877230a F test/trigger3.test d2c60d8be271c355d61727411e753181e877230a
F test/trigger4.test 74700b76ebf3947b2f7a92405141eb2cf2a5d359 F test/trigger4.test 74700b76ebf3947b2f7a92405141eb2cf2a5d359
@ -876,7 +890,7 @@ F test/unique.test 083c7fff74695bcc27a71d75699deba3595bc9c2
F test/unixexcl.test a9870e46cc6f8390a494513d4f2bf55b5a8b3e46 F test/unixexcl.test a9870e46cc6f8390a494513d4f2bf55b5a8b3e46
F test/unordered.test f53095cee37851bf30130fa1bf299a8845e837bb F test/unordered.test f53095cee37851bf30130fa1bf299a8845e837bb
F test/update.test 8bc86fd7ef1a00014f76dc6a6a7c974df4aef172 F test/update.test 8bc86fd7ef1a00014f76dc6a6a7c974df4aef172
F test/uri.test 0d289d32396bdbc491e9dc845f1a52e13f861e0b F test/uri.test 78e869db1ff6331157b08ef089b1b3e65819c74c
F test/utf16align.test 54cd35a27c005a9b6e7815d887718780b6a462ae F test/utf16align.test 54cd35a27c005a9b6e7815d887718780b6a462ae
F test/vacuum.test ce91c39f7f91a4273bf620efad21086b5aa6ef1d F test/vacuum.test ce91c39f7f91a4273bf620efad21086b5aa6ef1d
F test/vacuum2.test af432e6e3bfc0ea20a80cb86a03c7d9876d38324 F test/vacuum2.test af432e6e3bfc0ea20a80cb86a03c7d9876d38324
@ -885,7 +899,7 @@ F test/vacuum4.test d3f8ecff345f166911568f397d2432c16d2867d9
F test/varint.test ab7b110089a08b9926ed7390e7e97bdefeb74102 F test/varint.test ab7b110089a08b9926ed7390e7e97bdefeb74102
F test/veryquick.test 7701bb609fe8bf6535514e8b849a309e8f00573b F test/veryquick.test 7701bb609fe8bf6535514e8b849a309e8f00573b
F test/view.test b182a67ec43f490b156b5a710827a341be83dd17 F test/view.test b182a67ec43f490b156b5a710827a341be83dd17
F test/vtab1.test 12fbb309ce830c4064e44f275eb02a65c2780076 F test/vtab1.test e429a6835faa3870016c55d1178dcfead85f936a
F test/vtab2.test 7bcffc050da5c68f4f312e49e443063e2d391c0d F test/vtab2.test 7bcffc050da5c68f4f312e49e443063e2d391c0d
F test/vtab3.test baad99fd27217f5d6db10660522e0b7192446de1 F test/vtab3.test baad99fd27217f5d6db10660522e0b7192446de1
F test/vtab4.test 942f8b8280b3ea8a41dae20e7822d065ca1cb275 F test/vtab4.test 942f8b8280b3ea8a41dae20e7822d065ca1cb275
@ -897,22 +911,23 @@ F test/vtab9.test ea58d2b95d61955f87226381716b2d0b1d4e4f9b
F test/vtabA.test c86e1990b7e1e2bb34602a06fffa4c69f2b516dc F test/vtabA.test c86e1990b7e1e2bb34602a06fffa4c69f2b516dc
F test/vtabB.test 04df5dc531b9f44d9ca65b9c1b79f12b5922a796 F test/vtabB.test 04df5dc531b9f44d9ca65b9c1b79f12b5922a796
F test/vtabC.test 4528f459a13136f982e75614d120aef165f17292 F test/vtabC.test 4528f459a13136f982e75614d120aef165f17292
F test/vtabD.test 74167b1578e5886fe4c886d6bef2fd1406444c42 F test/vtabD.test 05b3f1d77117271671089e48719524b676842e96
F test/vtabE.test 7c4693638d7797ce2eda17af74292b97e705cc61 F test/vtabE.test 7c4693638d7797ce2eda17af74292b97e705cc61
F test/vtabF.test fd5ad376f5a34fe0891df1f3cddb4fe7c3eb077e F test/vtabF.test fd5ad376f5a34fe0891df1f3cddb4fe7c3eb077e
F test/vtab_alter.test 9e374885248f69e251bdaacf480b04a197f125e5 F test/vtab_alter.test 9e374885248f69e251bdaacf480b04a197f125e5
F test/vtab_err.test 0d4d8eb4def1d053ac7c5050df3024fd47a3fbd8 F test/vtab_err.test 0d4d8eb4def1d053ac7c5050df3024fd47a3fbd8
F test/vtab_shared.test 0eff9ce4f19facbe0a3e693f6c14b80711a4222d F test/vtab_shared.test 82f463886e18d7f8395a4b6167c91815efe54839
F test/wal.test edefe316b4125d7f68004ea53c5e73c398d436cc F test/wal.test b3d28d655371bf3f6500c679f526e9860544fe70
F test/wal2.test f11883dd3cb7f647c5d2acfd7b5c6d4ba5770cc9 F test/wal2.test d5021064bebfc717fe2bf4db2536ea030b76a773
F test/wal3.test 6504bbf348b2d6dfade64a064f1050fd617e8706 F test/wal3.test 6504bbf348b2d6dfade64a064f1050fd617e8706
F test/wal4.test 4744e155cd6299c6bd99d3eab1c82f77db9cdb3c F test/wal4.test 4744e155cd6299c6bd99d3eab1c82f77db9cdb3c
F test/wal5.test f58ed4b8b542f71c7441da12fbd769d99b362437 F test/wal5.test f58ed4b8b542f71c7441da12fbd769d99b362437
F test/wal6.test 2e3bc767d9c2ce35c47106148d43fcbd072a93b3 F test/wal6.test 2e3bc767d9c2ce35c47106148d43fcbd072a93b3
F test/wal7.test 2ae8f427d240099cc4b2dfef63cff44e2a68a1bd F test/wal7.test 2ae8f427d240099cc4b2dfef63cff44e2a68a1bd
F test/wal8.test 5ab217d21f7e5e86af2933a4ffd0d8357cc2c0bd
F test/wal_common.tcl a98f17fba96206122eff624db0ab13ec377be4fe F test/wal_common.tcl a98f17fba96206122eff624db0ab13ec377be4fe
F test/walbak.test b9f68e39646375c2b877be906babcc15d38b4877 F test/walbak.test b9f68e39646375c2b877be906babcc15d38b4877
F test/walbig.test 0ab8a430ef420a3114f7092e0f30fc9585ffa155 F test/walbig.test f437473a16cfb314867c6b5d1dbcd519e73e3434
F test/walcksum.test f5447800a157c9e2234fbb8e80243f0813941bde F test/walcksum.test f5447800a157c9e2234fbb8e80243f0813941bde
F test/walcrash.test 4457436593be8c136f9148487c7dccd5e9013af2 F test/walcrash.test 4457436593be8c136f9148487c7dccd5e9013af2
F test/walcrash2.test 019d60b89d96c1937adb2b30b850ac7e86e5a142 F test/walcrash2.test 019d60b89d96c1937adb2b30b850ac7e86e5a142
@ -926,16 +941,16 @@ F test/walro.test e6bb27762c9f22601cbb8bff6e0acfd124e74b63
F test/walshared.test 6dda2293880c300baf5d791c307f653094585761 F test/walshared.test 6dda2293880c300baf5d791c307f653094585761
F test/walslow.test e7be6d9888f83aa5d3d3c7c08aa9b5c28b93609a F test/walslow.test e7be6d9888f83aa5d3d3c7c08aa9b5c28b93609a
F test/walthread.test a2ed5270eb695284d4ad27d252517bdc3317ee2a F test/walthread.test a2ed5270eb695284d4ad27d252517bdc3317ee2a
F test/where.test de337a3fe0a459ec7c93db16a519657a90552330 F test/where.test 4c9f69987ed2aa0173fa930f2b41ab9879478cd8
F test/where2.test 43d4becaf5a5df854e6c21d624a1cb84c6904554 F test/where2.test 43d4becaf5a5df854e6c21d624a1cb84c6904554
F test/where3.test 667e75642102c97a00bf9b23d3cb267db321d006 F test/where3.test 667e75642102c97a00bf9b23d3cb267db321d006
F test/where4.test e9b9e2f2f98f00379e6031db6a6fca29bae782a2 F test/where4.test e9b9e2f2f98f00379e6031db6a6fca29bae782a2
F test/where5.test fdf66f96d29a064b63eb543e28da4dfdccd81ad2 F test/where5.test fdf66f96d29a064b63eb543e28da4dfdccd81ad2
F test/where6.test 5da5a98cec820d488e82708301b96cb8c18a258b F test/where6.test 5da5a98cec820d488e82708301b96cb8c18a258b
F test/where7.test 814d7373413398e074f134cff5f8872e2c08bd3b F test/where7.test 5c566388f0cc318b0032ce860f4ac5548e3c265a
F test/where8.test a6c740fd286d7883e274e17b6230a9d672a7ab1f F test/where8.test a6c740fd286d7883e274e17b6230a9d672a7ab1f
F test/where8m.test da346596e19d54f0aba35ebade032a7c47d79739 F test/where8m.test da346596e19d54f0aba35ebade032a7c47d79739
F test/where9.test bed66dcfc69a54a99661c0c9906189cb5e58f4e2 F test/where9.test ae98dc22ef9b6f2bc81e9f164e41b38faa9bda06
F test/whereA.test 24c234263c8fe358f079d5e57d884fb569d2da0a F test/whereA.test 24c234263c8fe358f079d5e57d884fb569d2da0a
F test/whereB.test 0def95db3bdec220a731c7e4bec5930327c1d8c5 F test/whereB.test 0def95db3bdec220a731c7e4bec5930327c1d8c5
F test/whereC.test 13ff5ec0dba407c0e0c075980c75b3275a6774e5 F test/whereC.test 13ff5ec0dba407c0e0c075980c75b3275a6774e5
@ -943,16 +958,16 @@ F test/wherelimit.test 5e9fd41e79bb2b2d588ed999d641d9c965619b31
F test/win32lock.test b2a539e85ae6b2d78475e016a9636b4451dc7fb9 F test/win32lock.test b2a539e85ae6b2d78475e016a9636b4451dc7fb9
F test/zeroblob.test caaecfb4f908f7bc086ed238668049f96774d688 F test/zeroblob.test caaecfb4f908f7bc086ed238668049f96774d688
F test/zerodamage.test 0de750389990b1078bab203c712dc3fefd1d8b82 F test/zerodamage.test 0de750389990b1078bab203c712dc3fefd1d8b82
F tool/build-shell.sh 12aa4391073a777fcb6dcc490b219a018ae98bac F tool/build-shell.sh b64a481901fc9ffe5ca8812a2a9255b6cfb77381
F tool/diffdb.c 7524b1b5df217c20cd0431f6789851a4e0cb191b F tool/diffdb.c 7524b1b5df217c20cd0431f6789851a4e0cb191b
F tool/extract.c 054069d81b095fbdc189a6f5d4466e40380505e2 F tool/extract.c 054069d81b095fbdc189a6f5d4466e40380505e2
F tool/fragck.tcl 5265a95126abcf6ab357f7efa544787e5963f439 F tool/fragck.tcl 5265a95126abcf6ab357f7efa544787e5963f439
F tool/genfkey.README cf68fddd4643bbe3ff8e31b8b6d8b0a1b85e20f4 F tool/genfkey.README cf68fddd4643bbe3ff8e31b8b6d8b0a1b85e20f4
F tool/genfkey.test 4196a8928b78f51d54ef58e99e99401ab2f0a7e5 F tool/genfkey.test 4196a8928b78f51d54ef58e99e99401ab2f0a7e5
F tool/getlock.c f4c39b651370156cae979501a7b156bdba50e7ce F tool/getlock.c f4c39b651370156cae979501a7b156bdba50e7ce
F tool/lemon.c 445f18999b700d83b83a5d9be00c596546c21052 F tool/lemon.c 90f46af31c92b940fec25b491f39409fd95dcdfa
F tool/lempar.c 01ca97f87610d1dac6d8cd96ab109ab1130e76dc F tool/lempar.c 01ca97f87610d1dac6d8cd96ab109ab1130e76dc
F tool/mkkeywordhash.c d2e6b4a5965e23afb80fbe74bb54648cd371f309 F tool/mkkeywordhash.c bb52064aa614e1426445e4b2b9b00eeecd23cc79
F tool/mkopts.tcl 66ac10d240cc6e86abd37dc908d50382f84ff46e F tool/mkopts.tcl 66ac10d240cc6e86abd37dc908d50382f84ff46e
F tool/mkspeedsql.tcl a1a334d288f7adfe6e996f2e712becf076745c97 F tool/mkspeedsql.tcl a1a334d288f7adfe6e996f2e712becf076745c97
F tool/mksqlite3c-noext.tcl 105023aa86f696a74b1d6a4929d1e1c3baf9471c F tool/mksqlite3c-noext.tcl 105023aa86f696a74b1d6a4929d1e1c3baf9471c
@ -964,17 +979,12 @@ F tool/omittest.tcl 72a49b8a9a8b0bf213a438180307a0df836d4380
F tool/opcodeDoc.awk b3a2a3d5d3075b8bd90b7afe24283efdd586659c F tool/opcodeDoc.awk b3a2a3d5d3075b8bd90b7afe24283efdd586659c
F tool/restore_jrnl.tcl 6957a34f8f1f0f8285e07536225ec3b292a9024a F tool/restore_jrnl.tcl 6957a34f8f1f0f8285e07536225ec3b292a9024a
F tool/rollback-test.c 9fc98427d1e23e84429d7e6d07d9094fbdec65a5 F tool/rollback-test.c 9fc98427d1e23e84429d7e6d07d9094fbdec65a5
F tool/shell1.test 20dfe7099cf2afe37aecd69afb7678d14f7a0abf F tool/showdb.c 2e28d8e499b016485672e9a7ac65dacc0d28ff69
F tool/shell2.test 5dc76b8005b465f420fed8241621da7513060ff3
F tool/shell3.test 4fad469e8003938426355afdf34155f08c587836
F tool/shell4.test 35f9c3d452b4e76d5013c63e1fd07478a62f14ce
F tool/shell5.test 62bfaf9267296da1b91e4b1c03e44e7b393f6a94
F tool/showdb.c 43e913d954684c2f5007dcab46d1a1308852a0ad
F tool/showjournal.c b62cecaab86a4053d944c276bb5232e4d17ece02 F tool/showjournal.c b62cecaab86a4053d944c276bb5232e4d17ece02
F tool/showwal.c f09e5a80a293919290ec85a6a37c85a5ddcf37d9 F tool/showwal.c f09e5a80a293919290ec85a6a37c85a5ddcf37d9
F tool/soak1.tcl 8d407956e1a45b485a8e072470a3e629a27037fe F tool/soak1.tcl 8d407956e1a45b485a8e072470a3e629a27037fe
F tool/space_used.tcl f714c41a59e326b8b9042f415b628b561bafa06b F tool/space_used.tcl f714c41a59e326b8b9042f415b628b561bafa06b
F tool/spaceanal.tcl 15f6cd939b4ecc14d061de7e8ace89e26c30c40b F tool/spaceanal.tcl e42273000686a4afbf6a5e5d7fb12be65e92afb1
F tool/speedtest.tcl 06c76698485ccf597b9e7dbb1ac70706eb873355 F tool/speedtest.tcl 06c76698485ccf597b9e7dbb1ac70706eb873355
F tool/speedtest16.c c8a9c793df96db7e4933f0852abb7a03d48f2e81 F tool/speedtest16.c c8a9c793df96db7e4933f0852abb7a03d48f2e81
F tool/speedtest2.tcl ee2149167303ba8e95af97873c575c3e0fab58ff F tool/speedtest2.tcl ee2149167303ba8e95af97873c575c3e0fab58ff
@ -985,10 +995,12 @@ F tool/symbols-mingw.sh 4dbcea7e74768305384c9fd2ed2b41bbf9f0414d
F tool/symbols.sh fec58532668296d7c7dc48be9c87f75ccdb5814f F tool/symbols.sh fec58532668296d7c7dc48be9c87f75ccdb5814f
F tool/tostr.awk e75472c2f98dd76e06b8c9c1367f4ab07e122d06 F tool/tostr.awk e75472c2f98dd76e06b8c9c1367f4ab07e122d06
F tool/vdbe-compress.tcl d70ea6d8a19e3571d7ab8c9b75cba86d1173ff0f F tool/vdbe-compress.tcl d70ea6d8a19e3571d7ab8c9b75cba86d1173ff0f
F tool/warnings-clang.sh 9f406d66e750e8ac031c63a9ef3248aaa347ef2a F tool/warnings-clang.sh a8a0a3babda96dfb1ff51adda3cbbf3dfb7266c2
F tool/warnings.sh fbc018d67fd7395f440c28f33ef0f94420226381 F tool/warnings.sh fbc018d67fd7395f440c28f33ef0f94420226381
P 9da1cd0a06aca4eb3fb21fa4d321b9e3e07444b4 P af525b5d25b5dee20528acc8321b8c6ca9aa98ae
R 81a3b6b323708ac95becda2f7731a3a4 R 25bb989b178d8b31e0e59b449786abed
T +sym-version-3.7.10 * T +bgcolor * #d0c0ff
T +sym-release *
T +sym-version-3.7.12 *
U drh U drh
Z 1c2c75389d69d7c8466f72bd2161ada0 Z 093fff96464341c2fff7d3363f677b3a

View File

@ -1 +1 @@
ebd01a8deffb5024a5d7494eef800d2366d97204 8654aa9540fe9fd210899d83d17f3f407096c004

View File

@ -530,7 +530,7 @@ void sqlite3AlterRenameTable(
"WHEN name LIKE 'sqlite_autoindex%%' AND type='index' THEN " "WHEN name LIKE 'sqlite_autoindex%%' AND type='index' THEN "
"'sqlite_autoindex_' || %Q || substr(name,%d+18) " "'sqlite_autoindex_' || %Q || substr(name,%d+18) "
"ELSE name END " "ELSE name END "
"WHERE tbl_name=%Q AND " "WHERE tbl_name=%Q COLLATE nocase AND "
"(type='table' OR type='index' OR type='trigger');", "(type='table' OR type='index' OR type='trigger');",
zDb, SCHEMA_TABLE(iDb), zName, zName, zName, zDb, SCHEMA_TABLE(iDb), zName, zName, zName,
#ifndef SQLITE_OMIT_TRIGGER #ifndef SQLITE_OMIT_TRIGGER

View File

@ -932,6 +932,7 @@ static int loadStat3(sqlite3 *db, const char *zDb){
int eType; /* Datatype of a sample */ int eType; /* Datatype of a sample */
IndexSample *pSample; /* A slot in pIdx->aSample[] */ IndexSample *pSample; /* A slot in pIdx->aSample[] */
assert( db->lookaside.bEnabled==0 );
if( !sqlite3FindTable(db, "sqlite_stat3", zDb) ){ if( !sqlite3FindTable(db, "sqlite_stat3", zDb) ){
return SQLITE_OK; return SQLITE_OK;
} }
@ -958,7 +959,7 @@ static int loadStat3(sqlite3 *db, const char *zDb){
if( pIdx==0 ) continue; if( pIdx==0 ) continue;
assert( pIdx->nSample==0 ); assert( pIdx->nSample==0 );
pIdx->nSample = nSample; pIdx->nSample = nSample;
pIdx->aSample = sqlite3MallocZero( nSample*sizeof(IndexSample) ); pIdx->aSample = sqlite3DbMallocZero(db, nSample*sizeof(IndexSample));
pIdx->avgEq = pIdx->aiRowEst[1]; pIdx->avgEq = pIdx->aiRowEst[1];
if( pIdx->aSample==0 ){ if( pIdx->aSample==0 ){
db->mallocFailed = 1; db->mallocFailed = 1;
@ -1031,7 +1032,7 @@ static int loadStat3(sqlite3 *db, const char *zDb){
if( n < 1){ if( n < 1){
pSample->u.z = 0; pSample->u.z = 0;
}else{ }else{
pSample->u.z = sqlite3Malloc(n); pSample->u.z = sqlite3DbMallocRaw(db, n);
if( pSample->u.z==0 ){ if( pSample->u.z==0 ){
db->mallocFailed = 1; db->mallocFailed = 1;
sqlite3_finalize(pStmt); sqlite3_finalize(pStmt);
@ -1107,7 +1108,10 @@ int sqlite3AnalysisLoad(sqlite3 *db, int iDb){
/* Load the statistics from the sqlite_stat3 table. */ /* Load the statistics from the sqlite_stat3 table. */
#ifdef SQLITE_ENABLE_STAT3 #ifdef SQLITE_ENABLE_STAT3
if( rc==SQLITE_OK ){ if( rc==SQLITE_OK ){
int lookasideEnabled = db->lookaside.bEnabled;
db->lookaside.bEnabled = 0;
rc = loadStat3(db, sInfo.zDatabase); rc = loadStat3(db, sInfo.zDatabase);
db->lookaside.bEnabled = lookasideEnabled;
} }
#endif #endif

View File

@ -568,7 +568,7 @@ int sqlite3_backup_finish(sqlite3_backup *p){
} }
/* If a transaction is still open on the Btree, roll it back. */ /* If a transaction is still open on the Btree, roll it back. */
sqlite3BtreeRollback(p->pDest); sqlite3BtreeRollback(p->pDest, SQLITE_OK);
/* Set the error code of the destination database handle. */ /* Set the error code of the destination database handle. */
rc = (p->rc==SQLITE_DONE) ? SQLITE_OK : p->rc; rc = (p->rc==SQLITE_DONE) ? SQLITE_OK : p->rc;

View File

@ -870,12 +870,10 @@ static u8 *findOverflowCell(MemPage *pPage, int iCell){
assert( sqlite3_mutex_held(pPage->pBt->mutex) ); assert( sqlite3_mutex_held(pPage->pBt->mutex) );
for(i=pPage->nOverflow-1; i>=0; i--){ for(i=pPage->nOverflow-1; i>=0; i--){
int k; int k;
struct _OvflCell *pOvfl; k = pPage->aiOvfl[i];
pOvfl = &pPage->aOvfl[i];
k = pOvfl->idx;
if( k<=iCell ){ if( k<=iCell ){
if( k==iCell ){ if( k==iCell ){
return pOvfl->pCell; return pPage->apOvfl[i];
} }
iCell--; iCell--;
} }
@ -1689,11 +1687,8 @@ static int btreeInvokeBusyHandler(void *pArg){
** If zFilename is ":memory:" then an in-memory database is created ** If zFilename is ":memory:" then an in-memory database is created
** that is automatically destroyed when it is closed. ** that is automatically destroyed when it is closed.
** **
** The "flags" parameter is a bitmask that might contain bits ** The "flags" parameter is a bitmask that might contain bits like
** BTREE_OMIT_JOURNAL and/or BTREE_NO_READLOCK. The BTREE_NO_READLOCK ** BTREE_OMIT_JOURNAL and/or BTREE_MEMORY.
** bit is also set if the SQLITE_NoReadlock flags is set in db->flags.
** These flags are passed through into sqlite3PagerOpen() and must
** be the same values as PAGER_OMIT_JOURNAL and PAGER_NO_READLOCK.
** **
** If the database is already opened in the same database connection ** If the database is already opened in the same database connection
** and we are in shared cache mode, then the open will fail with an ** and we are in shared cache mode, then the open will fail with an
@ -1740,9 +1735,6 @@ int sqlite3BtreeOpen(
/* A BTREE_SINGLE database is always a temporary and/or ephemeral */ /* A BTREE_SINGLE database is always a temporary and/or ephemeral */
assert( (flags & BTREE_SINGLE)==0 || isTempDb ); assert( (flags & BTREE_SINGLE)==0 || isTempDb );
if( db->flags & SQLITE_NoReadlock ){
flags |= BTREE_NO_READLOCK;
}
if( isMemdb ){ if( isMemdb ){
flags |= BTREE_MEMORY; flags |= BTREE_MEMORY;
} }
@ -2049,7 +2041,7 @@ int sqlite3BtreeClose(Btree *p){
** The call to sqlite3BtreeRollback() drops any table-locks held by ** The call to sqlite3BtreeRollback() drops any table-locks held by
** this handle. ** this handle.
*/ */
sqlite3BtreeRollback(p); sqlite3BtreeRollback(p, SQLITE_OK);
sqlite3BtreeLeave(p); sqlite3BtreeLeave(p);
/* If there are still other outstanding references to the shared-btree /* If there are still other outstanding references to the shared-btree
@ -3287,6 +3279,7 @@ static int countWriteCursors(BtShared *pBt){
*/ */
void sqlite3BtreeTripAllCursors(Btree *pBtree, int errCode){ void sqlite3BtreeTripAllCursors(Btree *pBtree, int errCode){
BtCursor *p; BtCursor *p;
if( pBtree==0 ) return;
sqlite3BtreeEnter(pBtree); sqlite3BtreeEnter(pBtree);
for(p=pBtree->pBt->pCursor; p; p=p->pNext){ for(p=pBtree->pBt->pCursor; p; p=p->pNext){
int i; int i;
@ -3310,25 +3303,20 @@ void sqlite3BtreeTripAllCursors(Btree *pBtree, int errCode){
** This will release the write lock on the database file. If there ** This will release the write lock on the database file. If there
** are no active cursors, it also releases the read lock. ** are no active cursors, it also releases the read lock.
*/ */
int sqlite3BtreeRollback(Btree *p){ int sqlite3BtreeRollback(Btree *p, int tripCode){
int rc; int rc;
BtShared *pBt = p->pBt; BtShared *pBt = p->pBt;
MemPage *pPage1; MemPage *pPage1;
sqlite3BtreeEnter(p); sqlite3BtreeEnter(p);
rc = saveAllCursors(pBt, 0, 0); if( tripCode==SQLITE_OK ){
#ifndef SQLITE_OMIT_SHARED_CACHE rc = tripCode = saveAllCursors(pBt, 0, 0);
if( rc!=SQLITE_OK ){ }else{
/* This is a horrible situation. An IO or malloc() error occurred whilst rc = SQLITE_OK;
** trying to save cursor positions. If this is an automatic rollback (as }
** the result of a constraint, malloc() failure or IO error) then if( tripCode ){
** the cache may be internally inconsistent (not contain valid trees) so sqlite3BtreeTripAllCursors(p, tripCode);
** we cannot simply return the error to the caller. Instead, abort
** all queries that may be using any of the cursors that failed to save.
*/
sqlite3BtreeTripAllCursors(p, rc);
} }
#endif
btreeIntegrity(p); btreeIntegrity(p);
if( p->inTrans==TRANS_WRITE ){ if( p->inTrans==TRANS_WRITE ){
@ -5527,7 +5515,7 @@ static void dropCell(MemPage *pPage, int idx, int sz, int *pRC){
** If the cell content will fit on the page, then put it there. If it ** If the cell content will fit on the page, then put it there. If it
** will not fit, then make a copy of the cell content into pTemp if ** will not fit, then make a copy of the cell content into pTemp if
** pTemp is not null. Regardless of pTemp, allocate a new entry ** pTemp is not null. Regardless of pTemp, allocate a new entry
** in pPage->aOvfl[] and make it point to the cell content (either ** in pPage->apOvfl[] and make it point to the cell content (either
** in pTemp or the original pCell) and also record its index. ** in pTemp or the original pCell) and also record its index.
** Allocating a new entry in pPage->aCell[] implies that ** Allocating a new entry in pPage->aCell[] implies that
** pPage->nOverflow is incremented. ** pPage->nOverflow is incremented.
@ -5561,7 +5549,8 @@ static void insertCell(
assert( i>=0 && i<=pPage->nCell+pPage->nOverflow ); assert( i>=0 && i<=pPage->nCell+pPage->nOverflow );
assert( pPage->nCell<=MX_CELL(pPage->pBt) && MX_CELL(pPage->pBt)<=10921 ); assert( pPage->nCell<=MX_CELL(pPage->pBt) && MX_CELL(pPage->pBt)<=10921 );
assert( pPage->nOverflow<=ArraySize(pPage->aOvfl) ); assert( pPage->nOverflow<=ArraySize(pPage->apOvfl) );
assert( ArraySize(pPage->apOvfl)==ArraySize(pPage->aiOvfl) );
assert( sqlite3_mutex_held(pPage->pBt->mutex) ); assert( sqlite3_mutex_held(pPage->pBt->mutex) );
/* The cell should normally be sized correctly. However, when moving a /* The cell should normally be sized correctly. However, when moving a
** malformed cell from a leaf page to an interior page, if the cell size ** malformed cell from a leaf page to an interior page, if the cell size
@ -5578,9 +5567,9 @@ static void insertCell(
put4byte(pCell, iChild); put4byte(pCell, iChild);
} }
j = pPage->nOverflow++; j = pPage->nOverflow++;
assert( j<(int)(sizeof(pPage->aOvfl)/sizeof(pPage->aOvfl[0])) ); assert( j<(int)(sizeof(pPage->apOvfl)/sizeof(pPage->apOvfl[0])) );
pPage->aOvfl[j].pCell = pCell; pPage->apOvfl[j] = pCell;
pPage->aOvfl[j].idx = (u16)i; pPage->aiOvfl[j] = (u16)i;
}else{ }else{
int rc = sqlite3PagerWrite(pPage->pDbPage); int rc = sqlite3PagerWrite(pPage->pDbPage);
if( rc!=SQLITE_OK ){ if( rc!=SQLITE_OK ){
@ -5728,7 +5717,7 @@ static int balance_quick(MemPage *pParent, MemPage *pPage, u8 *pSpace){
if( rc==SQLITE_OK ){ if( rc==SQLITE_OK ){
u8 *pOut = &pSpace[4]; u8 *pOut = &pSpace[4];
u8 *pCell = pPage->aOvfl[0].pCell; u8 *pCell = pPage->apOvfl[0];
u16 szCell = cellSizePtr(pPage, pCell); u16 szCell = cellSizePtr(pPage, pCell);
u8 *pStop; u8 *pStop;
@ -5838,7 +5827,7 @@ static int ptrmapCheckPages(MemPage **apPage, int nPage){
** map entries are also updated so that the parent page is page pTo. ** map entries are also updated so that the parent page is page pTo.
** **
** If pFrom is currently carrying any overflow cells (entries in the ** If pFrom is currently carrying any overflow cells (entries in the
** MemPage.aOvfl[] array), they are not copied to pTo. ** MemPage.apOvfl[] array), they are not copied to pTo.
** **
** Before returning, page pTo is reinitialized using btreeInitPage(). ** Before returning, page pTo is reinitialized using btreeInitPage().
** **
@ -5975,7 +5964,7 @@ static int balance_nonroot(
** is called (indirectly) from sqlite3BtreeDelete(). ** is called (indirectly) from sqlite3BtreeDelete().
*/ */
assert( pParent->nOverflow==0 || pParent->nOverflow==1 ); assert( pParent->nOverflow==0 || pParent->nOverflow==1 );
assert( pParent->nOverflow==0 || pParent->aOvfl[0].idx==iParentIdx ); assert( pParent->nOverflow==0 || pParent->aiOvfl[0]==iParentIdx );
if( !aOvflSpace ){ if( !aOvflSpace ){
return SQLITE_NOMEM; return SQLITE_NOMEM;
@ -6022,8 +6011,8 @@ static int balance_nonroot(
nMaxCells += 1+apOld[i]->nCell+apOld[i]->nOverflow; nMaxCells += 1+apOld[i]->nCell+apOld[i]->nOverflow;
if( (i--)==0 ) break; if( (i--)==0 ) break;
if( i+nxDiv==pParent->aOvfl[0].idx && pParent->nOverflow ){ if( i+nxDiv==pParent->aiOvfl[0] && pParent->nOverflow ){
apDiv[i] = pParent->aOvfl[0].pCell; apDiv[i] = pParent->apOvfl[0];
pgno = get4byte(apDiv[i]); pgno = get4byte(apDiv[i]);
szNew[i] = cellSizePtr(pParent, apDiv[i]); szNew[i] = cellSizePtr(pParent, apDiv[i]);
pParent->nOverflow = 0; pParent->nOverflow = 0;
@ -6464,7 +6453,7 @@ static int balance_nonroot(
MemPage *pOld = apCopy[0]; MemPage *pOld = apCopy[0];
int nOverflow = pOld->nOverflow; int nOverflow = pOld->nOverflow;
int iNextOld = pOld->nCell + nOverflow; int iNextOld = pOld->nCell + nOverflow;
int iOverflow = (nOverflow ? pOld->aOvfl[0].idx : -1); int iOverflow = (nOverflow ? pOld->aiOvfl[0] : -1);
j = 0; /* Current 'old' sibling page */ j = 0; /* Current 'old' sibling page */
k = 0; /* Current 'new' sibling page */ k = 0; /* Current 'new' sibling page */
for(i=0; i<nCell; i++){ for(i=0; i<nCell; i++){
@ -6478,14 +6467,14 @@ static int balance_nonroot(
iNextOld = i + !leafData + pOld->nCell + pOld->nOverflow; iNextOld = i + !leafData + pOld->nCell + pOld->nOverflow;
if( pOld->nOverflow ){ if( pOld->nOverflow ){
nOverflow = pOld->nOverflow; nOverflow = pOld->nOverflow;
iOverflow = i + !leafData + pOld->aOvfl[0].idx; iOverflow = i + !leafData + pOld->aiOvfl[0];
} }
isDivider = !leafData; isDivider = !leafData;
} }
assert(nOverflow>0 || iOverflow<i ); assert(nOverflow>0 || iOverflow<i );
assert(nOverflow<2 || pOld->aOvfl[0].idx==pOld->aOvfl[1].idx-1); assert(nOverflow<2 || pOld->aiOvfl[0]==pOld->aiOvfl[1]-1);
assert(nOverflow<3 || pOld->aOvfl[1].idx==pOld->aOvfl[2].idx-1); assert(nOverflow<3 || pOld->aiOvfl[1]==pOld->aiOvfl[2]-1);
if( i==iOverflow ){ if( i==iOverflow ){
isDivider = 1; isDivider = 1;
if( (--nOverflow)>0 ){ if( (--nOverflow)>0 ){
@ -6606,7 +6595,10 @@ static int balance_deeper(MemPage *pRoot, MemPage **ppChild){
TRACE(("BALANCE: copy root %d into %d\n", pRoot->pgno, pChild->pgno)); TRACE(("BALANCE: copy root %d into %d\n", pRoot->pgno, pChild->pgno));
/* Copy the overflow cells from pRoot to pChild */ /* Copy the overflow cells from pRoot to pChild */
memcpy(pChild->aOvfl, pRoot->aOvfl, pRoot->nOverflow*sizeof(pRoot->aOvfl[0])); memcpy(pChild->aiOvfl, pRoot->aiOvfl,
pRoot->nOverflow*sizeof(pRoot->aiOvfl[0]));
memcpy(pChild->apOvfl, pRoot->apOvfl,
pRoot->nOverflow*sizeof(pRoot->apOvfl[0]));
pChild->nOverflow = pRoot->nOverflow; pChild->nOverflow = pRoot->nOverflow;
/* Zero the contents of pRoot. Then install pChild as the right-child. */ /* Zero the contents of pRoot. Then install pChild as the right-child. */
@ -6669,7 +6661,7 @@ static int balance(BtCursor *pCur){
#ifndef SQLITE_OMIT_QUICKBALANCE #ifndef SQLITE_OMIT_QUICKBALANCE
if( pPage->hasData if( pPage->hasData
&& pPage->nOverflow==1 && pPage->nOverflow==1
&& pPage->aOvfl[0].idx==pPage->nCell && pPage->aiOvfl[0]==pPage->nCell
&& pParent->pgno!=1 && pParent->pgno!=1
&& pParent->nCell==iIdx && pParent->nCell==iIdx
){ ){
@ -6797,13 +6789,6 @@ int sqlite3BtreeInsert(
** blob of associated data. */ ** blob of associated data. */
assert( (pKey==0)==(pCur->pKeyInfo==0) ); assert( (pKey==0)==(pCur->pKeyInfo==0) );
/* If this is an insert into a table b-tree, invalidate any incrblob
** cursors open on the row being replaced (assuming this is a replace
** operation - if it is not, the following is a no-op). */
if( pCur->pKeyInfo==0 ){
invalidateIncrblobCursors(p, nKey, 0);
}
/* Save the positions of any other cursors open on this table. /* Save the positions of any other cursors open on this table.
** **
** In some cases, the call to btreeMoveto() below is a no-op. For ** In some cases, the call to btreeMoveto() below is a no-op. For
@ -6817,6 +6802,14 @@ int sqlite3BtreeInsert(
*/ */
rc = saveAllCursors(pBt, pCur->pgnoRoot, pCur); rc = saveAllCursors(pBt, pCur->pgnoRoot, pCur);
if( rc ) return rc; if( rc ) return rc;
/* If this is an insert into a table b-tree, invalidate any incrblob
** cursors open on the row being replaced (assuming this is a replace
** operation - if it is not, the following is a no-op). */
if( pCur->pKeyInfo==0 ){
invalidateIncrblobCursors(p, nKey, 0);
}
if( !loc ){ if( !loc ){
rc = btreeMoveto(pCur, pKey, nKey, appendBias, &loc); rc = btreeMoveto(pCur, pKey, nKey, appendBias, &loc);
if( rc ) return rc; if( rc ) return rc;
@ -6927,12 +6920,6 @@ int sqlite3BtreeDelete(BtCursor *pCur){
return SQLITE_ERROR; /* Something has gone awry. */ return SQLITE_ERROR; /* Something has gone awry. */
} }
/* If this is a delete operation to remove a row from a table b-tree,
** invalidate any incrblob cursors open on the row being deleted. */
if( pCur->pKeyInfo==0 ){
invalidateIncrblobCursors(p, pCur->info.nKey, 0);
}
iCellDepth = pCur->iPage; iCellDepth = pCur->iPage;
iCellIdx = pCur->aiIdx[iCellDepth]; iCellIdx = pCur->aiIdx[iCellDepth];
pPage = pCur->apPage[iCellDepth]; pPage = pCur->apPage[iCellDepth];
@ -6958,6 +6945,13 @@ int sqlite3BtreeDelete(BtCursor *pCur){
*/ */
rc = saveAllCursors(pBt, pCur->pgnoRoot, pCur); rc = saveAllCursors(pBt, pCur->pgnoRoot, pCur);
if( rc ) return rc; if( rc ) return rc;
/* If this is a delete operation to remove a row from a table b-tree,
** invalidate any incrblob cursors open on the row being deleted. */
if( pCur->pKeyInfo==0 ){
invalidateIncrblobCursors(p, pCur->info.nKey, 0);
}
rc = sqlite3PagerWrite(pPage->pDbPage); rc = sqlite3PagerWrite(pPage->pDbPage);
if( rc ) return rc; if( rc ) return rc;
rc = clearCell(pPage, pCell); rc = clearCell(pPage, pCell);
@ -7239,13 +7233,13 @@ int sqlite3BtreeClearTable(Btree *p, int iTable, int *pnChange){
sqlite3BtreeEnter(p); sqlite3BtreeEnter(p);
assert( p->inTrans==TRANS_WRITE ); assert( p->inTrans==TRANS_WRITE );
/* Invalidate all incrblob cursors open on table iTable (assuming iTable
** is the root of a table b-tree - if it is not, the following call is
** a no-op). */
invalidateIncrblobCursors(p, 0, 1);
rc = saveAllCursors(pBt, (Pgno)iTable, 0); rc = saveAllCursors(pBt, (Pgno)iTable, 0);
if( SQLITE_OK==rc ){ if( SQLITE_OK==rc ){
/* Invalidate all incrblob cursors open on table iTable (assuming iTable
** is the root of a table b-tree - if it is not, the following call is
** a no-op). */
invalidateIncrblobCursors(p, 0, 1);
rc = clearDatabasePage(pBt, (Pgno)iTable, 0, pnChange); rc = clearDatabasePage(pBt, (Pgno)iTable, 0, pnChange);
} }
sqlite3BtreeLeave(p); sqlite3BtreeLeave(p);
@ -7560,6 +7554,25 @@ static void checkAppendMsg(
#endif /* SQLITE_OMIT_INTEGRITY_CHECK */ #endif /* SQLITE_OMIT_INTEGRITY_CHECK */
#ifndef SQLITE_OMIT_INTEGRITY_CHECK #ifndef SQLITE_OMIT_INTEGRITY_CHECK
/*
** Return non-zero if the bit in the IntegrityCk.aPgRef[] array that
** corresponds to page iPg is already set.
*/
static int getPageReferenced(IntegrityCk *pCheck, Pgno iPg){
assert( iPg<=pCheck->nPage && sizeof(pCheck->aPgRef[0])==1 );
return (pCheck->aPgRef[iPg/8] & (1 << (iPg & 0x07)));
}
/*
** Set the bit in the IntegrityCk.aPgRef[] array that corresponds to page iPg.
*/
static void setPageReferenced(IntegrityCk *pCheck, Pgno iPg){
assert( iPg<=pCheck->nPage && sizeof(pCheck->aPgRef[0])==1 );
pCheck->aPgRef[iPg/8] |= (1 << (iPg & 0x07));
}
/* /*
** Add 1 to the reference count for page iPage. If this is the second ** Add 1 to the reference count for page iPage. If this is the second
** reference to the page, add an error message to pCheck->zErrMsg. ** reference to the page, add an error message to pCheck->zErrMsg.
@ -7574,11 +7587,12 @@ static int checkRef(IntegrityCk *pCheck, Pgno iPage, char *zContext){
checkAppendMsg(pCheck, zContext, "invalid page number %d", iPage); checkAppendMsg(pCheck, zContext, "invalid page number %d", iPage);
return 1; return 1;
} }
if( pCheck->anRef[iPage]==1 ){ if( getPageReferenced(pCheck, iPage) ){
checkAppendMsg(pCheck, zContext, "2nd reference to page %d", iPage); checkAppendMsg(pCheck, zContext, "2nd reference to page %d", iPage);
return 1; return 1;
} }
return (pCheck->anRef[iPage]++)>1; setPageReferenced(pCheck, iPage);
return 0;
} }
#ifndef SQLITE_OMIT_AUTOVACUUM #ifndef SQLITE_OMIT_AUTOVACUUM
@ -7954,17 +7968,15 @@ char *sqlite3BtreeIntegrityCheck(
sqlite3BtreeLeave(p); sqlite3BtreeLeave(p);
return 0; return 0;
} }
sCheck.anRef = sqlite3Malloc( (sCheck.nPage+1)*sizeof(sCheck.anRef[0]) );
if( !sCheck.anRef ){ sCheck.aPgRef = sqlite3MallocZero((sCheck.nPage / 8)+ 1);
if( !sCheck.aPgRef ){
*pnErr = 1; *pnErr = 1;
sqlite3BtreeLeave(p); sqlite3BtreeLeave(p);
return 0; return 0;
} }
for(i=0; i<=sCheck.nPage; i++){ sCheck.anRef[i] = 0; }
i = PENDING_BYTE_PAGE(pBt); i = PENDING_BYTE_PAGE(pBt);
if( i<=sCheck.nPage ){ if( i<=sCheck.nPage ) setPageReferenced(&sCheck, i);
sCheck.anRef[i] = 1;
}
sqlite3StrAccumInit(&sCheck.errMsg, zErr, sizeof(zErr), 20000); sqlite3StrAccumInit(&sCheck.errMsg, zErr, sizeof(zErr), 20000);
sCheck.errMsg.useMalloc = 2; sCheck.errMsg.useMalloc = 2;
@ -7989,18 +8001,18 @@ char *sqlite3BtreeIntegrityCheck(
*/ */
for(i=1; i<=sCheck.nPage && sCheck.mxErr; i++){ for(i=1; i<=sCheck.nPage && sCheck.mxErr; i++){
#ifdef SQLITE_OMIT_AUTOVACUUM #ifdef SQLITE_OMIT_AUTOVACUUM
if( sCheck.anRef[i]==0 ){ if( getPageReferenced(&sCheck, i)==0 ){
checkAppendMsg(&sCheck, 0, "Page %d is never used", i); checkAppendMsg(&sCheck, 0, "Page %d is never used", i);
} }
#else #else
/* If the database supports auto-vacuum, make sure no tables contain /* If the database supports auto-vacuum, make sure no tables contain
** references to pointer-map pages. ** references to pointer-map pages.
*/ */
if( sCheck.anRef[i]==0 && if( getPageReferenced(&sCheck, i)==0 &&
(PTRMAP_PAGENO(pBt, i)!=i || !pBt->autoVacuum) ){ (PTRMAP_PAGENO(pBt, i)!=i || !pBt->autoVacuum) ){
checkAppendMsg(&sCheck, 0, "Page %d is never used", i); checkAppendMsg(&sCheck, 0, "Page %d is never used", i);
} }
if( sCheck.anRef[i]!=0 && if( getPageReferenced(&sCheck, i)!=0 &&
(PTRMAP_PAGENO(pBt, i)==i && pBt->autoVacuum) ){ (PTRMAP_PAGENO(pBt, i)==i && pBt->autoVacuum) ){
checkAppendMsg(&sCheck, 0, "Pointer map page %d is referenced", i); checkAppendMsg(&sCheck, 0, "Pointer map page %d is referenced", i);
} }
@ -8021,7 +8033,7 @@ char *sqlite3BtreeIntegrityCheck(
/* Clean up and report errors. /* Clean up and report errors.
*/ */
sqlite3BtreeLeave(p); sqlite3BtreeLeave(p);
sqlite3_free(sCheck.anRef); sqlite3_free(sCheck.aPgRef);
if( sCheck.mallocFailed ){ if( sCheck.mallocFailed ){
sqlite3StrAccumReset(&sCheck.errMsg); sqlite3StrAccumReset(&sCheck.errMsg);
*pnErr = sCheck.nErr+1; *pnErr = sCheck.nErr+1;

View File

@ -57,10 +57,9 @@ int sqlite3BtreeOpen(
** pager.h. ** pager.h.
*/ */
#define BTREE_OMIT_JOURNAL 1 /* Do not create or use a rollback journal */ #define BTREE_OMIT_JOURNAL 1 /* Do not create or use a rollback journal */
#define BTREE_NO_READLOCK 2 /* Omit readlocks on readonly files */ #define BTREE_MEMORY 2 /* This is an in-memory DB */
#define BTREE_MEMORY 4 /* This is an in-memory DB */ #define BTREE_SINGLE 4 /* The file contains at most 1 b-tree */
#define BTREE_SINGLE 8 /* The file contains at most 1 b-tree */ #define BTREE_UNORDERED 8 /* Use of a hash implementation is OK */
#define BTREE_UNORDERED 16 /* Use of a hash implementation is OK */
int sqlite3BtreeClose(Btree*); int sqlite3BtreeClose(Btree*);
int sqlite3BtreeSetCacheSize(Btree*,int); int sqlite3BtreeSetCacheSize(Btree*,int);
@ -78,7 +77,7 @@ int sqlite3BtreeBeginTrans(Btree*,int);
int sqlite3BtreeCommitPhaseOne(Btree*, const char *zMaster); int sqlite3BtreeCommitPhaseOne(Btree*, const char *zMaster);
int sqlite3BtreeCommitPhaseTwo(Btree*, int); int sqlite3BtreeCommitPhaseTwo(Btree*, int);
int sqlite3BtreeCommit(Btree*); int sqlite3BtreeCommit(Btree*);
int sqlite3BtreeRollback(Btree*); int sqlite3BtreeRollback(Btree*,int);
int sqlite3BtreeBeginStmt(Btree*,int); int sqlite3BtreeBeginStmt(Btree*,int);
int sqlite3BtreeCreateTable(Btree*, int*, int flags); int sqlite3BtreeCreateTable(Btree*, int*, int flags);
int sqlite3BtreeIsInTrans(Btree*); int sqlite3BtreeIsInTrans(Btree*);

View File

@ -284,10 +284,9 @@ struct MemPage {
u16 nFree; /* Number of free bytes on the page */ u16 nFree; /* Number of free bytes on the page */
u16 nCell; /* Number of cells on this page, local and ovfl */ u16 nCell; /* Number of cells on this page, local and ovfl */
u16 maskPage; /* Mask for page offset */ u16 maskPage; /* Mask for page offset */
struct _OvflCell { /* Cells that will not fit on aData[] */ u16 aiOvfl[5]; /* Insert the i-th overflow cell before the aiOvfl-th
u8 *pCell; /* Pointers to the body of the overflow cell */ ** non-overflow cell */
u16 idx; /* Insert this cell before idx-th non-overflow cell */ u8 *apOvfl[5]; /* Pointers to the body of overflow cells */
} aOvfl[5];
BtShared *pBt; /* Pointer to BtShared that this page is part of */ BtShared *pBt; /* Pointer to BtShared that this page is part of */
u8 *aData; /* Pointer to disk image of the page data */ u8 *aData; /* Pointer to disk image of the page data */
u8 *aDataEnd; /* One byte past the end of usable data */ u8 *aDataEnd; /* One byte past the end of usable data */
@ -495,6 +494,9 @@ struct BtCursor {
BtShared *pBt; /* The BtShared this cursor points to */ BtShared *pBt; /* The BtShared this cursor points to */
BtCursor *pNext, *pPrev; /* Forms a linked list of all cursors */ BtCursor *pNext, *pPrev; /* Forms a linked list of all cursors */
struct KeyInfo *pKeyInfo; /* Argument passed to comparison function */ struct KeyInfo *pKeyInfo; /* Argument passed to comparison function */
#ifndef SQLITE_OMIT_INCRBLOB
Pgno *aOverflow; /* Cache of overflow page locations */
#endif
Pgno pgnoRoot; /* The root page of this tree */ Pgno pgnoRoot; /* The root page of this tree */
sqlite3_int64 cachedRowid; /* Next rowid cache. 0 means not valid */ sqlite3_int64 cachedRowid; /* Next rowid cache. 0 means not valid */
CellInfo info; /* A parse of the cell we are pointing at */ CellInfo info; /* A parse of the cell we are pointing at */
@ -506,7 +508,6 @@ struct BtCursor {
u8 validNKey; /* True if info.nKey is valid */ u8 validNKey; /* True if info.nKey is valid */
u8 eState; /* One of the CURSOR_XXX constants (see below) */ u8 eState; /* One of the CURSOR_XXX constants (see below) */
#ifndef SQLITE_OMIT_INCRBLOB #ifndef SQLITE_OMIT_INCRBLOB
Pgno *aOverflow; /* Cache of overflow page locations */
u8 isIncrblobHandle; /* True if this cursor is an incr. io handle */ u8 isIncrblobHandle; /* True if this cursor is an incr. io handle */
#endif #endif
i16 iPage; /* Index of current page in apPage */ i16 iPage; /* Index of current page in apPage */
@ -630,13 +631,19 @@ struct BtCursor {
/* /*
** This structure is passed around through all the sanity checking routines ** This structure is passed around through all the sanity checking routines
** in order to keep track of some global state information. ** in order to keep track of some global state information.
**
** The aRef[] array is allocated so that there is 1 bit for each page in
** the database. As the integrity-check proceeds, for each page used in
** the database the corresponding bit is set. This allows integrity-check to
** detect pages that are used twice and orphaned pages (both of which
** indicate corruption).
*/ */
typedef struct IntegrityCk IntegrityCk; typedef struct IntegrityCk IntegrityCk;
struct IntegrityCk { struct IntegrityCk {
BtShared *pBt; /* The tree being checked out */ BtShared *pBt; /* The tree being checked out */
Pager *pPager; /* The associated pager. Also accessible by pBt->pPager */ Pager *pPager; /* The associated pager. Also accessible by pBt->pPager */
u8 *aPgRef; /* 1 bit per page in the db (see above) */
Pgno nPage; /* Number of pages in the database */ Pgno nPage; /* Number of pages in the database */
int *anRef; /* Number of times each page is referenced */
int mxErr; /* Stop accumulating errors when this reaches zero */ int mxErr; /* Stop accumulating errors when this reaches zero */
int nErr; /* Number of messages written to zErrMsg so far */ int nErr; /* Number of messages written to zErrMsg so far */
int mallocFailed; /* A memory allocation error has occurred */ int mallocFailed; /* A memory allocation error has occurred */

View File

@ -537,7 +537,7 @@ void sqlite3DeleteTable(sqlite3 *db, Table *pTable){
sqlite3DbFree(db, pTable->zColAff); sqlite3DbFree(db, pTable->zColAff);
sqlite3SelectDelete(db, pTable->pSelect); sqlite3SelectDelete(db, pTable->pSelect);
#ifndef SQLITE_OMIT_CHECK #ifndef SQLITE_OMIT_CHECK
sqlite3ExprDelete(db, pTable->pCheck); sqlite3ExprListDelete(db, pTable->pCheck);
#endif #endif
#ifndef SQLITE_OMIT_VIRTUALTABLE #ifndef SQLITE_OMIT_VIRTUALTABLE
sqlite3VtabClear(db, pTable); sqlite3VtabClear(db, pTable);
@ -1200,15 +1200,17 @@ void sqlite3AddCheckConstraint(
Parse *pParse, /* Parsing context */ Parse *pParse, /* Parsing context */
Expr *pCheckExpr /* The check expression */ Expr *pCheckExpr /* The check expression */
){ ){
sqlite3 *db = pParse->db;
#ifndef SQLITE_OMIT_CHECK #ifndef SQLITE_OMIT_CHECK
Table *pTab = pParse->pNewTable; Table *pTab = pParse->pNewTable;
if( pTab && !IN_DECLARE_VTAB ){ if( pTab && !IN_DECLARE_VTAB ){
pTab->pCheck = sqlite3ExprAnd(db, pTab->pCheck, pCheckExpr); pTab->pCheck = sqlite3ExprListAppend(pParse, pTab->pCheck, pCheckExpr);
if( pParse->constraintName.n ){
sqlite3ExprListSetName(pParse, pTab->pCheck, &pParse->constraintName, 1);
}
}else }else
#endif #endif
{ {
sqlite3ExprDelete(db, pCheckExpr); sqlite3ExprDelete(pParse->db, pCheckExpr);
} }
} }
@ -1478,6 +1480,8 @@ void sqlite3EndTable(
if( p->pCheck ){ if( p->pCheck ){
SrcList sSrc; /* Fake SrcList for pParse->pNewTable */ SrcList sSrc; /* Fake SrcList for pParse->pNewTable */
NameContext sNC; /* Name context for pParse->pNewTable */ NameContext sNC; /* Name context for pParse->pNewTable */
ExprList *pList; /* List of all CHECK constraints */
int i; /* Loop counter */
memset(&sNC, 0, sizeof(sNC)); memset(&sNC, 0, sizeof(sNC));
memset(&sSrc, 0, sizeof(sSrc)); memset(&sSrc, 0, sizeof(sSrc));
@ -1488,8 +1492,11 @@ void sqlite3EndTable(
sNC.pParse = pParse; sNC.pParse = pParse;
sNC.pSrcList = &sSrc; sNC.pSrcList = &sSrc;
sNC.isCheck = 1; sNC.isCheck = 1;
if( sqlite3ResolveExprNames(&sNC, p->pCheck) ){ pList = p->pCheck;
return; for(i=0; i<pList->nExpr; i++){
if( sqlite3ResolveExprNames(&sNC, pList->a[i].pExpr) ){
return;
}
} }
} }
#endif /* !defined(SQLITE_OMIT_CHECK) */ #endif /* !defined(SQLITE_OMIT_CHECK) */
@ -1638,7 +1645,6 @@ void sqlite3EndTable(
return; return;
} }
pParse->pNewTable = 0; pParse->pNewTable = 0;
db->nTable++;
db->flags |= SQLITE_InternChanges; db->flags |= SQLITE_InternChanges;
#ifndef SQLITE_OMIT_ALTERTABLE #ifndef SQLITE_OMIT_ALTERTABLE
@ -3041,45 +3047,43 @@ exit_drop_index:
} }
/* /*
** pArray is a pointer to an array of objects. Each object in the ** pArray is a pointer to an array of objects. Each object in the
** array is szEntry bytes in size. This routine allocates a new ** array is szEntry bytes in size. This routine uses sqlite3DbRealloc()
** object on the end of the array. ** to extend the array so that there is space for a new object at the end.
** **
** *pnEntry is the number of entries already in use. *pnAlloc is ** When this function is called, *pnEntry contains the current size of
** the previously allocated size of the array. initSize is the ** the array (in entries - so the allocation is ((*pnEntry) * szEntry) bytes
** suggested initial array size allocation. ** in total).
** **
** The index of the new entry is returned in *pIdx. ** If the realloc() is successful (i.e. if no OOM condition occurs), the
** space allocated for the new object is zeroed, *pnEntry updated to
** reflect the new size of the array and a pointer to the new allocation
** returned. *pIdx is set to the index of the new array entry in this case.
** **
** This routine returns a pointer to the array of objects. This ** Otherwise, if the realloc() fails, *pIdx is set to -1, *pnEntry remains
** might be the same as the pArray parameter or it might be a different ** unchanged and a copy of pArray returned.
** pointer if the array was resized.
*/ */
void *sqlite3ArrayAllocate( void *sqlite3ArrayAllocate(
sqlite3 *db, /* Connection to notify of malloc failures */ sqlite3 *db, /* Connection to notify of malloc failures */
void *pArray, /* Array of objects. Might be reallocated */ void *pArray, /* Array of objects. Might be reallocated */
int szEntry, /* Size of each object in the array */ int szEntry, /* Size of each object in the array */
int initSize, /* Suggested initial allocation, in elements */
int *pnEntry, /* Number of objects currently in use */ int *pnEntry, /* Number of objects currently in use */
int *pnAlloc, /* Current size of the allocation, in elements */
int *pIdx /* Write the index of a new slot here */ int *pIdx /* Write the index of a new slot here */
){ ){
char *z; char *z;
if( *pnEntry >= *pnAlloc ){ int n = *pnEntry;
void *pNew; if( (n & (n-1))==0 ){
int newSize; int sz = (n==0) ? 1 : 2*n;
newSize = (*pnAlloc)*2 + initSize; void *pNew = sqlite3DbRealloc(db, pArray, sz*szEntry);
pNew = sqlite3DbRealloc(db, pArray, newSize*szEntry);
if( pNew==0 ){ if( pNew==0 ){
*pIdx = -1; *pIdx = -1;
return pArray; return pArray;
} }
*pnAlloc = sqlite3DbMallocSize(db, pNew)/szEntry;
pArray = pNew; pArray = pNew;
} }
z = (char*)pArray; z = (char*)pArray;
memset(&z[*pnEntry * szEntry], 0, szEntry); memset(&z[n * szEntry], 0, szEntry);
*pIdx = *pnEntry; *pIdx = n;
++*pnEntry; ++*pnEntry;
return pArray; return pArray;
} }
@ -3095,15 +3099,12 @@ IdList *sqlite3IdListAppend(sqlite3 *db, IdList *pList, Token *pToken){
if( pList==0 ){ if( pList==0 ){
pList = sqlite3DbMallocZero(db, sizeof(IdList) ); pList = sqlite3DbMallocZero(db, sizeof(IdList) );
if( pList==0 ) return 0; if( pList==0 ) return 0;
pList->nAlloc = 0;
} }
pList->a = sqlite3ArrayAllocate( pList->a = sqlite3ArrayAllocate(
db, db,
pList->a, pList->a,
sizeof(pList->a[0]), sizeof(pList->a[0]),
5,
&pList->nId, &pList->nId,
&pList->nAlloc,
&i &i
); );
if( i<0 ){ if( i<0 ){

View File

@ -223,38 +223,57 @@ CollSeq *sqlite3FindCollSeq(
** that uses encoding enc. The value returned indicates how well the ** that uses encoding enc. The value returned indicates how well the
** request is matched. A higher value indicates a better match. ** request is matched. A higher value indicates a better match.
** **
** If nArg is -1 that means to only return a match (non-zero) if p->nArg
** is also -1. In other words, we are searching for a function that
** takes a variable number of arguments.
**
** If nArg is -2 that means that we are searching for any function
** regardless of the number of arguments it uses, so return a positive
** match score for any
**
** The returned value is always between 0 and 6, as follows: ** The returned value is always between 0 and 6, as follows:
** **
** 0: Not a match, or if nArg<0 and the function is has no implementation. ** 0: Not a match.
** 1: A variable arguments function that prefers UTF-8 when a UTF-16 ** 1: UTF8/16 conversion required and function takes any number of arguments.
** encoding is requested, or vice versa. ** 2: UTF16 byte order change required and function takes any number of args.
** 2: A variable arguments function that uses UTF-16BE when UTF-16LE is ** 3: encoding matches and function takes any number of arguments
** requested, or vice versa. ** 4: UTF8/16 conversion required - argument count matches exactly
** 3: A variable arguments function using the same text encoding. ** 5: UTF16 byte order conversion required - argument count matches exactly
** 4: A function with the exact number of arguments requested that ** 6: Perfect match: encoding and argument count match exactly.
** prefers UTF-8 when a UTF-16 encoding is requested, or vice versa.
** 5: A function with the exact number of arguments requested that
** prefers UTF-16LE when UTF-16BE is requested, or vice versa.
** 6: An exact match.
** **
** If nArg==(-2) then any function with a non-null xStep or xFunc is
** a perfect match and any function with both xStep and xFunc NULL is
** a non-match.
*/ */
static int matchQuality(FuncDef *p, int nArg, u8 enc){ #define FUNC_PERFECT_MATCH 6 /* The score for a perfect match */
int match = 0; static int matchQuality(
if( p->nArg==-1 || p->nArg==nArg FuncDef *p, /* The function we are evaluating for match quality */
|| (nArg==-1 && (p->xFunc!=0 || p->xStep!=0)) int nArg, /* Desired number of arguments. (-1)==any */
){ u8 enc /* Desired text encoding */
){
int match;
/* nArg of -2 is a special case */
if( nArg==(-2) ) return (p->xFunc==0 && p->xStep==0) ? 0 : FUNC_PERFECT_MATCH;
/* Wrong number of arguments means "no match" */
if( p->nArg!=nArg && p->nArg>=0 ) return 0;
/* Give a better score to a function with a specific number of arguments
** than to function that accepts any number of arguments. */
if( p->nArg==nArg ){
match = 4;
}else{
match = 1; match = 1;
if( p->nArg==nArg || nArg==-1 ){
match = 4;
}
if( enc==p->iPrefEnc ){
match += 2;
}
else if( (enc==SQLITE_UTF16LE && p->iPrefEnc==SQLITE_UTF16BE) ||
(enc==SQLITE_UTF16BE && p->iPrefEnc==SQLITE_UTF16LE) ){
match += 1;
}
} }
/* Bonus points if the text encoding matches */
if( enc==p->iPrefEnc ){
match += 2; /* Exact encoding match */
}else if( (enc & p->iPrefEnc & 2)!=0 ){
match += 1; /* Both are UTF16, but with different byte orders */
}
return match; return match;
} }
@ -310,13 +329,12 @@ void sqlite3FuncDefInsert(
** **
** If the createFlag argument is true, then a new (blank) FuncDef ** If the createFlag argument is true, then a new (blank) FuncDef
** structure is created and liked into the "db" structure if a ** structure is created and liked into the "db" structure if a
** no matching function previously existed. When createFlag is true ** no matching function previously existed.
** and the nArg parameter is -1, then only a function that accepts
** any number of arguments will be returned.
** **
** If createFlag is false and nArg is -1, then the first valid ** If nArg is -2, then the first valid function found is returned. A
** function found is returned. A function is valid if either xFunc ** function is valid if either xFunc or xStep is non-zero. The nArg==(-2)
** or xStep is non-zero. ** case is used to see if zName is a valid function name for some number
** of arguments. If nArg is -2, then createFlag must be 0.
** **
** If createFlag is false, then a function with the required name and ** If createFlag is false, then a function with the required name and
** number of arguments may be returned even if the eTextRep flag does not ** number of arguments may be returned even if the eTextRep flag does not
@ -328,14 +346,15 @@ FuncDef *sqlite3FindFunction(
int nName, /* Number of characters in the name */ int nName, /* Number of characters in the name */
int nArg, /* Number of arguments. -1 means any number */ int nArg, /* Number of arguments. -1 means any number */
u8 enc, /* Preferred text encoding */ u8 enc, /* Preferred text encoding */
int createFlag /* Create new entry if true and does not otherwise exist */ u8 createFlag /* Create new entry if true and does not otherwise exist */
){ ){
FuncDef *p; /* Iterator variable */ FuncDef *p; /* Iterator variable */
FuncDef *pBest = 0; /* Best match found so far */ FuncDef *pBest = 0; /* Best match found so far */
int bestScore = 0; /* Score of best match */ int bestScore = 0; /* Score of best match */
int h; /* Hash value */ int h; /* Hash value */
assert( nArg>=(-2) );
assert( nArg>=(-1) || createFlag==0 );
assert( enc==SQLITE_UTF8 || enc==SQLITE_UTF16LE || enc==SQLITE_UTF16BE ); assert( enc==SQLITE_UTF8 || enc==SQLITE_UTF16LE || enc==SQLITE_UTF16BE );
h = (sqlite3UpperToLower[(u8)zName[0]] + nName) % ArraySize(db->aFunc.a); h = (sqlite3UpperToLower[(u8)zName[0]] + nName) % ArraySize(db->aFunc.a);
@ -381,7 +400,7 @@ FuncDef *sqlite3FindFunction(
** exact match for the name, number of arguments and encoding, then add a ** exact match for the name, number of arguments and encoding, then add a
** new entry to the hash table and return it. ** new entry to the hash table and return it.
*/ */
if( createFlag && (bestScore<6 || pBest->nArg!=nArg) && if( createFlag && bestScore<FUNC_PERFECT_MATCH &&
(pBest = sqlite3DbMallocZero(db, sizeof(*pBest)+nName+1))!=0 ){ (pBest = sqlite3DbMallocZero(db, sizeof(*pBest)+nName+1))!=0 ){
pBest->zName = (char *)&pBest[1]; pBest->zName = (char *)&pBest[1];
pBest->nArg = (u16)nArg; pBest->nArg = (u16)nArg;

View File

@ -374,7 +374,7 @@ void sqlite3DeleteFrom(
pParse, pTabList, pWhere, 0, 0, WHERE_DUPLICATES_OK pParse, pTabList, pWhere, 0, 0, WHERE_DUPLICATES_OK
); );
if( pWInfo==0 ) goto delete_from_cleanup; if( pWInfo==0 ) goto delete_from_cleanup;
regRowid = sqlite3ExprCodeGetColumn(pParse, pTab, -1, iCur, iRowid); regRowid = sqlite3ExprCodeGetColumn(pParse, pTab, -1, iCur, iRowid, 0);
sqlite3VdbeAddOp2(v, OP_RowSetAdd, iRowSet, regRowid); sqlite3VdbeAddOp2(v, OP_RowSetAdd, iRowSet, regRowid);
if( db->flags & SQLITE_CountRows ){ if( db->flags & SQLITE_CountRows ){
sqlite3VdbeAddOp2(v, OP_AddImm, memCnt, 1); sqlite3VdbeAddOp2(v, OP_AddImm, memCnt, 1);

View File

@ -484,23 +484,55 @@ Expr *sqlite3PExpr(
Expr *pRight, /* Right operand */ Expr *pRight, /* Right operand */
const Token *pToken /* Argument token */ const Token *pToken /* Argument token */
){ ){
Expr *p = sqlite3ExprAlloc(pParse->db, op, pToken, 1); Expr *p;
sqlite3ExprAttachSubtrees(pParse->db, p, pLeft, pRight); if( op==TK_AND && pLeft && pRight ){
/* Take advantage of short-circuit false optimization for AND */
p = sqlite3ExprAnd(pParse->db, pLeft, pRight);
}else{
p = sqlite3ExprAlloc(pParse->db, op, pToken, 1);
sqlite3ExprAttachSubtrees(pParse->db, p, pLeft, pRight);
}
if( p ) { if( p ) {
sqlite3ExprCheckHeight(pParse, p->nHeight); sqlite3ExprCheckHeight(pParse, p->nHeight);
} }
return p; return p;
} }
/*
** Return 1 if an expression must be FALSE in all cases and 0 if the
** expression might be true. This is an optimization. If is OK to
** return 0 here even if the expression really is always false (a
** false negative). But it is a bug to return 1 if the expression
** might be true in some rare circumstances (a false positive.)
**
** Note that if the expression is part of conditional for a
** LEFT JOIN, then we cannot determine at compile-time whether or not
** is it true or false, so always return 0.
*/
static int exprAlwaysFalse(Expr *p){
int v = 0;
if( ExprHasProperty(p, EP_FromJoin) ) return 0;
if( !sqlite3ExprIsInteger(p, &v) ) return 0;
return v==0;
}
/* /*
** Join two expressions using an AND operator. If either expression is ** Join two expressions using an AND operator. If either expression is
** NULL, then just return the other expression. ** NULL, then just return the other expression.
**
** If one side or the other of the AND is known to be false, then instead
** of returning an AND expression, just return a constant expression with
** a value of false.
*/ */
Expr *sqlite3ExprAnd(sqlite3 *db, Expr *pLeft, Expr *pRight){ Expr *sqlite3ExprAnd(sqlite3 *db, Expr *pLeft, Expr *pRight){
if( pLeft==0 ){ if( pLeft==0 ){
return pRight; return pRight;
}else if( pRight==0 ){ }else if( pRight==0 ){
return pLeft; return pLeft;
}else if( exprAlwaysFalse(pLeft) || exprAlwaysFalse(pRight) ){
sqlite3ExprDelete(db, pLeft);
sqlite3ExprDelete(db, pRight);
return sqlite3ExprAlloc(db, TK_INTEGER, &sqlite3IntTokens[0], 0);
}else{ }else{
Expr *pNew = sqlite3ExprAlloc(db, TK_AND, 0, 0); Expr *pNew = sqlite3ExprAlloc(db, TK_AND, 0, 0);
sqlite3ExprAttachSubtrees(db, pNew, pLeft, pRight); sqlite3ExprAttachSubtrees(db, pNew, pLeft, pRight);
@ -856,8 +888,9 @@ ExprList *sqlite3ExprListDup(sqlite3 *db, ExprList *p, int flags){
pNew = sqlite3DbMallocRaw(db, sizeof(*pNew) ); pNew = sqlite3DbMallocRaw(db, sizeof(*pNew) );
if( pNew==0 ) return 0; if( pNew==0 ) return 0;
pNew->iECursor = 0; pNew->iECursor = 0;
pNew->nExpr = pNew->nAlloc = p->nExpr; pNew->nExpr = i = p->nExpr;
pNew->a = pItem = sqlite3DbMallocRaw(db, p->nExpr*sizeof(p->a[0]) ); if( (flags & EXPRDUP_REDUCE)==0 ) for(i=1; i<p->nExpr; i+=i){}
pNew->a = pItem = sqlite3DbMallocRaw(db, i*sizeof(p->a[0]) );
if( pItem==0 ){ if( pItem==0 ){
sqlite3DbFree(db, pNew); sqlite3DbFree(db, pNew);
return 0; return 0;
@ -925,12 +958,15 @@ IdList *sqlite3IdListDup(sqlite3 *db, IdList *p){
if( p==0 ) return 0; if( p==0 ) return 0;
pNew = sqlite3DbMallocRaw(db, sizeof(*pNew) ); pNew = sqlite3DbMallocRaw(db, sizeof(*pNew) );
if( pNew==0 ) return 0; if( pNew==0 ) return 0;
pNew->nId = pNew->nAlloc = p->nId; pNew->nId = p->nId;
pNew->a = sqlite3DbMallocRaw(db, p->nId*sizeof(p->a[0]) ); pNew->a = sqlite3DbMallocRaw(db, p->nId*sizeof(p->a[0]) );
if( pNew->a==0 ){ if( pNew->a==0 ){
sqlite3DbFree(db, pNew); sqlite3DbFree(db, pNew);
return 0; return 0;
} }
/* Note that because the size of the allocation for p->a[] is not
** necessarily a power of two, sqlite3IdListAppend() may not be called
** on the duplicate created by this function. */
for(i=0; i<p->nId; i++){ for(i=0; i<p->nId; i++){
struct IdList_item *pNewItem = &pNew->a[i]; struct IdList_item *pNewItem = &pNew->a[i];
struct IdList_item *pOldItem = &p->a[i]; struct IdList_item *pOldItem = &p->a[i];
@ -992,17 +1028,16 @@ ExprList *sqlite3ExprListAppend(
if( pList==0 ){ if( pList==0 ){
goto no_mem; goto no_mem;
} }
assert( pList->nAlloc==0 ); pList->a = sqlite3DbMallocRaw(db, sizeof(pList->a[0]));
} if( pList->a==0 ) goto no_mem;
if( pList->nAlloc<=pList->nExpr ){ }else if( (pList->nExpr & (pList->nExpr-1))==0 ){
struct ExprList_item *a; struct ExprList_item *a;
int n = pList->nAlloc*2 + 4; assert( pList->nExpr>0 );
a = sqlite3DbRealloc(db, pList->a, n*sizeof(pList->a[0])); a = sqlite3DbRealloc(db, pList->a, pList->nExpr*2*sizeof(pList->a[0]));
if( a==0 ){ if( a==0 ){
goto no_mem; goto no_mem;
} }
pList->a = a; pList->a = a;
pList->nAlloc = sqlite3DbMallocSize(db, a)/sizeof(a[0]);
} }
assert( pList->a!=0 ); assert( pList->a!=0 );
if( 1 ){ if( 1 ){
@ -1093,8 +1128,7 @@ void sqlite3ExprListDelete(sqlite3 *db, ExprList *pList){
int i; int i;
struct ExprList_item *pItem; struct ExprList_item *pItem;
if( pList==0 ) return; if( pList==0 ) return;
assert( pList->a!=0 || (pList->nExpr==0 && pList->nAlloc==0) ); assert( pList->a!=0 || pList->nExpr==0 );
assert( pList->nExpr<=pList->nAlloc );
for(pItem=pList->a, i=0; i<pList->nExpr; i++, pItem++){ for(pItem=pList->a, i=0; i<pList->nExpr; i++, pItem++){
sqlite3ExprDelete(db, pItem->pExpr); sqlite3ExprDelete(db, pItem->pExpr);
sqlite3DbFree(db, pItem->zName); sqlite3DbFree(db, pItem->zName);
@ -2030,15 +2064,6 @@ void sqlite3ExprCacheStore(Parse *pParse, int iTab, int iCol, int iReg){
*/ */
#ifndef NDEBUG #ifndef NDEBUG
for(i=0, p=pParse->aColCache; i<SQLITE_N_COLCACHE; i++, p++){ for(i=0, p=pParse->aColCache; i<SQLITE_N_COLCACHE; i++, p++){
#if 0 /* This code wold remove the entry from the cache if it existed */
if( p->iReg && p->iTable==iTab && p->iColumn==iCol ){
cacheEntryClear(pParse, p);
p->iLevel = pParse->iCacheLevel;
p->iReg = iReg;
p->lru = pParse->iCacheCnt++;
return;
}
#endif
assert( p->iReg==0 || p->iTable!=iTab || p->iColumn!=iCol ); assert( p->iReg==0 || p->iTable!=iTab || p->iColumn!=iCol );
} }
#endif #endif
@ -2173,7 +2198,8 @@ int sqlite3ExprCodeGetColumn(
Table *pTab, /* Description of the table we are reading from */ Table *pTab, /* Description of the table we are reading from */
int iColumn, /* Index of the table column */ int iColumn, /* Index of the table column */
int iTable, /* The cursor pointing to the table */ int iTable, /* The cursor pointing to the table */
int iReg /* Store results here */ int iReg, /* Store results here */
u8 p5 /* P5 value for OP_Column */
){ ){
Vdbe *v = pParse->pVdbe; Vdbe *v = pParse->pVdbe;
int i; int i;
@ -2188,7 +2214,11 @@ int sqlite3ExprCodeGetColumn(
} }
assert( v!=0 ); assert( v!=0 );
sqlite3ExprCodeGetColumnOfTable(v, pTab, iTable, iColumn, iReg); sqlite3ExprCodeGetColumnOfTable(v, pTab, iTable, iColumn, iReg);
sqlite3ExprCacheStore(pParse, iTable, iColumn, iReg); if( p5 ){
sqlite3VdbeChangeP5(v, p5);
}else{
sqlite3ExprCacheStore(pParse, iTable, iColumn, iReg);
}
return iReg; return iReg;
} }
@ -2316,7 +2346,8 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){
inReg = pExpr->iColumn + pParse->ckBase; inReg = pExpr->iColumn + pParse->ckBase;
}else{ }else{
inReg = sqlite3ExprCodeGetColumn(pParse, pExpr->pTab, inReg = sqlite3ExprCodeGetColumn(pParse, pExpr->pTab,
pExpr->iColumn, pExpr->iTable, target); pExpr->iColumn, pExpr->iTable, target,
pExpr->op2);
} }
break; break;
} }
@ -2593,6 +2624,25 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){
if( pFarg ){ if( pFarg ){
r1 = sqlite3GetTempRange(pParse, nFarg); r1 = sqlite3GetTempRange(pParse, nFarg);
/* For length() and typeof() functions with a column argument,
** set the P5 parameter to the OP_Column opcode to OPFLAG_LENGTHARG
** or OPFLAG_TYPEOFARG respectively, to avoid unnecessary data
** loading.
*/
if( (pDef->flags & (SQLITE_FUNC_LENGTH|SQLITE_FUNC_TYPEOF))!=0 ){
u8 exprOp;
assert( nFarg==1 );
assert( pFarg->a[0].pExpr!=0 );
exprOp = pFarg->a[0].pExpr->op;
if( exprOp==TK_COLUMN || exprOp==TK_AGG_COLUMN ){
assert( SQLITE_FUNC_LENGTH==OPFLAG_LENGTHARG );
assert( SQLITE_FUNC_TYPEOF==OPFLAG_TYPEOFARG );
testcase( pDef->flags==SQLITE_FUNC_LENGTH );
pFarg->a[0].pExpr->op2 = pDef->flags;
}
}
sqlite3ExprCachePush(pParse); /* Ticket 2ea2425d34be */ sqlite3ExprCachePush(pParse); /* Ticket 2ea2425d34be */
sqlite3ExprCodeExprList(pParse, pFarg, r1, 1); sqlite3ExprCodeExprList(pParse, pFarg, r1, 1);
sqlite3ExprCachePop(pParse, 1); /* Ticket 2ea2425d34be */ sqlite3ExprCachePop(pParse, 1); /* Ticket 2ea2425d34be */
@ -3728,7 +3778,7 @@ int sqlite3ExprCompare(Expr *pA, Expr *pB){
if( !ExprHasProperty(pB, EP_IntValue) || pA->u.iValue!=pB->u.iValue ){ if( !ExprHasProperty(pB, EP_IntValue) || pA->u.iValue!=pB->u.iValue ){
return 2; return 2;
} }
}else if( pA->op!=TK_COLUMN && pA->u.zToken ){ }else if( pA->op!=TK_COLUMN && pA->op!=TK_AGG_COLUMN && pA->u.zToken ){
if( ExprHasProperty(pB, EP_IntValue) || NEVER(pB->u.zToken==0) ) return 2; if( ExprHasProperty(pB, EP_IntValue) || NEVER(pB->u.zToken==0) ) return 2;
if( strcmp(pA->u.zToken,pB->u.zToken)!=0 ){ if( strcmp(pA->u.zToken,pB->u.zToken)!=0 ){
return 2; return 2;
@ -3765,6 +3815,41 @@ int sqlite3ExprListCompare(ExprList *pA, ExprList *pB){
return 0; return 0;
} }
/*
** This is the expression callback for sqlite3FunctionUsesOtherSrc().
**
** Determine if an expression references any table other than one of the
** tables in pWalker->u.pSrcList and abort if it does.
*/
static int exprUsesOtherSrc(Walker *pWalker, Expr *pExpr){
if( pExpr->op==TK_COLUMN || pExpr->op==TK_AGG_COLUMN ){
int i;
SrcList *pSrc = pWalker->u.pSrcList;
for(i=0; i<pSrc->nSrc; i++){
if( pExpr->iTable==pSrc->a[i].iCursor ) return WRC_Continue;
}
return WRC_Abort;
}else{
return WRC_Continue;
}
}
/*
** Determine if any of the arguments to the pExpr Function references
** any SrcList other than pSrcList. Return true if they do. Return
** false if pExpr has no argument or has only constant arguments or
** only references tables named in pSrcList.
*/
static int sqlite3FunctionUsesOtherSrc(Expr *pExpr, SrcList *pSrcList){
Walker w;
assert( pExpr->op==TK_AGG_FUNCTION );
memset(&w, 0, sizeof(w));
w.xExprCallback = exprUsesOtherSrc;
w.u.pSrcList = pSrcList;
if( sqlite3WalkExprList(&w, pExpr->x.pList)!=WRC_Continue ) return 1;
return 0;
}
/* /*
** Add a new element to the pAggInfo->aCol[] array. Return the index of ** Add a new element to the pAggInfo->aCol[] array. Return the index of
** the new element. Return a negative number if malloc fails. ** the new element. Return a negative number if malloc fails.
@ -3775,9 +3860,7 @@ static int addAggInfoColumn(sqlite3 *db, AggInfo *pInfo){
db, db,
pInfo->aCol, pInfo->aCol,
sizeof(pInfo->aCol[0]), sizeof(pInfo->aCol[0]),
3,
&pInfo->nColumn, &pInfo->nColumn,
&pInfo->nColumnAlloc,
&i &i
); );
return i; return i;
@ -3793,9 +3876,7 @@ static int addAggInfoFunc(sqlite3 *db, AggInfo *pInfo){
db, db,
pInfo->aFunc, pInfo->aFunc,
sizeof(pInfo->aFunc[0]), sizeof(pInfo->aFunc[0]),
3,
&pInfo->nFunc, &pInfo->nFunc,
&pInfo->nFuncAlloc,
&i &i
); );
return i; return i;
@ -3884,9 +3965,7 @@ static int analyzeAggregate(Walker *pWalker, Expr *pExpr){
return WRC_Prune; return WRC_Prune;
} }
case TK_AGG_FUNCTION: { case TK_AGG_FUNCTION: {
/* The pNC->nDepth==0 test causes aggregate functions in subqueries if( !sqlite3FunctionUsesOtherSrc(pExpr, pSrcList) ){
** to be ignored */
if( pNC->nDepth==0 ){
/* Check to see if pExpr is a duplicate of another aggregate /* Check to see if pExpr is a duplicate of another aggregate
** function that is already in the pAggInfo structure ** function that is already in the pAggInfo structure
*/ */
@ -3930,15 +4009,9 @@ static int analyzeAggregate(Walker *pWalker, Expr *pExpr){
return WRC_Continue; return WRC_Continue;
} }
static int analyzeAggregatesInSelect(Walker *pWalker, Select *pSelect){ static int analyzeAggregatesInSelect(Walker *pWalker, Select *pSelect){
NameContext *pNC = pWalker->u.pNC; UNUSED_PARAMETER(pWalker);
if( pNC->nDepth==0 ){ UNUSED_PARAMETER(pSelect);
pNC->nDepth++; return WRC_Continue;
sqlite3WalkSelect(pWalker, pSelect);
pNC->nDepth--;
return WRC_Prune;
}else{
return WRC_Continue;
}
} }
/* /*
@ -3951,6 +4024,7 @@ static int analyzeAggregatesInSelect(Walker *pWalker, Select *pSelect){
*/ */
void sqlite3ExprAnalyzeAggregates(NameContext *pNC, Expr *pExpr){ void sqlite3ExprAnalyzeAggregates(NameContext *pNC, Expr *pExpr){
Walker w; Walker w;
memset(&w, 0, sizeof(w));
w.xExprCallback = analyzeAggregate; w.xExprCallback = analyzeAggregate;
w.xSelectCallback = analyzeAggregatesInSelect; w.xSelectCallback = analyzeAggregatesInSelect;
w.u.pNC = pNC; w.u.pNC = pNC;

View File

@ -28,6 +28,14 @@ static CollSeq *sqlite3GetFuncCollSeq(sqlite3_context *context){
return context->pColl; return context->pColl;
} }
/*
** Indicate that the accumulator load should be skipped on this
** iteration of the aggregate loop.
*/
static void sqlite3SkipAccumulatorLoad(sqlite3_context *context){
context->skipFlag = 1;
}
/* /*
** Implementation of the non-aggregate min() and max() functions ** Implementation of the non-aggregate min() and max() functions
*/ */
@ -408,7 +416,7 @@ static void randomFunc(
** 2s complement of that positive value. The end result can ** 2s complement of that positive value. The end result can
** therefore be no less than -9223372036854775807. ** therefore be no less than -9223372036854775807.
*/ */
r = -(r ^ (((sqlite3_int64)1)<<63)); r = -(r & LARGEST_INT64);
} }
sqlite3_result_int64(context, r); sqlite3_result_int64(context, r);
} }
@ -1334,11 +1342,12 @@ static void minmaxStep(
Mem *pBest; Mem *pBest;
UNUSED_PARAMETER(NotUsed); UNUSED_PARAMETER(NotUsed);
if( sqlite3_value_type(argv[0])==SQLITE_NULL ) return;
pBest = (Mem *)sqlite3_aggregate_context(context, sizeof(*pBest)); pBest = (Mem *)sqlite3_aggregate_context(context, sizeof(*pBest));
if( !pBest ) return; if( !pBest ) return;
if( pBest->flags ){ if( sqlite3_value_type(argv[0])==SQLITE_NULL ){
if( pBest->flags ) sqlite3SkipAccumulatorLoad(context);
}else if( pBest->flags ){
int max; int max;
int cmp; int cmp;
CollSeq *pColl = sqlite3GetFuncCollSeq(context); CollSeq *pColl = sqlite3GetFuncCollSeq(context);
@ -1354,6 +1363,8 @@ static void minmaxStep(
cmp = sqlite3MemCompare(pBest, pArg, pColl); cmp = sqlite3MemCompare(pBest, pArg, pColl);
if( (max && cmp<0) || (!max && cmp>0) ){ if( (max && cmp<0) || (!max && cmp>0) ){
sqlite3VdbeMemCopy(pBest, pArg); sqlite3VdbeMemCopy(pBest, pArg);
}else{
sqlite3SkipAccumulatorLoad(context);
} }
}else{ }else{
sqlite3VdbeMemCopy(pBest, pArg); sqlite3VdbeMemCopy(pBest, pArg);
@ -1363,7 +1374,7 @@ static void minMaxFinalize(sqlite3_context *context){
sqlite3_value *pRes; sqlite3_value *pRes;
pRes = (sqlite3_value *)sqlite3_aggregate_context(context, 0); pRes = (sqlite3_value *)sqlite3_aggregate_context(context, 0);
if( pRes ){ if( pRes ){
if( ALWAYS(pRes->flags) ){ if( pRes->flags ){
sqlite3_result_value(context, pRes); sqlite3_result_value(context, pRes);
} }
sqlite3VdbeMemRelease(pRes); sqlite3VdbeMemRelease(pRes);
@ -1537,8 +1548,8 @@ void sqlite3RegisterGlobalFunctions(void){
FUNCTION(max, -1, 1, 1, minmaxFunc ), FUNCTION(max, -1, 1, 1, minmaxFunc ),
FUNCTION(max, 0, 1, 1, 0 ), FUNCTION(max, 0, 1, 1, 0 ),
AGGREGATE(max, 1, 1, 1, minmaxStep, minMaxFinalize ), AGGREGATE(max, 1, 1, 1, minmaxStep, minMaxFinalize ),
FUNCTION(typeof, 1, 0, 0, typeofFunc ), FUNCTION2(typeof, 1, 0, 0, typeofFunc, SQLITE_FUNC_TYPEOF),
FUNCTION(length, 1, 0, 0, lengthFunc ), FUNCTION2(length, 1, 0, 0, lengthFunc, SQLITE_FUNC_LENGTH),
FUNCTION(substr, 2, 0, 0, substrFunc ), FUNCTION(substr, 2, 0, 0, substrFunc ),
FUNCTION(substr, 3, 0, 0, substrFunc ), FUNCTION(substr, 3, 0, 0, substrFunc ),
FUNCTION(abs, 1, 0, 0, absFunc ), FUNCTION(abs, 1, 0, 0, absFunc ),
@ -1550,11 +1561,9 @@ void sqlite3RegisterGlobalFunctions(void){
FUNCTION(lower, 1, 0, 0, lowerFunc ), FUNCTION(lower, 1, 0, 0, lowerFunc ),
FUNCTION(coalesce, 1, 0, 0, 0 ), FUNCTION(coalesce, 1, 0, 0, 0 ),
FUNCTION(coalesce, 0, 0, 0, 0 ), FUNCTION(coalesce, 0, 0, 0, 0 ),
/* FUNCTION(coalesce, -1, 0, 0, ifnullFunc ), */ FUNCTION2(coalesce, -1, 0, 0, ifnullFunc, SQLITE_FUNC_COALESCE),
{-1,SQLITE_UTF8,SQLITE_FUNC_COALESCE,0,0,ifnullFunc,0,0,"coalesce",0,0},
FUNCTION(hex, 1, 0, 0, hexFunc ), FUNCTION(hex, 1, 0, 0, hexFunc ),
/* FUNCTION(ifnull, 2, 0, 0, ifnullFunc ), */ FUNCTION2(ifnull, 2, 0, 0, ifnullFunc, SQLITE_FUNC_COALESCE),
{2,SQLITE_UTF8,SQLITE_FUNC_COALESCE,0,0,ifnullFunc,0,0,"ifnull",0,0},
FUNCTION(random, 0, 0, 0, randomFunc ), FUNCTION(random, 0, 0, 0, randomFunc ),
FUNCTION(randomblob, 1, 0, 0, randomBlob ), FUNCTION(randomblob, 1, 0, 0, randomBlob ),
FUNCTION(nullif, 2, 0, 1, nullifFunc ), FUNCTION(nullif, 2, 0, 1, nullifFunc ),

View File

@ -1157,9 +1157,11 @@ void sqlite3GenerateConstraintChecks(
int regData; /* Register containing first data column */ int regData; /* Register containing first data column */
int iCur; /* Table cursor number */ int iCur; /* Table cursor number */
Index *pIdx; /* Pointer to one of the indices */ Index *pIdx; /* Pointer to one of the indices */
sqlite3 *db; /* Database connection */
int seenReplace = 0; /* True if REPLACE is used to resolve INT PK conflict */ int seenReplace = 0; /* True if REPLACE is used to resolve INT PK conflict */
int regOldRowid = (rowidChng && isUpdate) ? rowidChng : regRowid; int regOldRowid = (rowidChng && isUpdate) ? rowidChng : regRowid;
db = pParse->db;
v = sqlite3GetVdbe(pParse); v = sqlite3GetVdbe(pParse);
assert( v!=0 ); assert( v!=0 );
assert( pTab->pSelect==0 ); /* This table is not a VIEW */ assert( pTab->pSelect==0 ); /* This table is not a VIEW */
@ -1192,7 +1194,7 @@ void sqlite3GenerateConstraintChecks(
char *zMsg; char *zMsg;
sqlite3VdbeAddOp3(v, OP_HaltIfNull, sqlite3VdbeAddOp3(v, OP_HaltIfNull,
SQLITE_CONSTRAINT, onError, regData+i); SQLITE_CONSTRAINT, onError, regData+i);
zMsg = sqlite3MPrintf(pParse->db, "%s.%s may not be NULL", zMsg = sqlite3MPrintf(db, "%s.%s may not be NULL",
pTab->zName, pTab->aCol[i].zName); pTab->zName, pTab->aCol[i].zName);
sqlite3VdbeChangeP4(v, -1, zMsg, P4_DYNAMIC); sqlite3VdbeChangeP4(v, -1, zMsg, P4_DYNAMIC);
break; break;
@ -1214,18 +1216,27 @@ void sqlite3GenerateConstraintChecks(
/* Test all CHECK constraints /* Test all CHECK constraints
*/ */
#ifndef SQLITE_OMIT_CHECK #ifndef SQLITE_OMIT_CHECK
if( pTab->pCheck && (pParse->db->flags & SQLITE_IgnoreChecks)==0 ){ if( pTab->pCheck && (db->flags & SQLITE_IgnoreChecks)==0 ){
int allOk = sqlite3VdbeMakeLabel(v); ExprList *pCheck = pTab->pCheck;
pParse->ckBase = regData; pParse->ckBase = regData;
sqlite3ExprIfTrue(pParse, pTab->pCheck, allOk, SQLITE_JUMPIFNULL);
onError = overrideError!=OE_Default ? overrideError : OE_Abort; onError = overrideError!=OE_Default ? overrideError : OE_Abort;
if( onError==OE_Ignore ){ for(i=0; i<pCheck->nExpr; i++){
sqlite3VdbeAddOp2(v, OP_Goto, 0, ignoreDest); int allOk = sqlite3VdbeMakeLabel(v);
}else{ sqlite3ExprIfTrue(pParse, pCheck->a[i].pExpr, allOk, SQLITE_JUMPIFNULL);
if( onError==OE_Replace ) onError = OE_Abort; /* IMP: R-15569-63625 */ if( onError==OE_Ignore ){
sqlite3HaltConstraint(pParse, onError, 0, 0); sqlite3VdbeAddOp2(v, OP_Goto, 0, ignoreDest);
}else{
char *zConsName = pCheck->a[i].zName;
if( onError==OE_Replace ) onError = OE_Abort; /* IMP: R-15569-63625 */
if( zConsName ){
zConsName = sqlite3MPrintf(db, "constraint %s failed", zConsName);
}else{
zConsName = 0;
}
sqlite3HaltConstraint(pParse, onError, zConsName, P4_DYNAMIC);
}
sqlite3VdbeResolveLabel(v, allOk);
} }
sqlite3VdbeResolveLabel(v, allOk);
} }
#endif /* !defined(SQLITE_OMIT_CHECK) */ #endif /* !defined(SQLITE_OMIT_CHECK) */
@ -1281,7 +1292,7 @@ void sqlite3GenerateConstraintChecks(
** table. ** table.
*/ */
Trigger *pTrigger = 0; Trigger *pTrigger = 0;
if( pParse->db->flags&SQLITE_RecTriggers ){ if( db->flags&SQLITE_RecTriggers ){
pTrigger = sqlite3TriggersExist(pParse, pTab, TK_DELETE, 0, 0); pTrigger = sqlite3TriggersExist(pParse, pTab, TK_DELETE, 0, 0);
} }
if( pTrigger || sqlite3FkRequired(pParse, pTab, 0, 0) ){ if( pTrigger || sqlite3FkRequired(pParse, pTab, 0, 0) ){
@ -1370,7 +1381,7 @@ void sqlite3GenerateConstraintChecks(
char *zErr; char *zErr;
sqlite3StrAccumInit(&errMsg, 0, 0, 200); sqlite3StrAccumInit(&errMsg, 0, 0, 200);
errMsg.db = pParse->db; errMsg.db = db;
zSep = pIdx->nColumn>1 ? "columns " : "column "; zSep = pIdx->nColumn>1 ? "columns " : "column ";
for(j=0; j<pIdx->nColumn; j++){ for(j=0; j<pIdx->nColumn; j++){
char *zCol = pTab->aCol[pIdx->aiColumn[j]].zName; char *zCol = pTab->aCol[pIdx->aiColumn[j]].zName;
@ -1394,7 +1405,7 @@ void sqlite3GenerateConstraintChecks(
Trigger *pTrigger = 0; Trigger *pTrigger = 0;
assert( onError==OE_Replace ); assert( onError==OE_Replace );
sqlite3MultiWrite(pParse); sqlite3MultiWrite(pParse);
if( pParse->db->flags&SQLITE_RecTriggers ){ if( db->flags&SQLITE_RecTriggers ){
pTrigger = sqlite3TriggersExist(pParse, pTab, TK_DELETE, 0, 0); pTrigger = sqlite3TriggersExist(pParse, pTab, TK_DELETE, 0, 0);
} }
sqlite3GenerateRowDelete( sqlite3GenerateRowDelete(
@ -1724,7 +1735,7 @@ static int xferOptimization(
} }
} }
#ifndef SQLITE_OMIT_CHECK #ifndef SQLITE_OMIT_CHECK
if( pDest->pCheck && sqlite3ExprCompare(pSrc->pCheck, pDest->pCheck) ){ if( pDest->pCheck && sqlite3ExprListCompare(pSrc->pCheck, pDest->pCheck) ){
return 0; /* Tables have different CHECK constraints. Ticket #2252 */ return 0; /* Tables have different CHECK constraints. Ticket #2252 */
} }
#endif #endif

View File

@ -849,19 +849,23 @@ int sqlite3_close(sqlite3 *db){
} }
/* /*
** Rollback all database files. ** Rollback all database files. If tripCode is not SQLITE_OK, then
** any open cursors are invalidated ("tripped" - as in "tripping a circuit
** breaker") and made to return tripCode if there are any further
** attempts to use that cursor.
*/ */
void sqlite3RollbackAll(sqlite3 *db){ void sqlite3RollbackAll(sqlite3 *db, int tripCode){
int i; int i;
int inTrans = 0; int inTrans = 0;
assert( sqlite3_mutex_held(db->mutex) ); assert( sqlite3_mutex_held(db->mutex) );
sqlite3BeginBenignMalloc(); sqlite3BeginBenignMalloc();
for(i=0; i<db->nDb; i++){ for(i=0; i<db->nDb; i++){
if( db->aDb[i].pBt ){ Btree *p = db->aDb[i].pBt;
if( sqlite3BtreeIsInTrans(db->aDb[i].pBt) ){ if( p ){
if( sqlite3BtreeIsInTrans(p) ){
inTrans = 1; inTrans = 1;
} }
sqlite3BtreeRollback(db->aDb[i].pBt); sqlite3BtreeRollback(p, tripCode);
db->aDb[i].inTrans = 0; db->aDb[i].inTrans = 0;
} }
} }
@ -916,12 +920,21 @@ const char *sqlite3ErrStr(int rc){
/* SQLITE_RANGE */ "bind or column index out of range", /* SQLITE_RANGE */ "bind or column index out of range",
/* SQLITE_NOTADB */ "file is encrypted or is not a database", /* SQLITE_NOTADB */ "file is encrypted or is not a database",
}; };
rc &= 0xff; const char *zErr = "unknown error";
if( ALWAYS(rc>=0) && rc<(int)(sizeof(aMsg)/sizeof(aMsg[0])) && aMsg[rc]!=0 ){ switch( rc ){
return aMsg[rc]; case SQLITE_ABORT_ROLLBACK: {
}else{ zErr = "abort due to ROLLBACK";
return "unknown error"; break;
}
default: {
rc &= 0xff;
if( ALWAYS(rc>=0) && rc<ArraySize(aMsg) && aMsg[rc]!=0 ){
zErr = aMsg[rc];
}
break;
}
} }
return zErr;
} }
/* /*
@ -1299,9 +1312,8 @@ void *sqlite3_profile(
} }
#endif /* SQLITE_OMIT_TRACE */ #endif /* SQLITE_OMIT_TRACE */
/*** EXPERIMENTAL *** /*
** ** Register a function to be invoked when a transaction commits.
** Register a function to be invoked when a transaction comments.
** If the invoked function returns non-zero, then the commit becomes a ** If the invoked function returns non-zero, then the commit becomes a
** rollback. ** rollback.
*/ */
@ -2692,35 +2704,27 @@ int sqlite3_extended_result_codes(sqlite3 *db, int onoff){
*/ */
int sqlite3_file_control(sqlite3 *db, const char *zDbName, int op, void *pArg){ int sqlite3_file_control(sqlite3 *db, const char *zDbName, int op, void *pArg){
int rc = SQLITE_ERROR; int rc = SQLITE_ERROR;
int iDb; Btree *pBtree;
sqlite3_mutex_enter(db->mutex); sqlite3_mutex_enter(db->mutex);
if( zDbName==0 ){ pBtree = sqlite3DbNameToBtree(db, zDbName);
iDb = 0; if( pBtree ){
}else{ Pager *pPager;
for(iDb=0; iDb<db->nDb; iDb++){ sqlite3_file *fd;
if( strcmp(db->aDb[iDb].zName, zDbName)==0 ) break; sqlite3BtreeEnter(pBtree);
} pPager = sqlite3BtreePager(pBtree);
} assert( pPager!=0 );
if( iDb<db->nDb ){ fd = sqlite3PagerFile(pPager);
Btree *pBtree = db->aDb[iDb].pBt; assert( fd!=0 );
if( pBtree ){ if( op==SQLITE_FCNTL_FILE_POINTER ){
Pager *pPager; *(sqlite3_file**)pArg = fd;
sqlite3_file *fd; rc = SQLITE_OK;
sqlite3BtreeEnter(pBtree); }else if( fd->pMethods ){
pPager = sqlite3BtreePager(pBtree); rc = sqlite3OsFileControl(fd, op, pArg);
assert( pPager!=0 ); }else{
fd = sqlite3PagerFile(pPager); rc = SQLITE_NOTFOUND;
assert( fd!=0 );
if( op==SQLITE_FCNTL_FILE_POINTER ){
*(sqlite3_file**)pArg = fd;
rc = SQLITE_OK;
}else if( fd->pMethods ){
rc = sqlite3OsFileControl(fd, op, pArg);
}else{
rc = SQLITE_NOTFOUND;
}
sqlite3BtreeLeave(pBtree);
} }
sqlite3BtreeLeave(pBtree);
} }
sqlite3_mutex_leave(db->mutex); sqlite3_mutex_leave(db->mutex);
return rc; return rc;
@ -2995,7 +2999,8 @@ const char *sqlite3_uri_parameter(const char *zFilename, const char *zParam){
*/ */
int sqlite3_uri_boolean(const char *zFilename, const char *zParam, int bDflt){ int sqlite3_uri_boolean(const char *zFilename, const char *zParam, int bDflt){
const char *z = sqlite3_uri_parameter(zFilename, zParam); const char *z = sqlite3_uri_parameter(zFilename, zParam);
return z ? sqlite3GetBoolean(z) : (bDflt!=0); bDflt = bDflt!=0;
return z ? sqlite3GetBoolean(z, bDflt) : bDflt;
} }
/* /*
@ -3015,15 +3020,34 @@ sqlite3_int64 sqlite3_uri_int64(
} }
/* /*
** Return the filename of the database associated with a database ** Return the Btree pointer identified by zDbName. Return NULL if not found.
** connection.
*/ */
const char *sqlite3_db_filename(sqlite3 *db, const char *zDbName){ Btree *sqlite3DbNameToBtree(sqlite3 *db, const char *zDbName){
int i; int i;
for(i=0; i<db->nDb; i++){ for(i=0; i<db->nDb; i++){
if( db->aDb[i].pBt && sqlite3StrICmp(zDbName, db->aDb[i].zName)==0 ){ if( db->aDb[i].pBt
return sqlite3BtreeGetFilename(db->aDb[i].pBt); && (zDbName==0 || sqlite3StrICmp(zDbName, db->aDb[i].zName)==0)
){
return db->aDb[i].pBt;
} }
} }
return 0; return 0;
} }
/*
** Return the filename of the database associated with a database
** connection.
*/
const char *sqlite3_db_filename(sqlite3 *db, const char *zDbName){
Btree *pBt = sqlite3DbNameToBtree(db, zDbName);
return pBt ? sqlite3BtreeGetFilename(pBt) : 0;
}
/*
** Return 1 if database is read-only or 0 if read/write. Return -1 if
** no such database exists.
*/
int sqlite3_db_readonly(sqlite3 *db, const char *zDbName){
Btree *pBt = sqlite3DbNameToBtree(db, zDbName);
return pBt ? sqlite3PagerIsreadonly(sqlite3BtreePager(pBt)) : -1;
}

View File

@ -15,7 +15,31 @@
** to obtain the memory it needs. ** to obtain the memory it needs.
** **
** This file contains implementations of the low-level memory allocation ** This file contains implementations of the low-level memory allocation
** routines specified in the sqlite3_mem_methods object. ** routines specified in the sqlite3_mem_methods object. The content of
** this file is only used if SQLITE_SYSTEM_MALLOC is defined. The
** SQLITE_SYSTEM_MALLOC macro is defined automatically if neither the
** SQLITE_MEMDEBUG nor the SQLITE_WIN32_MALLOC macros are defined. The
** default configuration is to use memory allocation routines in this
** file.
**
** C-preprocessor macro summary:
**
** HAVE_MALLOC_USABLE_SIZE The configure script sets this symbol if
** the malloc_usable_size() interface exists
** on the target platform. Or, this symbol
** can be set manually, if desired.
** If an equivalent interface exists by
** a different name, using a separate -D
** option to rename it.
**
** SQLITE_WITHOUT_ZONEMALLOC Some older macs lack support for the zone
** memory allocator. Set this symbol to enable
** building on older macs.
**
** SQLITE_WITHOUT_MSIZE Set this symbol to disable the use of
** _msize() on windows systems. This might
** be necessary when compiling for Delphi,
** for example.
*/ */
#include "sqliteInt.h" #include "sqliteInt.h"
@ -27,17 +51,19 @@
#ifdef SQLITE_SYSTEM_MALLOC #ifdef SQLITE_SYSTEM_MALLOC
/* /*
** Windows systems have malloc_usable_size() but it is called _msize() ** The MSVCRT has malloc_usable_size() but it is called _msize().
** The use of _msize() is automatic, but can be disabled by compiling
** with -DSQLITE_WITHOUT_MSIZE
*/ */
#if !defined(HAVE_MALLOC_USABLE_SIZE) && SQLITE_OS_WIN #if defined(_MSC_VER) && !defined(SQLITE_WITHOUT_MSIZE)
# define HAVE_MALLOC_USABLE_SIZE 1 # define SQLITE_MALLOCSIZE _msize
# define malloc_usable_size _msize
#endif #endif
#if defined(__APPLE__) #if defined(__APPLE__) && !defined(SQLITE_WITHOUT_ZONEMALLOC)
/* /*
** Use the zone allocator available on apple products ** Use the zone allocator available on apple products unless the
** SQLITE_WITHOUT_ZONEMALLOC symbol is defined.
*/ */
#include <sys/sysctl.h> #include <sys/sysctl.h>
#include <malloc/malloc.h> #include <malloc/malloc.h>
@ -52,17 +78,23 @@ static malloc_zone_t* _sqliteZone_;
#else /* if not __APPLE__ */ #else /* if not __APPLE__ */
/* /*
** Use standard C library malloc and free on non-Apple systems. ** Use standard C library malloc and free on non-Apple systems.
** Also used by Apple systems if SQLITE_WITHOUT_ZONEMALLOC is defined.
*/ */
#define SQLITE_MALLOC(x) malloc(x) #define SQLITE_MALLOC(x) malloc(x)
#define SQLITE_FREE(x) free(x) #define SQLITE_FREE(x) free(x)
#define SQLITE_REALLOC(x,y) realloc((x),(y)) #define SQLITE_REALLOC(x,y) realloc((x),(y))
#if (defined(_MSC_VER) && !defined(SQLITE_WITHOUT_MSIZE)) \
|| (defined(HAVE_MALLOC_H) && defined(HAVE_MALLOC_USABLE_SIZE))
# include <malloc.h> /* Needed for malloc_usable_size on linux */
#endif
#ifdef HAVE_MALLOC_USABLE_SIZE #ifdef HAVE_MALLOC_USABLE_SIZE
#include <malloc.h> # ifndef SQLITE_MALLOCSIZE
#define SQLITE_MALLOCSIZE(x) malloc_usable_size(x) # define SQLITE_MALLOCSIZE(x) malloc_usable_size(x)
# endif
#else #else
#undef SQLITE_MALLOCSIZE # undef SQLITE_MALLOCSIZE
#endif #endif
#endif /* __APPLE__ or not __APPLE__ */ #endif /* __APPLE__ or not __APPLE__ */
@ -184,7 +216,7 @@ static int sqlite3MemRoundup(int n){
** Initialize this module. ** Initialize this module.
*/ */
static int sqlite3MemInit(void *NotUsed){ static int sqlite3MemInit(void *NotUsed){
#if defined(__APPLE__) #if defined(__APPLE__) && !defined(SQLITE_WITHOUT_ZONEMALLOC)
int cpuCount; int cpuCount;
size_t len; size_t len;
if( _sqliteZone_ ){ if( _sqliteZone_ ){

View File

@ -91,11 +91,23 @@
/* /*
** Determine if we are dealing with Windows NT. ** Determine if we are dealing with Windows NT.
**
** We ought to be able to determine if we are compiling for win98 or winNT
** using the _WIN32_WINNT macro as follows:
**
** #if defined(_WIN32_WINNT)
** # define SQLITE_OS_WINNT 1
** #else
** # define SQLITE_OS_WINNT 0
** #endif
**
** However, vs2005 does not set _WIN32_WINNT by default, as it ought to,
** so the above test does not work. We'll just assume that everything is
** winNT unless the programmer explicitly says otherwise by setting
** SQLITE_OS_WINNT to 0.
*/ */
#if defined(_WIN32_WINNT) #if SQLITE_OS_WIN && !defined(SQLITE_OS_WINNT)
# define SQLITE_OS_WINNT 1 # define SQLITE_OS_WINNT 1
#else
# define SQLITE_OS_WINNT 0
#endif #endif
/* /*

View File

@ -165,8 +165,8 @@
#endif #endif
/* /*
** Default permissions when creating auto proxy dir ** Default permissions when creating auto proxy dir
*/ */
#ifndef SQLITE_DEFAULT_PROXYDIR_PERMISSIONS #ifndef SQLITE_DEFAULT_PROXYDIR_PERMISSIONS
# define SQLITE_DEFAULT_PROXYDIR_PERMISSIONS 0755 # define SQLITE_DEFAULT_PROXYDIR_PERMISSIONS 0755
#endif #endif
@ -211,7 +211,7 @@ struct unixFile {
unixInodeInfo *pInode; /* Info about locks on this inode */ unixInodeInfo *pInode; /* Info about locks on this inode */
int h; /* The file descriptor */ int h; /* The file descriptor */
unsigned char eFileLock; /* The type of lock held on this fd */ unsigned char eFileLock; /* The type of lock held on this fd */
unsigned char ctrlFlags; /* Behavioral bits. UNIXFILE_* flags */ unsigned short int ctrlFlags; /* Behavioral bits. UNIXFILE_* flags */
int lastErrno; /* The unix errno from last I/O error */ int lastErrno; /* The unix errno from last I/O error */
void *lockingContext; /* Locking style specific state */ void *lockingContext; /* Locking style specific state */
UnixUnusedFd *pUnused; /* Pre-allocated UnixUnusedFd */ UnixUnusedFd *pUnused; /* Pre-allocated UnixUnusedFd */
@ -262,6 +262,7 @@ struct unixFile {
#define UNIXFILE_DELETE 0x20 /* Delete on close */ #define UNIXFILE_DELETE 0x20 /* Delete on close */
#define UNIXFILE_URI 0x40 /* Filename might have query parameters */ #define UNIXFILE_URI 0x40 /* Filename might have query parameters */
#define UNIXFILE_NOLOCK 0x80 /* Do no file locking */ #define UNIXFILE_NOLOCK 0x80 /* Do no file locking */
#define UNIXFILE_CHOWN 0x100 /* File ownership was changed */
/* /*
** Include code that is common to all os_*.c files ** Include code that is common to all os_*.c files
@ -418,6 +419,12 @@ static struct unix_syscall {
{ "rmdir", (sqlite3_syscall_ptr)rmdir, 0 }, { "rmdir", (sqlite3_syscall_ptr)rmdir, 0 },
#define osRmdir ((int(*)(const char*))aSyscall[19].pCurrent) #define osRmdir ((int(*)(const char*))aSyscall[19].pCurrent)
{ "fchown", (sqlite3_syscall_ptr)fchown, 0 },
#define osFchown ((int(*)(int,uid_t,gid_t))aSyscall[20].pCurrent)
{ "umask", (sqlite3_syscall_ptr)umask, 0 },
#define osUmask ((mode_t(*)(mode_t))aSyscall[21].pCurrent)
}; /* End of the overrideable system calls */ }; /* End of the overrideable system calls */
/* /*
@ -504,12 +511,46 @@ static const char *unixNextSystemCall(sqlite3_vfs *p, const char *zName){
} }
/* /*
** Retry open() calls that fail due to EINTR ** Invoke open(). Do so multiple times, until it either succeeds or
** fails for some reason other than EINTR.
**
** If the file creation mode "m" is 0 then set it to the default for
** SQLite. The default is SQLITE_DEFAULT_FILE_PERMISSIONS (normally
** 0644) as modified by the system umask. If m is not 0, then
** make the file creation mode be exactly m ignoring the umask.
**
** The m parameter will be non-zero only when creating -wal, -journal,
** and -shm files. We want those files to have *exactly* the same
** permissions as their original database, unadulterated by the umask.
** In that way, if a database file is -rw-rw-rw or -rw-rw-r-, and a
** transaction crashes and leaves behind hot journals, then any
** process that is able to write to the database will also be able to
** recover the hot journals.
*/ */
static int robust_open(const char *z, int f, int m){ static int robust_open(const char *z, int f, mode_t m){
int rc; int fd;
do{ rc = osOpen(z,f,m); }while( rc<0 && errno==EINTR ); mode_t m2;
return rc; mode_t origM = 0;
if( m==0 ){
m2 = SQLITE_DEFAULT_FILE_PERMISSIONS;
}else{
m2 = m;
origM = osUmask(0);
}
do{
#if defined(O_CLOEXEC)
fd = osOpen(z,f|O_CLOEXEC,m2);
#else
fd = osOpen(z,f,m2);
#endif
}while( fd<0 && errno==EINTR );
if( m ){
osUmask(origM);
}
#if defined(FD_CLOEXEC) && (!defined(O_CLOEXEC) || O_CLOEXEC==0)
if( fd>=0 ) osFcntl(fd, F_SETFD, osFcntl(fd, F_GETFD, 0) | FD_CLOEXEC);
#endif
return fd;
} }
/* /*
@ -3304,9 +3345,6 @@ static int openDirectory(const char *zFilename, int *pFd){
zDirname[ii] = '\0'; zDirname[ii] = '\0';
fd = robust_open(zDirname, O_RDONLY|O_BINARY, 0); fd = robust_open(zDirname, O_RDONLY|O_BINARY, 0);
if( fd>=0 ){ if( fd>=0 ){
#ifdef FD_CLOEXEC
osFcntl(fd, F_SETFD, osFcntl(fd, F_GETFD, 0) | FD_CLOEXEC);
#endif
OSTRACE(("OPENDIR %-3d %s\n", fd, zDirname)); OSTRACE(("OPENDIR %-3d %s\n", fd, zDirname));
} }
} }
@ -3389,7 +3427,7 @@ static int unixTruncate(sqlite3_file *id, i64 nByte){
** actual file size after the operation may be larger than the requested ** actual file size after the operation may be larger than the requested
** size). ** size).
*/ */
if( pFile->szChunk ){ if( pFile->szChunk>0 ){
nByte = ((nByte + pFile->szChunk - 1)/pFile->szChunk) * pFile->szChunk; nByte = ((nByte + pFile->szChunk - 1)/pFile->szChunk) * pFile->szChunk;
} }
@ -3856,8 +3894,7 @@ static int unixOpenSharedMemory(unixFile *pDbFd){
/* Call fstat() to figure out the permissions on the database file. If /* Call fstat() to figure out the permissions on the database file. If
** a new *-shm file is created, an attempt will be made to create it ** a new *-shm file is created, an attempt will be made to create it
** with the same permissions. The actual permissions the file is created ** with the same permissions.
** with are subject to the current umask setting.
*/ */
if( osFstat(pDbFd->h, &sStat) && pInode->bProcessLock==0 ){ if( osFstat(pDbFd->h, &sStat) && pInode->bProcessLock==0 ){
rc = SQLITE_IOERR_FSTAT; rc = SQLITE_IOERR_FSTAT;
@ -3901,10 +3938,19 @@ static int unixOpenSharedMemory(unixFile *pDbFd){
} }
pShmNode->h = robust_open(zShmFilename, openFlags, (sStat.st_mode&0777)); pShmNode->h = robust_open(zShmFilename, openFlags, (sStat.st_mode&0777));
if( pShmNode->h<0 ){ if( pShmNode->h<0 ){
if( pShmNode->h<0 ){ rc = unixLogError(SQLITE_CANTOPEN_BKPT, "open", zShmFilename);
rc = unixLogError(SQLITE_CANTOPEN_BKPT, "open", zShmFilename); goto shm_open_err;
goto shm_open_err; }
}
/* If this process is running as root, make sure that the SHM file
** is owned by the same user that owns the original database. Otherwise,
** the original owner will not be able to connect. If this process is
** not root, the following fchown() will fail, but we don't care. The
** if(){..} and the UNIXFILE_CHOWN flag are purely to silence compiler
** warnings.
*/
if( osFchown(pShmNode->h, sStat.st_uid, sStat.st_gid)==0 ){
pDbFd->ctrlFlags |= UNIXFILE_CHOWN;
} }
/* Check to see if another process is holding the dead-man switch. /* Check to see if another process is holding the dead-man switch.
@ -4879,12 +4925,10 @@ static UnixUnusedFd *findReusableFd(const char *zPath, int flags){
** written to *pMode. If an IO error occurs, an SQLite error code is ** written to *pMode. If an IO error occurs, an SQLite error code is
** returned and the value of *pMode is not modified. ** returned and the value of *pMode is not modified.
** **
** If the file being opened is a temporary file, it is always created with ** In most cases cases, this routine sets *pMode to 0, which will become
** the octal permissions 0600 (read/writable by owner only). If the file ** an indication to robust_open() to create the file using
** is a database or master journal file, it is created with the permissions ** SQLITE_DEFAULT_FILE_PERMISSIONS adjusted by the umask.
** mask SQLITE_DEFAULT_FILE_PERMISSIONS. ** But if the file being opened is a WAL or regular journal file, then
**
** Finally, if the file being opened is a WAL or regular journal file, then
** this function queries the file-system for the permissions on the ** this function queries the file-system for the permissions on the
** corresponding database file and sets *pMode to this value. Whenever ** corresponding database file and sets *pMode to this value. Whenever
** possible, WAL and journal files are created using the same permissions ** possible, WAL and journal files are created using the same permissions
@ -4898,10 +4942,14 @@ static UnixUnusedFd *findReusableFd(const char *zPath, int flags){
static int findCreateFileMode( static int findCreateFileMode(
const char *zPath, /* Path of file (possibly) being created */ const char *zPath, /* Path of file (possibly) being created */
int flags, /* Flags passed as 4th argument to xOpen() */ int flags, /* Flags passed as 4th argument to xOpen() */
mode_t *pMode /* OUT: Permissions to open file with */ mode_t *pMode, /* OUT: Permissions to open file with */
uid_t *pUid, /* OUT: uid to set on the file */
gid_t *pGid /* OUT: gid to set on the file */
){ ){
int rc = SQLITE_OK; /* Return Code */ int rc = SQLITE_OK; /* Return Code */
*pMode = SQLITE_DEFAULT_FILE_PERMISSIONS; *pMode = 0;
*pUid = 0;
*pGid = 0;
if( flags & (SQLITE_OPEN_WAL|SQLITE_OPEN_MAIN_JOURNAL) ){ if( flags & (SQLITE_OPEN_WAL|SQLITE_OPEN_MAIN_JOURNAL) ){
char zDb[MAX_PATHNAME+1]; /* Database file path */ char zDb[MAX_PATHNAME+1]; /* Database file path */
int nDb; /* Number of valid bytes in zDb */ int nDb; /* Number of valid bytes in zDb */
@ -4935,6 +4983,8 @@ static int findCreateFileMode(
if( 0==osStat(zDb, &sStat) ){ if( 0==osStat(zDb, &sStat) ){
*pMode = sStat.st_mode & 0777; *pMode = sStat.st_mode & 0777;
*pUid = sStat.st_uid;
*pGid = sStat.st_gid;
}else{ }else{
rc = SQLITE_IOERR_FSTAT; rc = SQLITE_IOERR_FSTAT;
} }
@ -5081,7 +5131,9 @@ static int unixOpen(
if( fd<0 ){ if( fd<0 ){
mode_t openMode; /* Permissions to create file with */ mode_t openMode; /* Permissions to create file with */
rc = findCreateFileMode(zName, flags, &openMode); uid_t uid; /* Userid for the file */
gid_t gid; /* Groupid for the file */
rc = findCreateFileMode(zName, flags, &openMode, &uid, &gid);
if( rc!=SQLITE_OK ){ if( rc!=SQLITE_OK ){
assert( !p->pUnused ); assert( !p->pUnused );
assert( eType==SQLITE_OPEN_WAL || eType==SQLITE_OPEN_MAIN_JOURNAL ); assert( eType==SQLITE_OPEN_WAL || eType==SQLITE_OPEN_MAIN_JOURNAL );
@ -5102,6 +5154,17 @@ static int unixOpen(
rc = unixLogError(SQLITE_CANTOPEN_BKPT, "open", zName); rc = unixLogError(SQLITE_CANTOPEN_BKPT, "open", zName);
goto open_finished; goto open_finished;
} }
/* If this process is running as root and if creating a new rollback
** journal or WAL file, set the ownership of the journal or WAL to be
** the same as the original database. If we are not running as root,
** then the fchown() call will fail, but that's ok. The "if(){}" and
** the setting of the UNIXFILE_CHOWN flag are purely to silence compiler
** warnings from gcc.
*/
if( flags & (SQLITE_OPEN_WAL|SQLITE_OPEN_MAIN_JOURNAL) ){
if( osFchown(fd, uid, gid)==0 ){ p->ctrlFlags |= UNIXFILE_CHOWN; }
}
} }
assert( fd>=0 ); assert( fd>=0 );
if( pOutFlags ){ if( pOutFlags ){
@ -5126,10 +5189,6 @@ static int unixOpen(
} }
#endif #endif
#ifdef FD_CLOEXEC
osFcntl(fd, F_SETFD, osFcntl(fd, F_GETFD, 0) | FD_CLOEXEC);
#endif
noLock = eType!=SQLITE_OPEN_MAIN_DB; noLock = eType!=SQLITE_OPEN_MAIN_DB;
@ -5413,7 +5472,7 @@ static int unixRandomness(sqlite3_vfs *NotUsed, int nBuf, char *zBuf){
memset(zBuf, 0, nBuf); memset(zBuf, 0, nBuf);
#if !defined(SQLITE_TEST) #if !defined(SQLITE_TEST)
{ {
int pid, fd; int pid, fd, got;
fd = robust_open("/dev/urandom", O_RDONLY, 0); fd = robust_open("/dev/urandom", O_RDONLY, 0);
if( fd<0 ){ if( fd<0 ){
time_t t; time_t t;
@ -5424,7 +5483,7 @@ static int unixRandomness(sqlite3_vfs *NotUsed, int nBuf, char *zBuf){
assert( sizeof(t)+sizeof(pid)<=(size_t)nBuf ); assert( sizeof(t)+sizeof(pid)<=(size_t)nBuf );
nBuf = sizeof(t) + sizeof(pid); nBuf = sizeof(t) + sizeof(pid);
}else{ }else{
do{ nBuf = osRead(fd, zBuf, nBuf); }while( nBuf<0 && errno==EINTR ); do{ got = osRead(fd, zBuf, nBuf); }while( got<0 && errno==EINTR );
robust_close(0, fd, __LINE__); robust_close(0, fd, __LINE__);
} }
} }
@ -5828,17 +5887,17 @@ static int proxyCreateUnixFile(
} }
} }
if( fd<0 ){ if( fd<0 ){
fd = robust_open(path, openFlags, SQLITE_DEFAULT_FILE_PERMISSIONS); fd = robust_open(path, openFlags, 0);
terrno = errno; terrno = errno;
if( fd<0 && errno==ENOENT && islockfile ){ if( fd<0 && errno==ENOENT && islockfile ){
if( proxyCreateLockPath(path) == SQLITE_OK ){ if( proxyCreateLockPath(path) == SQLITE_OK ){
fd = robust_open(path, openFlags, SQLITE_DEFAULT_FILE_PERMISSIONS); fd = robust_open(path, openFlags, 0);
} }
} }
} }
if( fd<0 ){ if( fd<0 ){
openFlags = O_RDONLY; openFlags = O_RDONLY;
fd = robust_open(path, openFlags, SQLITE_DEFAULT_FILE_PERMISSIONS); fd = robust_open(path, openFlags, 0);
terrno = errno; terrno = errno;
} }
if( fd<0 ){ if( fd<0 ){
@ -5962,8 +6021,7 @@ static int proxyBreakConchLock(unixFile *pFile, uuid_t myHostID){
goto end_breaklock; goto end_breaklock;
} }
/* write it out to the temporary break file */ /* write it out to the temporary break file */
fd = robust_open(tPath, (O_RDWR|O_CREAT|O_EXCL), fd = robust_open(tPath, (O_RDWR|O_CREAT|O_EXCL), 0);
SQLITE_DEFAULT_FILE_PERMISSIONS);
if( fd<0 ){ if( fd<0 ){
sqlite3_snprintf(sizeof(errmsg), errmsg, "create failed (%d)", errno); sqlite3_snprintf(sizeof(errmsg), errmsg, "create failed (%d)", errno);
goto end_breaklock; goto end_breaklock;
@ -6240,8 +6298,7 @@ static int proxyTakeConch(unixFile *pFile){
robust_close(pFile, pFile->h, __LINE__); robust_close(pFile, pFile->h, __LINE__);
} }
pFile->h = -1; pFile->h = -1;
fd = robust_open(pCtx->dbPath, pFile->openFlags, fd = robust_open(pCtx->dbPath, pFile->openFlags, 0);
SQLITE_DEFAULT_FILE_PERMISSIONS);
OSTRACE(("TRANSPROXY: OPEN %d\n", fd)); OSTRACE(("TRANSPROXY: OPEN %d\n", fd));
if( fd>=0 ){ if( fd>=0 ){
pFile->h = fd; pFile->h = fd;
@ -6810,7 +6867,7 @@ int sqlite3_os_init(void){
/* Double-check that the aSyscall[] array has been constructed /* Double-check that the aSyscall[] array has been constructed
** correctly. See ticket [bb3a86e890c8e96ab] */ ** correctly. See ticket [bb3a86e890c8e96ab] */
assert( ArraySize(aSyscall)==20 ); assert( ArraySize(aSyscall)==22 );
/* Register all VFSes defined in the aVfs[] array */ /* Register all VFSes defined in the aVfs[] array */
for(i=0; i<(sizeof(aVfs)/sizeof(sqlite3_vfs)); i++){ for(i=0; i<(sizeof(aVfs)/sizeof(sqlite3_vfs)); i++){

View File

@ -1616,6 +1616,9 @@ static int winClose(sqlite3_file *id){
} }
#endif #endif
OSTRACE(("CLOSE %d %s\n", pFile->h, rc ? "ok" : "failed")); OSTRACE(("CLOSE %d %s\n", pFile->h, rc ? "ok" : "failed"));
if( rc ){
pFile->h = NULL;
}
OpenCounter(-1); OpenCounter(-1);
return rc ? SQLITE_OK return rc ? SQLITE_OK
: winLogError(SQLITE_IOERR_CLOSE, osGetLastError(), : winLogError(SQLITE_IOERR_CLOSE, osGetLastError(),
@ -1633,6 +1636,9 @@ static int winRead(
int amt, /* Number of bytes to read */ int amt, /* Number of bytes to read */
sqlite3_int64 offset /* Begin reading at this offset */ sqlite3_int64 offset /* Begin reading at this offset */
){ ){
#if !SQLITE_OS_WINCE
OVERLAPPED overlapped; /* The offset for ReadFile. */
#endif
winFile *pFile = (winFile*)id; /* file handle */ winFile *pFile = (winFile*)id; /* file handle */
DWORD nRead; /* Number of bytes actually read from file */ DWORD nRead; /* Number of bytes actually read from file */
int nRetry = 0; /* Number of retrys */ int nRetry = 0; /* Number of retrys */
@ -1641,10 +1647,18 @@ static int winRead(
SimulateIOError(return SQLITE_IOERR_READ); SimulateIOError(return SQLITE_IOERR_READ);
OSTRACE(("READ %d lock=%d\n", pFile->h, pFile->locktype)); OSTRACE(("READ %d lock=%d\n", pFile->h, pFile->locktype));
#if SQLITE_OS_WINCE
if( seekWinFile(pFile, offset) ){ if( seekWinFile(pFile, offset) ){
return SQLITE_FULL; return SQLITE_FULL;
} }
while( !osReadFile(pFile->h, pBuf, amt, &nRead, 0) ){ while( !osReadFile(pFile->h, pBuf, amt, &nRead, 0) ){
#else
memset(&overlapped, 0, sizeof(OVERLAPPED));
overlapped.Offset = (LONG)(offset & 0xffffffff);
overlapped.OffsetHigh = (LONG)((offset>>32) & 0x7fffffff);
while( !osReadFile(pFile->h, pBuf, amt, &nRead, &overlapped) &&
osGetLastError()!=ERROR_HANDLE_EOF ){
#endif
DWORD lastErrno; DWORD lastErrno;
if( retryIoerr(&nRetry, &lastErrno) ) continue; if( retryIoerr(&nRetry, &lastErrno) ) continue;
pFile->lastErrno = lastErrno; pFile->lastErrno = lastErrno;
@ -1671,7 +1685,7 @@ static int winWrite(
int amt, /* Number of bytes to write */ int amt, /* Number of bytes to write */
sqlite3_int64 offset /* Offset into the file to begin writing at */ sqlite3_int64 offset /* Offset into the file to begin writing at */
){ ){
int rc; /* True if error has occured, else false */ int rc = 0; /* True if error has occured, else false */
winFile *pFile = (winFile*)id; /* File handle */ winFile *pFile = (winFile*)id; /* File handle */
int nRetry = 0; /* Number of retries */ int nRetry = 0; /* Number of retries */
@ -1682,19 +1696,44 @@ static int winWrite(
OSTRACE(("WRITE %d lock=%d\n", pFile->h, pFile->locktype)); OSTRACE(("WRITE %d lock=%d\n", pFile->h, pFile->locktype));
#if SQLITE_OS_WINCE
rc = seekWinFile(pFile, offset); rc = seekWinFile(pFile, offset);
if( rc==0 ){ if( rc==0 ){
#else
{
#endif
#if !SQLITE_OS_WINCE
OVERLAPPED overlapped; /* The offset for WriteFile. */
#endif
u8 *aRem = (u8 *)pBuf; /* Data yet to be written */ u8 *aRem = (u8 *)pBuf; /* Data yet to be written */
int nRem = amt; /* Number of bytes yet to be written */ int nRem = amt; /* Number of bytes yet to be written */
DWORD nWrite; /* Bytes written by each WriteFile() call */ DWORD nWrite; /* Bytes written by each WriteFile() call */
DWORD lastErrno = NO_ERROR; /* Value returned by GetLastError() */ DWORD lastErrno = NO_ERROR; /* Value returned by GetLastError() */
#if !SQLITE_OS_WINCE
memset(&overlapped, 0, sizeof(OVERLAPPED));
overlapped.Offset = (LONG)(offset & 0xffffffff);
overlapped.OffsetHigh = (LONG)((offset>>32) & 0x7fffffff);
#endif
while( nRem>0 ){ while( nRem>0 ){
#if SQLITE_OS_WINCE
if( !osWriteFile(pFile->h, aRem, nRem, &nWrite, 0) ){ if( !osWriteFile(pFile->h, aRem, nRem, &nWrite, 0) ){
#else
if( !osWriteFile(pFile->h, aRem, nRem, &nWrite, &overlapped) ){
#endif
if( retryIoerr(&nRetry, &lastErrno) ) continue; if( retryIoerr(&nRetry, &lastErrno) ) continue;
break; break;
} }
if( nWrite<=0 ) break; if( nWrite<=0 ){
lastErrno = osGetLastError();
break;
}
#if !SQLITE_OS_WINCE
offset += nWrite;
overlapped.Offset = (LONG)(offset & 0xffffffff);
overlapped.OffsetHigh = (LONG)((offset>>32) & 0x7fffffff);
#endif
aRem += nWrite; aRem += nWrite;
nRem -= nWrite; nRem -= nWrite;
} }
@ -2992,6 +3031,35 @@ static int getTempname(int nBuf, char *zBuf){
return SQLITE_OK; return SQLITE_OK;
} }
/*
** Return TRUE if the named file is really a directory. Return false if
** it is something other than a directory, or if there is any kind of memory
** allocation failure.
*/
static int winIsDir(const void *zConverted){
DWORD attr;
int rc = 0;
DWORD lastErrno;
if( isNT() ){
int cnt = 0;
WIN32_FILE_ATTRIBUTE_DATA sAttrData;
memset(&sAttrData, 0, sizeof(sAttrData));
while( !(rc = osGetFileAttributesExW((LPCWSTR)zConverted,
GetFileExInfoStandard,
&sAttrData)) && retryIoerr(&cnt, &lastErrno) ){}
if( !rc ){
return 0; /* Invalid name? */
}
attr = sAttrData.dwFileAttributes;
#if SQLITE_OS_WINCE==0
}else{
attr = osGetFileAttributesA((char*)zConverted);
#endif
}
return (attr!=INVALID_FILE_ATTRIBUTES) && (attr&FILE_ATTRIBUTE_DIRECTORY);
}
/* /*
** Open a file. ** Open a file.
*/ */
@ -3098,6 +3166,11 @@ static int winOpen(
return SQLITE_IOERR_NOMEM; return SQLITE_IOERR_NOMEM;
} }
if( winIsDir(zConverted) ){
sqlite3_free(zConverted);
return SQLITE_CANTOPEN_ISDIR;
}
if( isReadWrite ){ if( isReadWrite ){
dwDesiredAccess = GENERIC_READ | GENERIC_WRITE; dwDesiredAccess = GENERIC_READ | GENERIC_WRITE;
}else{ }else{
@ -3147,11 +3220,9 @@ static int winOpen(
dwCreationDisposition, dwCreationDisposition,
dwFlagsAndAttributes, dwFlagsAndAttributes,
NULL))==INVALID_HANDLE_VALUE && NULL))==INVALID_HANDLE_VALUE &&
retryIoerr(&cnt, &lastErrno) ){} retryIoerr(&cnt, &lastErrno) ){
/* isNT() is 1 if SQLITE_OS_WINCE==1, so this else is never executed. /* Noop */
** Since the ANSI version of these Windows API do not exist for WINCE, }
** it's important to not reference them for WINCE builds.
*/
#if SQLITE_OS_WINCE==0 #if SQLITE_OS_WINCE==0
}else{ }else{
while( (h = osCreateFileA((LPCSTR)zConverted, while( (h = osCreateFileA((LPCSTR)zConverted,
@ -3160,7 +3231,9 @@ static int winOpen(
dwCreationDisposition, dwCreationDisposition,
dwFlagsAndAttributes, dwFlagsAndAttributes,
NULL))==INVALID_HANDLE_VALUE && NULL))==INVALID_HANDLE_VALUE &&
retryIoerr(&cnt, &lastErrno) ){} retryIoerr(&cnt, &lastErrno) ){
/* Noop */
}
#endif #endif
} }
@ -3240,6 +3313,7 @@ static int winDelete(
){ ){
int cnt = 0; int cnt = 0;
int rc; int rc;
DWORD attr;
DWORD lastErrno; DWORD lastErrno;
void *zConverted; void *zConverted;
UNUSED_PARAMETER(pVfs); UNUSED_PARAMETER(pVfs);
@ -3251,20 +3325,50 @@ static int winDelete(
return SQLITE_IOERR_NOMEM; return SQLITE_IOERR_NOMEM;
} }
if( isNT() ){ if( isNT() ){
rc = 1; do {
while( osGetFileAttributesW(zConverted)!=INVALID_FILE_ATTRIBUTES && attr = osGetFileAttributesW(zConverted);
(rc = osDeleteFileW(zConverted))==0 && retryIoerr(&cnt, &lastErrno) ){} if ( attr==INVALID_FILE_ATTRIBUTES ){
rc = rc ? SQLITE_OK : SQLITE_ERROR; rc = SQLITE_OK; /* Already gone? */
break;
}
if ( attr&FILE_ATTRIBUTE_DIRECTORY ){
rc = SQLITE_ERROR; /* Files only. */
break;
}
if ( osDeleteFileW(zConverted) ){
rc = SQLITE_OK; /* Deleted OK. */
break;
}
if ( !retryIoerr(&cnt, &lastErrno) ){
rc = SQLITE_ERROR; /* No more retries. */
break;
}
} while(1);
/* isNT() is 1 if SQLITE_OS_WINCE==1, so this else is never executed. /* isNT() is 1 if SQLITE_OS_WINCE==1, so this else is never executed.
** Since the ANSI version of these Windows API do not exist for WINCE, ** Since the ANSI version of these Windows API do not exist for WINCE,
** it's important to not reference them for WINCE builds. ** it's important to not reference them for WINCE builds.
*/ */
#if SQLITE_OS_WINCE==0 #if SQLITE_OS_WINCE==0
}else{ }else{
rc = 1; do {
while( osGetFileAttributesA(zConverted)!=INVALID_FILE_ATTRIBUTES && attr = osGetFileAttributesA(zConverted);
(rc = osDeleteFileA(zConverted))==0 && retryIoerr(&cnt, &lastErrno) ){} if ( attr==INVALID_FILE_ATTRIBUTES ){
rc = rc ? SQLITE_OK : SQLITE_ERROR; rc = SQLITE_OK; /* Already gone? */
break;
}
if ( attr&FILE_ATTRIBUTE_DIRECTORY ){
rc = SQLITE_ERROR; /* Files only. */
break;
}
if ( osDeleteFileA(zConverted) ){
rc = SQLITE_OK; /* Deleted OK. */
break;
}
if ( !retryIoerr(&cnt, &lastErrno) ){
rc = SQLITE_ERROR; /* No more retries. */
break;
}
} while(1);
#endif #endif
} }
if( rc ){ if( rc ){

View File

@ -612,7 +612,6 @@ struct Pager {
u8 exclusiveMode; /* Boolean. True if locking_mode==EXCLUSIVE */ u8 exclusiveMode; /* Boolean. True if locking_mode==EXCLUSIVE */
u8 journalMode; /* One of the PAGER_JOURNALMODE_* values */ u8 journalMode; /* One of the PAGER_JOURNALMODE_* values */
u8 useJournal; /* Use a rollback journal on this file */ u8 useJournal; /* Use a rollback journal on this file */
u8 noReadlock; /* Do not bother to obtain readlocks */
u8 noSync; /* Do not sync the journal if true */ u8 noSync; /* Do not sync the journal if true */
u8 fullSync; /* Do extra syncs of the journal for robustness */ u8 fullSync; /* Do extra syncs of the journal for robustness */
u8 ckptSyncFlags; /* SYNC_NORMAL or SYNC_FULL for checkpoint */ u8 ckptSyncFlags; /* SYNC_NORMAL or SYNC_FULL for checkpoint */
@ -671,9 +670,9 @@ struct Pager {
char *zJournal; /* Name of the journal file */ char *zJournal; /* Name of the journal file */
int (*xBusyHandler)(void*); /* Function to call when busy */ int (*xBusyHandler)(void*); /* Function to call when busy */
void *pBusyHandlerArg; /* Context argument for xBusyHandler */ void *pBusyHandlerArg; /* Context argument for xBusyHandler */
int nHit, nMiss; /* Total cache hits and misses */ int aStat[3]; /* Total cache hits, misses and writes */
#ifdef SQLITE_TEST #ifdef SQLITE_TEST
int nRead, nWrite; /* Database pages read/written */ int nRead; /* Database pages read */
#endif #endif
void (*xReiniter)(DbPage*); /* Call this routine when reloading pages */ void (*xReiniter)(DbPage*); /* Call this routine when reloading pages */
#ifdef SQLITE_HAS_CODEC #ifdef SQLITE_HAS_CODEC
@ -690,6 +689,15 @@ struct Pager {
#endif #endif
}; };
/*
** Indexes for use with Pager.aStat[]. The Pager.aStat[] array contains
** the values accessed by passing SQLITE_DBSTATUS_CACHE_HIT, CACHE_MISS
** or CACHE_WRITE to sqlite3_db_status().
*/
#define PAGER_STAT_HIT 0
#define PAGER_STAT_MISS 1
#define PAGER_STAT_WRITE 2
/* /*
** The following global variables hold counters used for ** The following global variables hold counters used for
** testing purposes only. These variables do not exist in ** testing purposes only. These variables do not exist in
@ -860,7 +868,7 @@ static int assert_pager_state(Pager *p){
case PAGER_READER: case PAGER_READER:
assert( pPager->errCode==SQLITE_OK ); assert( pPager->errCode==SQLITE_OK );
assert( p->eLock!=UNKNOWN_LOCK ); assert( p->eLock!=UNKNOWN_LOCK );
assert( p->eLock>=SHARED_LOCK || p->noReadlock ); assert( p->eLock>=SHARED_LOCK );
break; break;
case PAGER_WRITER_LOCKED: case PAGER_WRITER_LOCKED:
@ -2972,6 +2980,7 @@ static int pagerWalFrames(
int isCommit /* True if this is a commit */ int isCommit /* True if this is a commit */
){ ){
int rc; /* Return code */ int rc; /* Return code */
int nList; /* Number of pages in pList */
#if defined(SQLITE_DEBUG) || defined(SQLITE_CHECK_PAGES) #if defined(SQLITE_DEBUG) || defined(SQLITE_CHECK_PAGES)
PgHdr *p; /* For looping over pages */ PgHdr *p; /* For looping over pages */
#endif #endif
@ -2985,6 +2994,7 @@ static int pagerWalFrames(
} }
#endif #endif
assert( pList->pDirty==0 || isCommit );
if( isCommit ){ if( isCommit ){
/* If a WAL transaction is being committed, there is no point in writing /* If a WAL transaction is being committed, there is no point in writing
** any pages with page numbers greater than nTruncate into the WAL file. ** any pages with page numbers greater than nTruncate into the WAL file.
@ -2992,11 +3002,18 @@ static int pagerWalFrames(
** list here. */ ** list here. */
PgHdr *p; PgHdr *p;
PgHdr **ppNext = &pList; PgHdr **ppNext = &pList;
for(p=pList; (*ppNext = p); p=p->pDirty){ nList = 0;
if( p->pgno<=nTruncate ) ppNext = &p->pDirty; for(p=pList; (*ppNext = p)!=0; p=p->pDirty){
if( p->pgno<=nTruncate ){
ppNext = &p->pDirty;
nList++;
}
} }
assert( pList ); assert( pList );
}else{
nList = 1;
} }
pPager->aStat[PAGER_STAT_WRITE] += nList;
if( pList->pgno==1 ) pager_write_changecounter(pList); if( pList->pgno==1 ) pager_write_changecounter(pList);
rc = sqlite3WalFrames(pPager->pWal, rc = sqlite3WalFrames(pPager->pWal,
@ -3069,7 +3086,7 @@ static int pagerPagecount(Pager *pPager, Pgno *pnPage){
** contains no valid committed transactions. ** contains no valid committed transactions.
*/ */
assert( pPager->eState==PAGER_OPEN ); assert( pPager->eState==PAGER_OPEN );
assert( pPager->eLock>=SHARED_LOCK || pPager->noReadlock ); assert( pPager->eLock>=SHARED_LOCK );
nPage = sqlite3WalDbsize(pPager->pWal); nPage = sqlite3WalDbsize(pPager->pWal);
/* If the database size was not available from the WAL sub-system, /* If the database size was not available from the WAL sub-system,
@ -3124,7 +3141,7 @@ static int pagerPagecount(Pager *pPager, Pgno *pnPage){
static int pagerOpenWalIfPresent(Pager *pPager){ static int pagerOpenWalIfPresent(Pager *pPager){
int rc = SQLITE_OK; int rc = SQLITE_OK;
assert( pPager->eState==PAGER_OPEN ); assert( pPager->eState==PAGER_OPEN );
assert( pPager->eLock>=SHARED_LOCK || pPager->noReadlock ); assert( pPager->eLock>=SHARED_LOCK );
if( !pPager->tempFile ){ if( !pPager->tempFile ){
int isWal; /* True if WAL file exists */ int isWal; /* True if WAL file exists */
@ -4064,6 +4081,7 @@ static int pager_write_pagelist(Pager *pPager, PgHdr *pList){
if( pgno>pPager->dbFileSize ){ if( pgno>pPager->dbFileSize ){
pPager->dbFileSize = pgno; pPager->dbFileSize = pgno;
} }
pPager->aStat[PAGER_STAT_WRITE]++;
/* Update any backup objects copying the contents of this pager. */ /* Update any backup objects copying the contents of this pager. */
sqlite3BackupUpdate(pPager->pBackup, pgno, (u8*)pList->pData); sqlite3BackupUpdate(pPager->pBackup, pgno, (u8*)pList->pData);
@ -4072,7 +4090,6 @@ static int pager_write_pagelist(Pager *pPager, PgHdr *pList){
PAGERID(pPager), pgno, pager_pagehash(pList))); PAGERID(pPager), pgno, pager_pagehash(pList)));
IOTRACE(("PGOUT %p %d\n", pPager, pgno)); IOTRACE(("PGOUT %p %d\n", pPager, pgno));
PAGER_INCR(sqlite3_pager_writedb_count); PAGER_INCR(sqlite3_pager_writedb_count);
PAGER_INCR(pPager->nWrite);
}else{ }else{
PAGERTRACE(("NOSTORE %d page %d\n", PAGERID(pPager), pgno)); PAGERTRACE(("NOSTORE %d page %d\n", PAGERID(pPager), pgno));
} }
@ -4287,7 +4304,7 @@ static int pagerStress(void *p, PgHdr *pPg){
** **
** The flags argument is used to specify properties that affect the ** The flags argument is used to specify properties that affect the
** operation of the pager. It should be passed some bitwise combination ** operation of the pager. It should be passed some bitwise combination
** of the PAGER_OMIT_JOURNAL and PAGER_NO_READLOCK flags. ** of the PAGER_* flags.
** **
** The vfsFlags parameter is a bitmask to pass to the flags parameter ** The vfsFlags parameter is a bitmask to pass to the flags parameter
** of the xOpen() method of the supplied VFS when opening files. ** of the xOpen() method of the supplied VFS when opening files.
@ -4318,7 +4335,6 @@ int sqlite3PagerOpen(
char *zPathname = 0; /* Full path to database file */ char *zPathname = 0; /* Full path to database file */
int nPathname = 0; /* Number of bytes in zPathname */ int nPathname = 0; /* Number of bytes in zPathname */
int useJournal = (flags & PAGER_OMIT_JOURNAL)==0; /* False to omit journal */ int useJournal = (flags & PAGER_OMIT_JOURNAL)==0; /* False to omit journal */
int noReadlock = (flags & PAGER_NO_READLOCK)!=0; /* True to omit read-lock */
int pcacheSize = sqlite3PcacheSize(); /* Bytes to allocate for PCache */ int pcacheSize = sqlite3PcacheSize(); /* Bytes to allocate for PCache */
u32 szPageDflt = SQLITE_DEFAULT_PAGE_SIZE; /* Default page size */ u32 szPageDflt = SQLITE_DEFAULT_PAGE_SIZE; /* Default page size */
const char *zUri = 0; /* URI args to copy */ const char *zUri = 0; /* URI args to copy */
@ -4525,7 +4541,6 @@ int sqlite3PagerOpen(
IOTRACE(("OPEN %p %s\n", pPager, pPager->zFilename)) IOTRACE(("OPEN %p %s\n", pPager, pPager->zFilename))
pPager->useJournal = (u8)useJournal; pPager->useJournal = (u8)useJournal;
pPager->noReadlock = (noReadlock && readOnly) ?1:0;
/* pPager->stmtOpen = 0; */ /* pPager->stmtOpen = 0; */
/* pPager->stmtInUse = 0; */ /* pPager->stmtInUse = 0; */
/* pPager->nRef = 0; */ /* pPager->nRef = 0; */
@ -4747,14 +4762,11 @@ int sqlite3PagerSharedLock(Pager *pPager){
int bHotJournal = 1; /* True if there exists a hot journal-file */ int bHotJournal = 1; /* True if there exists a hot journal-file */
assert( !MEMDB ); assert( !MEMDB );
assert( pPager->noReadlock==0 || pPager->readOnly );
if( pPager->noReadlock==0 ){ rc = pager_wait_on_lock(pPager, SHARED_LOCK);
rc = pager_wait_on_lock(pPager, SHARED_LOCK); if( rc!=SQLITE_OK ){
if( rc!=SQLITE_OK ){ assert( pPager->eLock==NO_LOCK || pPager->eLock==UNKNOWN_LOCK );
assert( pPager->eLock==NO_LOCK || pPager->eLock==UNKNOWN_LOCK ); goto failed;
goto failed;
}
} }
/* If a journal file exists, and there is no RESERVED lock on the /* If a journal file exists, and there is no RESERVED lock on the
@ -5035,7 +5047,7 @@ int sqlite3PagerAcquire(
/* In this case the pcache already contains an initialized copy of /* In this case the pcache already contains an initialized copy of
** the page. Return without further ado. */ ** the page. Return without further ado. */
assert( pgno<=PAGER_MAX_PGNO && pgno!=PAGER_MJ_PGNO(pPager) ); assert( pgno<=PAGER_MAX_PGNO && pgno!=PAGER_MJ_PGNO(pPager) );
pPager->nHit++; pPager->aStat[PAGER_STAT_HIT]++;
return SQLITE_OK; return SQLITE_OK;
}else{ }else{
@ -5077,7 +5089,7 @@ int sqlite3PagerAcquire(
IOTRACE(("ZERO %p %d\n", pPager, pgno)); IOTRACE(("ZERO %p %d\n", pPager, pgno));
}else{ }else{
assert( pPg->pPager==pPager ); assert( pPg->pPager==pPager );
pPager->nMiss++; pPager->aStat[PAGER_STAT_MISS]++;
rc = readDbPage(pPg); rc = readDbPage(pPg);
if( rc!=SQLITE_OK ){ if( rc!=SQLITE_OK ){
goto pager_acquire_err; goto pager_acquire_err;
@ -5662,6 +5674,7 @@ static int pager_incr_changecounter(Pager *pPager, int isDirectMode){
CODEC2(pPager, pPgHdr->pData, 1, 6, rc=SQLITE_NOMEM, zBuf); CODEC2(pPager, pPgHdr->pData, 1, 6, rc=SQLITE_NOMEM, zBuf);
if( rc==SQLITE_OK ){ if( rc==SQLITE_OK ){
rc = sqlite3OsWrite(pPager->fd, zBuf, pPager->pageSize, 0); rc = sqlite3OsWrite(pPager->fd, zBuf, pPager->pageSize, 0);
pPager->aStat[PAGER_STAT_WRITE]++;
} }
if( rc==SQLITE_OK ){ if( rc==SQLITE_OK ){
pPager->changeCountDone = 1; pPager->changeCountDone = 1;
@ -6105,11 +6118,11 @@ int *sqlite3PagerStats(Pager *pPager){
a[3] = pPager->eState==PAGER_OPEN ? -1 : (int) pPager->dbSize; a[3] = pPager->eState==PAGER_OPEN ? -1 : (int) pPager->dbSize;
a[4] = pPager->eState; a[4] = pPager->eState;
a[5] = pPager->errCode; a[5] = pPager->errCode;
a[6] = pPager->nHit; a[6] = pPager->aStat[PAGER_STAT_HIT];
a[7] = pPager->nMiss; a[7] = pPager->aStat[PAGER_STAT_MISS];
a[8] = 0; /* Used to be pPager->nOvfl */ a[8] = 0; /* Used to be pPager->nOvfl */
a[9] = pPager->nRead; a[9] = pPager->nRead;
a[10] = pPager->nWrite; a[10] = pPager->aStat[PAGER_STAT_WRITE];
return a; return a;
} }
#endif #endif
@ -6122,20 +6135,19 @@ int *sqlite3PagerStats(Pager *pPager){
** returning. ** returning.
*/ */
void sqlite3PagerCacheStat(Pager *pPager, int eStat, int reset, int *pnVal){ void sqlite3PagerCacheStat(Pager *pPager, int eStat, int reset, int *pnVal){
int *piStat;
assert( eStat==SQLITE_DBSTATUS_CACHE_HIT assert( eStat==SQLITE_DBSTATUS_CACHE_HIT
|| eStat==SQLITE_DBSTATUS_CACHE_MISS || eStat==SQLITE_DBSTATUS_CACHE_MISS
|| eStat==SQLITE_DBSTATUS_CACHE_WRITE
); );
if( eStat==SQLITE_DBSTATUS_CACHE_HIT ){
piStat = &pPager->nHit;
}else{
piStat = &pPager->nMiss;
}
*pnVal += *piStat; assert( SQLITE_DBSTATUS_CACHE_HIT+1==SQLITE_DBSTATUS_CACHE_MISS );
assert( SQLITE_DBSTATUS_CACHE_HIT+2==SQLITE_DBSTATUS_CACHE_WRITE );
assert( PAGER_STAT_HIT==0 && PAGER_STAT_MISS==1 && PAGER_STAT_WRITE==2 );
*pnVal += pPager->aStat[eStat - SQLITE_DBSTATUS_CACHE_HIT];
if( reset ){ if( reset ){
*piStat = 0; pPager->aStat[eStat - SQLITE_DBSTATUS_CACHE_HIT] = 0;
} }
} }
@ -6762,7 +6774,7 @@ static int pagerOpenWal(Pager *pPager){
int rc = SQLITE_OK; int rc = SQLITE_OK;
assert( pPager->pWal==0 && pPager->tempFile==0 ); assert( pPager->pWal==0 && pPager->tempFile==0 );
assert( pPager->eLock==SHARED_LOCK || pPager->eLock==EXCLUSIVE_LOCK || pPager->noReadlock); assert( pPager->eLock==SHARED_LOCK || pPager->eLock==EXCLUSIVE_LOCK );
/* If the pager is already in exclusive-mode, the WAL module will use /* If the pager is already in exclusive-mode, the WAL module will use
** heap-memory for the wal-index instead of the VFS shared-memory ** heap-memory for the wal-index instead of the VFS shared-memory
@ -6877,6 +6889,20 @@ int sqlite3PagerCloseWal(Pager *pPager){
return rc; return rc;
} }
#ifdef SQLITE_ENABLE_ZIPVFS
/*
** A read-lock must be held on the pager when this function is called. If
** the pager is in WAL mode and the WAL file currently contains one or more
** frames, return the size in bytes of the page images stored within the
** WAL frames. Otherwise, if this is not a WAL database or the WAL file
** is empty, return 0.
*/
int sqlite3PagerWalFramesize(Pager *pPager){
assert( pPager->eState==PAGER_READER );
return sqlite3WalFramesize(pPager->pWal);
}
#endif
#ifdef SQLITE_HAS_CODEC #ifdef SQLITE_HAS_CODEC
/* /*
** This function is called by the wal module when writing page content ** This function is called by the wal module when writing page content

View File

@ -58,8 +58,7 @@ typedef struct PgHdr DbPage;
** NOTE: These values must match the corresponding BTREE_ values in btree.h. ** NOTE: These values must match the corresponding BTREE_ values in btree.h.
*/ */
#define PAGER_OMIT_JOURNAL 0x0001 /* Do not use a rollback journal */ #define PAGER_OMIT_JOURNAL 0x0001 /* Do not use a rollback journal */
#define PAGER_NO_READLOCK 0x0002 /* Omit readlocks on readonly files */ #define PAGER_MEMORY 0x0002 /* In-memory database */
#define PAGER_MEMORY 0x0004 /* In-memory database */
/* /*
** Valid values for the second argument to sqlite3PagerLockingMode(). ** Valid values for the second argument to sqlite3PagerLockingMode().
@ -144,6 +143,9 @@ int sqlite3PagerWalSupported(Pager *pPager);
int sqlite3PagerWalCallback(Pager *pPager); int sqlite3PagerWalCallback(Pager *pPager);
int sqlite3PagerOpenWal(Pager *pPager, int *pisOpen); int sqlite3PagerOpenWal(Pager *pPager, int *pisOpen);
int sqlite3PagerCloseWal(Pager *pPager); int sqlite3PagerCloseWal(Pager *pPager);
#ifdef SQLITE_ENABLE_ZIPVFS
int sqlite3PagerWalFramesize(Pager *pPager);
#endif
/* Functions used to query pager state and configuration. */ /* Functions used to query pager state and configuration. */
u8 sqlite3PagerIsreadonly(Pager*); u8 sqlite3PagerIsreadonly(Pager*);

View File

@ -75,7 +75,7 @@ struct LimitVal {
*/ */
struct LikeOp { struct LikeOp {
Token eOperator; /* "like" or "glob" or "regexp" */ Token eOperator; /* "like" or "glob" or "regexp" */
int not; /* True if the NOT keyword is present */ int bNot; /* True if the NOT keyword is present */
}; };
/* /*
@ -94,6 +94,14 @@ struct TrigEvent { int a; IdList * b; };
*/ */
struct AttachKey { int type; Token key; }; struct AttachKey { int type; Token key; };
/*
** One or more VALUES claues
*/
struct ValueList {
ExprList *pList;
Select *pSelect;
};
} // end %include } // end %include
// Input is a single SQL command // Input is a single SQL command
@ -177,6 +185,7 @@ column(A) ::= columnid(X) type carglist. {
columnid(A) ::= nm(X). { columnid(A) ::= nm(X). {
sqlite3AddColumn(pParse,&X); sqlite3AddColumn(pParse,&X);
A = X; A = X;
pParse->constraintName.n = 0;
} }
@ -265,10 +274,9 @@ signed ::= minus_num.
// "carglist" is a list of additional constraints that come after the // "carglist" is a list of additional constraints that come after the
// column name and column type in a CREATE TABLE statement. // column name and column type in a CREATE TABLE statement.
// //
carglist ::= carglist carg. carglist ::= carglist ccons.
carglist ::= . carglist ::= .
carg ::= CONSTRAINT nm ccons. ccons ::= CONSTRAINT nm(X). {pParse->constraintName = X;}
carg ::= ccons.
ccons ::= DEFAULT term(X). {sqlite3AddDefaultValue(pParse,&X);} ccons ::= DEFAULT term(X). {sqlite3AddDefaultValue(pParse,&X);}
ccons ::= DEFAULT LP expr(X) RP. {sqlite3AddDefaultValue(pParse,&X);} ccons ::= DEFAULT LP expr(X) RP. {sqlite3AddDefaultValue(pParse,&X);}
ccons ::= DEFAULT PLUS term(X). {sqlite3AddDefaultValue(pParse,&X);} ccons ::= DEFAULT PLUS term(X). {sqlite3AddDefaultValue(pParse,&X);}
@ -331,15 +339,13 @@ init_deferred_pred_opt(A) ::= . {A = 0;}
init_deferred_pred_opt(A) ::= INITIALLY DEFERRED. {A = 1;} init_deferred_pred_opt(A) ::= INITIALLY DEFERRED. {A = 1;}
init_deferred_pred_opt(A) ::= INITIALLY IMMEDIATE. {A = 0;} init_deferred_pred_opt(A) ::= INITIALLY IMMEDIATE. {A = 0;}
// For the time being, the only constraint we care about is the primary conslist_opt(A) ::= . {A.n = 0; A.z = 0;}
// key and UNIQUE. Both create indices. conslist_opt(A) ::= COMMA(X) conslist. {A = X;}
// conslist ::= conslist tconscomma tcons.
conslist_opt(A) ::= . {A.n = 0; A.z = 0;}
conslist_opt(A) ::= COMMA(X) conslist. {A = X;}
conslist ::= conslist COMMA tcons.
conslist ::= conslist tcons.
conslist ::= tcons. conslist ::= tcons.
tcons ::= CONSTRAINT nm. tconscomma ::= COMMA. {pParse->constraintName.n = 0;}
tconscomma ::= .
tcons ::= CONSTRAINT nm(X). {pParse->constraintName = X;}
tcons ::= PRIMARY KEY LP idxlist(X) autoinc(I) RP onconf(R). tcons ::= PRIMARY KEY LP idxlist(X) autoinc(I) RP onconf(R).
{sqlite3AddPrimaryKey(pParse,X,R,I,0);} {sqlite3AddPrimaryKey(pParse,X,R,I,0);}
tcons ::= UNIQUE LP idxlist(X) RP onconf(R). tcons ::= UNIQUE LP idxlist(X) RP onconf(R).
@ -573,20 +579,17 @@ using_opt(U) ::= . {U = 0;}
%destructor orderby_opt {sqlite3ExprListDelete(pParse->db, $$);} %destructor orderby_opt {sqlite3ExprListDelete(pParse->db, $$);}
%type sortlist {ExprList*} %type sortlist {ExprList*}
%destructor sortlist {sqlite3ExprListDelete(pParse->db, $$);} %destructor sortlist {sqlite3ExprListDelete(pParse->db, $$);}
%type sortitem {Expr*}
%destructor sortitem {sqlite3ExprDelete(pParse->db, $$);}
orderby_opt(A) ::= . {A = 0;} orderby_opt(A) ::= . {A = 0;}
orderby_opt(A) ::= ORDER BY sortlist(X). {A = X;} orderby_opt(A) ::= ORDER BY sortlist(X). {A = X;}
sortlist(A) ::= sortlist(X) COMMA sortitem(Y) sortorder(Z). { sortlist(A) ::= sortlist(X) COMMA expr(Y) sortorder(Z). {
A = sqlite3ExprListAppend(pParse,X,Y); A = sqlite3ExprListAppend(pParse,X,Y.pExpr);
if( A ) A->a[A->nExpr-1].sortOrder = (u8)Z; if( A ) A->a[A->nExpr-1].sortOrder = (u8)Z;
} }
sortlist(A) ::= sortitem(Y) sortorder(Z). { sortlist(A) ::= expr(Y) sortorder(Z). {
A = sqlite3ExprListAppend(pParse,0,Y); A = sqlite3ExprListAppend(pParse,0,Y.pExpr);
if( A && ALWAYS(A->a) ) A->a[0].sortOrder = (u8)Z; if( A && ALWAYS(A->a) ) A->a[0].sortOrder = (u8)Z;
} }
sortitem(A) ::= expr(X). {A = X.pExpr;}
%type sortorder {int} %type sortorder {int}
@ -679,9 +682,8 @@ setlist(A) ::= nm(X) EQ expr(Y). {
////////////////////////// The INSERT command ///////////////////////////////// ////////////////////////// The INSERT command /////////////////////////////////
// //
cmd ::= insert_cmd(R) INTO fullname(X) inscollist_opt(F) cmd ::= insert_cmd(R) INTO fullname(X) inscollist_opt(F) valuelist(Y).
VALUES LP itemlist(Y) RP. {sqlite3Insert(pParse, X, Y.pList, Y.pSelect, F, R);}
{sqlite3Insert(pParse, X, Y, 0, F, R);}
cmd ::= insert_cmd(R) INTO fullname(X) inscollist_opt(F) select(S). cmd ::= insert_cmd(R) INTO fullname(X) inscollist_opt(F) select(S).
{sqlite3Insert(pParse, X, 0, S, F, R);} {sqlite3Insert(pParse, X, 0, S, F, R);}
cmd ::= insert_cmd(R) INTO fullname(X) inscollist_opt(F) DEFAULT VALUES. cmd ::= insert_cmd(R) INTO fullname(X) inscollist_opt(F) DEFAULT VALUES.
@ -691,14 +693,46 @@ cmd ::= insert_cmd(R) INTO fullname(X) inscollist_opt(F) DEFAULT VALUES.
insert_cmd(A) ::= INSERT orconf(R). {A = R;} insert_cmd(A) ::= INSERT orconf(R). {A = R;}
insert_cmd(A) ::= REPLACE. {A = OE_Replace;} insert_cmd(A) ::= REPLACE. {A = OE_Replace;}
// A ValueList is either a single VALUES clause or a comma-separated list
// of VALUES clauses. If it is a single VALUES clause then the
// ValueList.pList field points to the expression list of that clause.
// If it is a list of VALUES clauses, then those clauses are transformed
// into a set of SELECT statements without FROM clauses and connected by
// UNION ALL and the ValueList.pSelect points to the right-most SELECT in
// that compound.
%type valuelist {struct ValueList}
%destructor valuelist {
sqlite3ExprListDelete(pParse->db, $$.pList);
sqlite3SelectDelete(pParse->db, $$.pSelect);
}
valuelist(A) ::= VALUES LP nexprlist(X) RP. {
A.pList = X;
A.pSelect = 0;
}
%type itemlist {ExprList*} // Since a list of VALUEs is inplemented as a compound SELECT, we have
%destructor itemlist {sqlite3ExprListDelete(pParse->db, $$);} // to disable the value list option if compound SELECTs are disabled.
%ifndef SQLITE_OMIT_COMPOUND_SELECT
itemlist(A) ::= itemlist(X) COMMA expr(Y). valuelist(A) ::= valuelist(X) COMMA LP exprlist(Y) RP. {
{A = sqlite3ExprListAppend(pParse,X,Y.pExpr);} Select *pRight = sqlite3SelectNew(pParse, Y, 0, 0, 0, 0, 0, 0, 0, 0);
itemlist(A) ::= expr(X). if( X.pList ){
{A = sqlite3ExprListAppend(pParse,0,X.pExpr);} X.pSelect = sqlite3SelectNew(pParse, X.pList, 0, 0, 0, 0, 0, 0, 0, 0);
X.pList = 0;
}
A.pList = 0;
if( X.pSelect==0 || pRight==0 ){
sqlite3SelectDelete(pParse->db, pRight);
sqlite3SelectDelete(pParse->db, X.pSelect);
A.pSelect = 0;
}else{
pRight->op = TK_ALL;
pRight->pPrior = X.pSelect;
pRight->selFlags |= SF_Values;
pRight->pPrior->selFlags |= SF_Values;
A.pSelect = pRight;
}
}
%endif SQLITE_OMIT_COMPOUND_SELECT
%type inscollist_opt {IdList*} %type inscollist_opt {IdList*}
%destructor inscollist_opt {sqlite3IdListDelete(pParse->db, $$);} %destructor inscollist_opt {sqlite3IdListDelete(pParse->db, $$);}
@ -845,16 +879,16 @@ expr(A) ::= expr(X) STAR|SLASH|REM(OP) expr(Y).
{spanBinaryExpr(&A,pParse,@OP,&X,&Y);} {spanBinaryExpr(&A,pParse,@OP,&X,&Y);}
expr(A) ::= expr(X) CONCAT(OP) expr(Y). {spanBinaryExpr(&A,pParse,@OP,&X,&Y);} expr(A) ::= expr(X) CONCAT(OP) expr(Y). {spanBinaryExpr(&A,pParse,@OP,&X,&Y);}
%type likeop {struct LikeOp} %type likeop {struct LikeOp}
likeop(A) ::= LIKE_KW(X). {A.eOperator = X; A.not = 0;} likeop(A) ::= LIKE_KW(X). {A.eOperator = X; A.bNot = 0;}
likeop(A) ::= NOT LIKE_KW(X). {A.eOperator = X; A.not = 1;} likeop(A) ::= NOT LIKE_KW(X). {A.eOperator = X; A.bNot = 1;}
likeop(A) ::= MATCH(X). {A.eOperator = X; A.not = 0;} likeop(A) ::= MATCH(X). {A.eOperator = X; A.bNot = 0;}
likeop(A) ::= NOT MATCH(X). {A.eOperator = X; A.not = 1;} likeop(A) ::= NOT MATCH(X). {A.eOperator = X; A.bNot = 1;}
expr(A) ::= expr(X) likeop(OP) expr(Y). [LIKE_KW] { expr(A) ::= expr(X) likeop(OP) expr(Y). [LIKE_KW] {
ExprList *pList; ExprList *pList;
pList = sqlite3ExprListAppend(pParse,0, Y.pExpr); pList = sqlite3ExprListAppend(pParse,0, Y.pExpr);
pList = sqlite3ExprListAppend(pParse,pList, X.pExpr); pList = sqlite3ExprListAppend(pParse,pList, X.pExpr);
A.pExpr = sqlite3ExprFunction(pParse, pList, &OP.eOperator); A.pExpr = sqlite3ExprFunction(pParse, pList, &OP.eOperator);
if( OP.not ) A.pExpr = sqlite3PExpr(pParse, TK_NOT, A.pExpr, 0, 0); if( OP.bNot ) A.pExpr = sqlite3PExpr(pParse, TK_NOT, A.pExpr, 0, 0);
A.zStart = X.zStart; A.zStart = X.zStart;
A.zEnd = Y.zEnd; A.zEnd = Y.zEnd;
if( A.pExpr ) A.pExpr->flags |= EP_InfixFunc; if( A.pExpr ) A.pExpr->flags |= EP_InfixFunc;
@ -865,7 +899,7 @@ expr(A) ::= expr(X) likeop(OP) expr(Y) ESCAPE expr(E). [LIKE_KW] {
pList = sqlite3ExprListAppend(pParse,pList, X.pExpr); pList = sqlite3ExprListAppend(pParse,pList, X.pExpr);
pList = sqlite3ExprListAppend(pParse,pList, E.pExpr); pList = sqlite3ExprListAppend(pParse,pList, E.pExpr);
A.pExpr = sqlite3ExprFunction(pParse, pList, &OP.eOperator); A.pExpr = sqlite3ExprFunction(pParse, pList, &OP.eOperator);
if( OP.not ) A.pExpr = sqlite3PExpr(pParse, TK_NOT, A.pExpr, 0, 0); if( OP.bNot ) A.pExpr = sqlite3PExpr(pParse, TK_NOT, A.pExpr, 0, 0);
A.zStart = X.zStart; A.zStart = X.zStart;
A.zEnd = E.zEnd; A.zEnd = E.zEnd;
if( A.pExpr ) A.pExpr->flags |= EP_InfixFunc; if( A.pExpr ) A.pExpr->flags |= EP_InfixFunc;
@ -1163,11 +1197,10 @@ nmnum(A) ::= ON(X). {A = X;}
nmnum(A) ::= DELETE(X). {A = X;} nmnum(A) ::= DELETE(X). {A = X;}
nmnum(A) ::= DEFAULT(X). {A = X;} nmnum(A) ::= DEFAULT(X). {A = X;}
%endif SQLITE_OMIT_PRAGMA %endif SQLITE_OMIT_PRAGMA
plus_num(A) ::= plus_opt number(X). {A = X;} plus_num(A) ::= PLUS number(X). {A = X;}
plus_num(A) ::= number(X). {A = X;}
minus_num(A) ::= MINUS number(X). {A = X;} minus_num(A) ::= MINUS number(X). {A = X;}
number(A) ::= INTEGER|FLOAT(X). {A = X;} number(A) ::= INTEGER|FLOAT(X). {A = X;}
plus_opt ::= PLUS.
plus_opt ::= .
//////////////////////////// The CREATE TRIGGER command ///////////////////// //////////////////////////// The CREATE TRIGGER command /////////////////////
@ -1261,8 +1294,8 @@ trigger_cmd(A) ::=
// INSERT // INSERT
trigger_cmd(A) ::= trigger_cmd(A) ::=
insert_cmd(R) INTO trnm(X) inscollist_opt(F) VALUES LP itemlist(Y) RP. insert_cmd(R) INTO trnm(X) inscollist_opt(F) valuelist(Y).
{A = sqlite3TriggerInsertStep(pParse->db, &X, F, Y, 0, R);} {A = sqlite3TriggerInsertStep(pParse->db, &X, F, Y.pList, Y.pSelect, R);}
trigger_cmd(A) ::= insert_cmd(R) INTO trnm(X) inscollist_opt(F) select(S). trigger_cmd(A) ::= insert_cmd(R) INTO trnm(X) inscollist_opt(F) select(S).
{A = sqlite3TriggerInsertStep(pParse->db, &X, F, 0, S, R);} {A = sqlite3TriggerInsertStep(pParse->db, &X, F, 0, S, R);}
@ -1356,8 +1389,9 @@ kwcolumn_opt ::= COLUMNKW.
%ifndef SQLITE_OMIT_VIRTUALTABLE %ifndef SQLITE_OMIT_VIRTUALTABLE
cmd ::= create_vtab. {sqlite3VtabFinishParse(pParse,0);} cmd ::= create_vtab. {sqlite3VtabFinishParse(pParse,0);}
cmd ::= create_vtab LP vtabarglist RP(X). {sqlite3VtabFinishParse(pParse,&X);} cmd ::= create_vtab LP vtabarglist RP(X). {sqlite3VtabFinishParse(pParse,&X);}
create_vtab ::= createkw VIRTUAL TABLE nm(X) dbnm(Y) USING nm(Z). { create_vtab ::= createkw VIRTUAL TABLE ifnotexists(E)
sqlite3VtabBeginParse(pParse, &X, &Y, &Z); nm(X) dbnm(Y) USING nm(Z). {
sqlite3VtabBeginParse(pParse, &X, &Y, &Z, E);
} }
vtabarglist ::= vtabarg. vtabarglist ::= vtabarg.
vtabarglist ::= vtabarglist COMMA vtabarg. vtabarglist ::= vtabarglist COMMA vtabarg.

View File

@ -27,8 +27,8 @@ struct PgHdr {
void *pData; /* Page data */ void *pData; /* Page data */
void *pExtra; /* Extra content */ void *pExtra; /* Extra content */
PgHdr *pDirty; /* Transient list of dirty pages */ PgHdr *pDirty; /* Transient list of dirty pages */
Pgno pgno; /* Page number for this page */
Pager *pPager; /* The pager this page is part of */ Pager *pPager; /* The pager this page is part of */
Pgno pgno; /* Page number for this page */
#ifdef SQLITE_CHECK_PAGES #ifdef SQLITE_CHECK_PAGES
u32 pageHash; /* Hash of page content */ u32 pageHash; /* Hash of page content */
#endif #endif

View File

@ -76,6 +76,7 @@ struct PCache1 {
unsigned int nMin; /* Minimum number of pages reserved */ unsigned int nMin; /* Minimum number of pages reserved */
unsigned int nMax; /* Configured "cache_size" value */ unsigned int nMax; /* Configured "cache_size" value */
unsigned int n90pct; /* nMax*9/10 */ unsigned int n90pct; /* nMax*9/10 */
unsigned int iMaxKey; /* Largest key seen since xTruncate() */
/* Hash table of all pages. The following variables may only be accessed /* Hash table of all pages. The following variables may only be accessed
** when the accessor is holding the PGroup mutex. ** when the accessor is holding the PGroup mutex.
@ -84,8 +85,6 @@ struct PCache1 {
unsigned int nPage; /* Total number of pages in apHash */ unsigned int nPage; /* Total number of pages in apHash */
unsigned int nHash; /* Number of slots in apHash[] */ unsigned int nHash; /* Number of slots in apHash[] */
PgHdr1 **apHash; /* Hash table for fast lookup by key */ PgHdr1 **apHash; /* Hash table for fast lookup by key */
unsigned int iMaxKey; /* Largest key seen since xTruncate() */
}; };
/* /*
@ -129,8 +128,8 @@ static SQLITE_WSD struct PCacheGlobal {
void *pStart, *pEnd; /* Bounds of pagecache malloc range */ void *pStart, *pEnd; /* Bounds of pagecache malloc range */
/* Above requires no mutex. Use mutex below for variable that follow. */ /* Above requires no mutex. Use mutex below for variable that follow. */
sqlite3_mutex *mutex; /* Mutex for accessing the following: */ sqlite3_mutex *mutex; /* Mutex for accessing the following: */
int nFreeSlot; /* Number of unused pcache slots */
PgFreeslot *pFree; /* Free page blocks */ PgFreeslot *pFree; /* Free page blocks */
int nFreeSlot; /* Number of unused pcache slots */
/* The following value requires a mutex to change. We skip the mutex on /* The following value requires a mutex to change. We skip the mutex on
** reading because (1) most platforms read a 32-bit integer atomically and ** reading because (1) most platforms read a 32-bit integer atomically and
** (2) even if an incorrect value is read, no great harm is done since this ** (2) even if an incorrect value is read, no great harm is done since this

View File

@ -16,14 +16,15 @@
/* /*
** Interpret the given string as a safety level. Return 0 for OFF, ** Interpret the given string as a safety level. Return 0 for OFF,
** 1 for ON or NORMAL and 2 for FULL. Return 1 for an empty or ** 1 for ON or NORMAL and 2 for FULL. Return 1 for an empty or
** unrecognized string argument. ** unrecognized string argument. The FULL option is disallowed
** if the omitFull parameter it 1.
** **
** Note that the values returned are one less that the values that ** Note that the values returned are one less that the values that
** should be passed into sqlite3BtreeSetSafetyLevel(). The is done ** should be passed into sqlite3BtreeSetSafetyLevel(). The is done
** to support legacy SQL code. The safety level used to be boolean ** to support legacy SQL code. The safety level used to be boolean
** and older scripts may have used numbers 0 for OFF and 1 for ON. ** and older scripts may have used numbers 0 for OFF and 1 for ON.
*/ */
static u8 getSafetyLevel(const char *z){ static u8 getSafetyLevel(const char *z, int omitFull, int dflt){
/* 123456789 123456789 */ /* 123456789 123456789 */
static const char zText[] = "onoffalseyestruefull"; static const char zText[] = "onoffalseyestruefull";
static const u8 iOffset[] = {0, 1, 2, 4, 9, 12, 16}; static const u8 iOffset[] = {0, 1, 2, 4, 9, 12, 16};
@ -34,19 +35,19 @@ static u8 getSafetyLevel(const char *z){
return (u8)sqlite3Atoi(z); return (u8)sqlite3Atoi(z);
} }
n = sqlite3Strlen30(z); n = sqlite3Strlen30(z);
for(i=0; i<ArraySize(iLength); i++){ for(i=0; i<ArraySize(iLength)-omitFull; i++){
if( iLength[i]==n && sqlite3StrNICmp(&zText[iOffset[i]],z,n)==0 ){ if( iLength[i]==n && sqlite3StrNICmp(&zText[iOffset[i]],z,n)==0 ){
return iValue[i]; return iValue[i];
} }
} }
return 1; return dflt;
} }
/* /*
** Interpret the given string as a boolean value. ** Interpret the given string as a boolean value.
*/ */
u8 sqlite3GetBoolean(const char *z){ u8 sqlite3GetBoolean(const char *z, int dflt){
return getSafetyLevel(z)&1; return getSafetyLevel(z,1,dflt)!=0;
} }
/* The sqlite3GetBoolean() function is used by other modules but the /* The sqlite3GetBoolean() function is used by other modules but the
@ -189,7 +190,6 @@ static int flagPragma(Parse *pParse, const char *zLeft, const char *zRight){
#endif #endif
/* The following is VERY experimental */ /* The following is VERY experimental */
{ "writable_schema", SQLITE_WriteSchema|SQLITE_RecoveryMode }, { "writable_schema", SQLITE_WriteSchema|SQLITE_RecoveryMode },
{ "omit_readlock", SQLITE_NoReadlock },
/* TODO: Maybe it shouldn't be possible to change the ReadUncommitted /* TODO: Maybe it shouldn't be possible to change the ReadUncommitted
** flag if there are any active statements. */ ** flag if there are any active statements. */
@ -221,7 +221,7 @@ static int flagPragma(Parse *pParse, const char *zLeft, const char *zRight){
mask &= ~(SQLITE_ForeignKeys); mask &= ~(SQLITE_ForeignKeys);
} }
if( sqlite3GetBoolean(zRight) ){ if( sqlite3GetBoolean(zRight, 0) ){
db->flags |= mask; db->flags |= mask;
}else{ }else{
db->flags &= ~mask; db->flags &= ~mask;
@ -312,9 +312,12 @@ void sqlite3Pragma(
const char *zDb = 0; /* The database name */ const char *zDb = 0; /* The database name */
Token *pId; /* Pointer to <id> token */ Token *pId; /* Pointer to <id> token */
int iDb; /* Database index for <database> */ int iDb; /* Database index for <database> */
sqlite3 *db = pParse->db; char *aFcntl[4]; /* Argument to SQLITE_FCNTL_PRAGMA */
Db *pDb; int rc; /* return value form SQLITE_FCNTL_PRAGMA */
Vdbe *v = pParse->pVdbe = sqlite3VdbeCreate(db); sqlite3 *db = pParse->db; /* The database connection */
Db *pDb; /* The specific database being pragmaed */
Vdbe *v = pParse->pVdbe = sqlite3VdbeCreate(db); /* Prepared statement */
if( v==0 ) return; if( v==0 ) return;
sqlite3VdbeRunOnlyOnce(v); sqlite3VdbeRunOnlyOnce(v);
pParse->nMem = 2; pParse->nMem = 2;
@ -345,6 +348,34 @@ void sqlite3Pragma(
if( sqlite3AuthCheck(pParse, SQLITE_PRAGMA, zLeft, zRight, zDb) ){ if( sqlite3AuthCheck(pParse, SQLITE_PRAGMA, zLeft, zRight, zDb) ){
goto pragma_out; goto pragma_out;
} }
/* Send an SQLITE_FCNTL_PRAGMA file-control to the underlying VFS
** connection. If it returns SQLITE_OK, then assume that the VFS
** handled the pragma and generate a no-op prepared statement.
*/
aFcntl[0] = 0;
aFcntl[1] = zLeft;
aFcntl[2] = zRight;
aFcntl[3] = 0;
rc = sqlite3_file_control(db, zDb, SQLITE_FCNTL_PRAGMA, (void*)aFcntl);
if( rc==SQLITE_OK ){
if( aFcntl[0] ){
int mem = ++pParse->nMem;
sqlite3VdbeAddOp4(v, OP_String8, 0, mem, 0, aFcntl[0], 0);
sqlite3VdbeSetNumCols(v, 1);
sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "result", SQLITE_STATIC);
sqlite3VdbeAddOp2(v, OP_ResultRow, mem, 1);
sqlite3_free(aFcntl[0]);
}
}else if( rc!=SQLITE_NOTFOUND ){
if( aFcntl[0] ){
sqlite3ErrorMsg(pParse, "%s", aFcntl[0]);
sqlite3_free(aFcntl[0]);
}
pParse->nErr++;
pParse->rc = rc;
}else
#if !defined(SQLITE_OMIT_PAGER_PRAGMAS) && !defined(SQLITE_OMIT_DEPRECATED) #if !defined(SQLITE_OMIT_PAGER_PRAGMAS) && !defined(SQLITE_OMIT_DEPRECATED)
/* /*
@ -437,7 +468,7 @@ void sqlite3Pragma(
int b = -1; int b = -1;
assert( pBt!=0 ); assert( pBt!=0 );
if( zRight ){ if( zRight ){
b = sqlite3GetBoolean(zRight); b = sqlite3GetBoolean(zRight, 0);
} }
if( pId2->n==0 && b>=0 ){ if( pId2->n==0 && b>=0 ){
int ii; int ii;
@ -632,7 +663,7 @@ void sqlite3Pragma(
** creates the database file. It is important that it is created ** creates the database file. It is important that it is created
** as an auto-vacuum capable db. ** as an auto-vacuum capable db.
*/ */
int rc = sqlite3BtreeSetAutoVacuum(pBt, eAuto); rc = sqlite3BtreeSetAutoVacuum(pBt, eAuto);
if( rc==SQLITE_OK && (eAuto==1 || eAuto==2) ){ if( rc==SQLITE_OK && (eAuto==1 || eAuto==2) ){
/* When setting the auto_vacuum mode to either "full" or /* When setting the auto_vacuum mode to either "full" or
** "incremental", write the value of meta[6] in the database ** "incremental", write the value of meta[6] in the database
@ -750,7 +781,6 @@ void sqlite3Pragma(
}else{ }else{
#ifndef SQLITE_OMIT_WSD #ifndef SQLITE_OMIT_WSD
if( zRight[0] ){ if( zRight[0] ){
int rc;
int res; int res;
rc = sqlite3OsAccess(db->pVfs, zRight, SQLITE_ACCESS_READWRITE, &res); rc = sqlite3OsAccess(db->pVfs, zRight, SQLITE_ACCESS_READWRITE, &res);
if( rc!=SQLITE_OK || res==0 ){ if( rc!=SQLITE_OK || res==0 ){
@ -842,7 +872,7 @@ void sqlite3Pragma(
sqlite3ErrorMsg(pParse, sqlite3ErrorMsg(pParse,
"Safety level may not be changed inside a transaction"); "Safety level may not be changed inside a transaction");
}else{ }else{
pDb->safety_level = getSafetyLevel(zRight)+1; pDb->safety_level = getSafetyLevel(zRight,0,1)+1;
} }
} }
}else }else
@ -1041,7 +1071,7 @@ void sqlite3Pragma(
#ifndef NDEBUG #ifndef NDEBUG
if( sqlite3StrICmp(zLeft, "parser_trace")==0 ){ if( sqlite3StrICmp(zLeft, "parser_trace")==0 ){
if( zRight ){ if( zRight ){
if( sqlite3GetBoolean(zRight) ){ if( sqlite3GetBoolean(zRight, 0) ){
sqlite3ParserTrace(stderr, "parser: "); sqlite3ParserTrace(stderr, "parser: ");
}else{ }else{
sqlite3ParserTrace(0, 0); sqlite3ParserTrace(0, 0);
@ -1055,7 +1085,7 @@ void sqlite3Pragma(
*/ */
if( sqlite3StrICmp(zLeft, "case_sensitive_like")==0 ){ if( sqlite3StrICmp(zLeft, "case_sensitive_like")==0 ){
if( zRight ){ if( zRight ){
sqlite3RegisterLikeFunctions(db, sqlite3GetBoolean(zRight)); sqlite3RegisterLikeFunctions(db, sqlite3GetBoolean(zRight, 0));
} }
}else }else

View File

@ -533,7 +533,7 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
nId = sqlite3Strlen30(zId); nId = sqlite3Strlen30(zId);
pDef = sqlite3FindFunction(pParse->db, zId, nId, n, enc, 0); pDef = sqlite3FindFunction(pParse->db, zId, nId, n, enc, 0);
if( pDef==0 ){ if( pDef==0 ){
pDef = sqlite3FindFunction(pParse->db, zId, nId, -1, enc, 0); pDef = sqlite3FindFunction(pParse->db, zId, nId, -2, enc, 0);
if( pDef==0 ){ if( pDef==0 ){
no_such_func = 1; no_such_func = 1;
}else{ }else{
@ -883,7 +883,7 @@ static int resolveOrderGroupBy(
ExprList *pOrderBy, /* An ORDER BY or GROUP BY clause to resolve */ ExprList *pOrderBy, /* An ORDER BY or GROUP BY clause to resolve */
const char *zType /* Either "ORDER" or "GROUP", as appropriate */ const char *zType /* Either "ORDER" or "GROUP", as appropriate */
){ ){
int i; /* Loop counter */ int i, j; /* Loop counters */
int iCol; /* Column number */ int iCol; /* Column number */
struct ExprList_item *pItem; /* A term of the ORDER BY clause */ struct ExprList_item *pItem; /* A term of the ORDER BY clause */
Parse *pParse; /* Parsing context */ Parse *pParse; /* Parsing context */
@ -920,6 +920,11 @@ static int resolveOrderGroupBy(
if( sqlite3ResolveExprNames(pNC, pE) ){ if( sqlite3ResolveExprNames(pNC, pE) ){
return 1; return 1;
} }
for(j=0; j<pSelect->pEList->nExpr; j++){
if( sqlite3ExprCompare(pE, pSelect->pEList->a[j].pExpr)==0 ){
pItem->iOrderByCol = j+1;
}
}
} }
return sqlite3ResolveOrderGroupBy(pParse, pSelect, pOrderBy, zType); return sqlite3ResolveOrderGroupBy(pParse, pSelect, pOrderBy, zType);
} }

View File

@ -76,6 +76,11 @@
/* /*
** Each entry in a RowSet is an instance of the following object. ** Each entry in a RowSet is an instance of the following object.
**
** This same object is reused to store a linked list of trees of RowSetEntry
** objects. In that alternative use, pRight points to the next entry
** in the list, pLeft points to the tree, and v is unused. The
** RowSet.pForest value points to the head of this forest list.
*/ */
struct RowSetEntry { struct RowSetEntry {
i64 v; /* ROWID value for this entry */ i64 v; /* ROWID value for this entry */
@ -105,12 +110,18 @@ struct RowSet {
struct RowSetEntry *pEntry; /* List of entries using pRight */ struct RowSetEntry *pEntry; /* List of entries using pRight */
struct RowSetEntry *pLast; /* Last entry on the pEntry list */ struct RowSetEntry *pLast; /* Last entry on the pEntry list */
struct RowSetEntry *pFresh; /* Source of new entry objects */ struct RowSetEntry *pFresh; /* Source of new entry objects */
struct RowSetEntry *pTree; /* Binary tree of entries */ struct RowSetEntry *pForest; /* List of binary trees of entries */
u16 nFresh; /* Number of objects on pFresh */ u16 nFresh; /* Number of objects on pFresh */
u8 isSorted; /* True if pEntry is sorted */ u8 rsFlags; /* Various flags */
u8 iBatch; /* Current insert batch */ u8 iBatch; /* Current insert batch */
}; };
/*
** Allowed values for RowSet.rsFlags
*/
#define ROWSET_SORTED 0x01 /* True if RowSet.pEntry is sorted */
#define ROWSET_NEXT 0x02 /* True if sqlite3RowSetNext() has been called */
/* /*
** Turn bulk memory into a RowSet object. N bytes of memory ** Turn bulk memory into a RowSet object. N bytes of memory
** are available at pSpace. The db pointer is used as a memory context ** are available at pSpace. The db pointer is used as a memory context
@ -131,10 +142,10 @@ RowSet *sqlite3RowSetInit(sqlite3 *db, void *pSpace, unsigned int N){
p->db = db; p->db = db;
p->pEntry = 0; p->pEntry = 0;
p->pLast = 0; p->pLast = 0;
p->pTree = 0; p->pForest = 0;
p->pFresh = (struct RowSetEntry*)(ROUND8(sizeof(*p)) + (char*)p); p->pFresh = (struct RowSetEntry*)(ROUND8(sizeof(*p)) + (char*)p);
p->nFresh = (u16)((N - ROUND8(sizeof(*p)))/sizeof(struct RowSetEntry)); p->nFresh = (u16)((N - ROUND8(sizeof(*p)))/sizeof(struct RowSetEntry));
p->isSorted = 1; p->rsFlags = ROWSET_SORTED;
p->iBatch = 0; p->iBatch = 0;
return p; return p;
} }
@ -154,8 +165,33 @@ void sqlite3RowSetClear(RowSet *p){
p->nFresh = 0; p->nFresh = 0;
p->pEntry = 0; p->pEntry = 0;
p->pLast = 0; p->pLast = 0;
p->pTree = 0; p->pForest = 0;
p->isSorted = 1; p->rsFlags = ROWSET_SORTED;
}
/*
** Allocate a new RowSetEntry object that is associated with the
** given RowSet. Return a pointer to the new and completely uninitialized
** objected.
**
** In an OOM situation, the RowSet.db->mallocFailed flag is set and this
** routine returns NULL.
*/
static struct RowSetEntry *rowSetEntryAlloc(RowSet *p){
assert( p!=0 );
if( p->nFresh==0 ){
struct RowSetChunk *pNew;
pNew = sqlite3DbMallocRaw(p->db, sizeof(*pNew));
if( pNew==0 ){
return 0;
}
pNew->pNextChunk = p->pChunk;
p->pChunk = pNew;
p->pFresh = pNew->aEntry;
p->nFresh = ROWSET_ENTRY_PER_CHUNK;
}
p->nFresh--;
return p->pFresh++;
} }
/* /*
@ -167,30 +203,21 @@ void sqlite3RowSetClear(RowSet *p){
void sqlite3RowSetInsert(RowSet *p, i64 rowid){ void sqlite3RowSetInsert(RowSet *p, i64 rowid){
struct RowSetEntry *pEntry; /* The new entry */ struct RowSetEntry *pEntry; /* The new entry */
struct RowSetEntry *pLast; /* The last prior entry */ struct RowSetEntry *pLast; /* The last prior entry */
assert( p!=0 );
if( p->nFresh==0 ){ /* This routine is never called after sqlite3RowSetNext() */
struct RowSetChunk *pNew; assert( p!=0 && (p->rsFlags & ROWSET_NEXT)==0 );
pNew = sqlite3DbMallocRaw(p->db, sizeof(*pNew));
if( pNew==0 ){ pEntry = rowSetEntryAlloc(p);
return; if( pEntry==0 ) return;
}
pNew->pNextChunk = p->pChunk;
p->pChunk = pNew;
p->pFresh = pNew->aEntry;
p->nFresh = ROWSET_ENTRY_PER_CHUNK;
}
pEntry = p->pFresh++;
p->nFresh--;
pEntry->v = rowid; pEntry->v = rowid;
pEntry->pRight = 0; pEntry->pRight = 0;
pLast = p->pLast; pLast = p->pLast;
if( pLast ){ if( pLast ){
if( p->isSorted && rowid<=pLast->v ){ if( (p->rsFlags & ROWSET_SORTED)!=0 && rowid<=pLast->v ){
p->isSorted = 0; p->rsFlags &= ~ROWSET_SORTED;
} }
pLast->pRight = pEntry; pLast->pRight = pEntry;
}else{ }else{
assert( p->pEntry==0 ); /* Fires if INSERT after SMALLEST */
p->pEntry = pEntry; p->pEntry = pEntry;
} }
p->pLast = pEntry; p->pLast = pEntry;
@ -202,7 +229,7 @@ void sqlite3RowSetInsert(RowSet *p, i64 rowid){
** The input lists are connected via pRight pointers and are ** The input lists are connected via pRight pointers and are
** assumed to each already be in sorted order. ** assumed to each already be in sorted order.
*/ */
static struct RowSetEntry *rowSetMerge( static struct RowSetEntry *rowSetEntryMerge(
struct RowSetEntry *pA, /* First sorted list to be merged */ struct RowSetEntry *pA, /* First sorted list to be merged */
struct RowSetEntry *pB /* Second sorted list to be merged */ struct RowSetEntry *pB /* Second sorted list to be merged */
){ ){
@ -236,32 +263,29 @@ static struct RowSetEntry *rowSetMerge(
} }
/* /*
** Sort all elements on the pEntry list of the RowSet into ascending order. ** Sort all elements on the list of RowSetEntry objects into order of
** increasing v.
*/ */
static void rowSetSort(RowSet *p){ static struct RowSetEntry *rowSetEntrySort(struct RowSetEntry *pIn){
unsigned int i; unsigned int i;
struct RowSetEntry *pEntry; struct RowSetEntry *pNext, *aBucket[40];
struct RowSetEntry *aBucket[40];
assert( p->isSorted==0 );
memset(aBucket, 0, sizeof(aBucket)); memset(aBucket, 0, sizeof(aBucket));
while( p->pEntry ){ while( pIn ){
pEntry = p->pEntry; pNext = pIn->pRight;
p->pEntry = pEntry->pRight; pIn->pRight = 0;
pEntry->pRight = 0;
for(i=0; aBucket[i]; i++){ for(i=0; aBucket[i]; i++){
pEntry = rowSetMerge(aBucket[i], pEntry); pIn = rowSetEntryMerge(aBucket[i], pIn);
aBucket[i] = 0; aBucket[i] = 0;
} }
aBucket[i] = pEntry; aBucket[i] = pIn;
pIn = pNext;
} }
pEntry = 0; pIn = 0;
for(i=0; i<sizeof(aBucket)/sizeof(aBucket[0]); i++){ for(i=0; i<sizeof(aBucket)/sizeof(aBucket[0]); i++){
pEntry = rowSetMerge(pEntry, aBucket[i]); pIn = rowSetEntryMerge(pIn, aBucket[i]);
} }
p->pEntry = pEntry; return pIn;
p->pLast = 0;
p->isSorted = 1;
} }
@ -355,20 +379,37 @@ static struct RowSetEntry *rowSetListToTree(struct RowSetEntry *pList){
} }
/* /*
** Convert the list in p->pEntry into a sorted list if it is not ** Take all the entries on p->pEntry and on the trees in p->pForest and
** sorted already. If there is a binary tree on p->pTree, then ** sort them all together into one big ordered list on p->pEntry.
** convert it into a list too and merge it into the p->pEntry list. **
** This routine should only be called once in the life of a RowSet.
*/ */
static void rowSetToList(RowSet *p){ static void rowSetToList(RowSet *p){
if( !p->isSorted ){
rowSetSort(p); /* This routine is called only once */
assert( p!=0 && (p->rsFlags & ROWSET_NEXT)==0 );
if( (p->rsFlags & ROWSET_SORTED)==0 ){
p->pEntry = rowSetEntrySort(p->pEntry);
} }
if( p->pTree ){
struct RowSetEntry *pHead, *pTail; /* While this module could theoretically support it, sqlite3RowSetNext()
rowSetTreeToList(p->pTree, &pHead, &pTail); ** is never called after sqlite3RowSetText() for the same RowSet. So
p->pTree = 0; ** there is never a forest to deal with. Should this change, simply
p->pEntry = rowSetMerge(p->pEntry, pHead); ** remove the assert() and the #if 0. */
assert( p->pForest==0 );
#if 0
while( p->pForest ){
struct RowSetEntry *pTree = p->pForest->pLeft;
if( pTree ){
struct RowSetEntry *pHead, *pTail;
rowSetTreeToList(pTree, &pHead, &pTail);
p->pEntry = rowSetEntryMerge(p->pEntry, pHead);
}
p->pForest = p->pForest->pRight;
} }
#endif
p->rsFlags |= ROWSET_NEXT; /* Verify this routine is never called again */
} }
/* /*
@ -380,7 +421,12 @@ static void rowSetToList(RowSet *p){
** routine may not be called again. ** routine may not be called again.
*/ */
int sqlite3RowSetNext(RowSet *p, i64 *pRowid){ int sqlite3RowSetNext(RowSet *p, i64 *pRowid){
rowSetToList(p); assert( p!=0 );
/* Merge the forest into a single sorted list on first call */
if( (p->rsFlags & ROWSET_NEXT)==0 ) rowSetToList(p);
/* Return the next entry on the list */
if( p->pEntry ){ if( p->pEntry ){
*pRowid = p->pEntry->v; *pRowid = p->pEntry->v;
p->pEntry = p->pEntry->pRight; p->pEntry = p->pEntry->pRight;
@ -396,26 +442,66 @@ int sqlite3RowSetNext(RowSet *p, i64 *pRowid){
/* /*
** Check to see if element iRowid was inserted into the the rowset as ** Check to see if element iRowid was inserted into the the rowset as
** part of any insert batch prior to iBatch. Return 1 or 0. ** part of any insert batch prior to iBatch. Return 1 or 0.
**
** If this is the first test of a new batch and if there exist entires
** on pRowSet->pEntry, then sort those entires into the forest at
** pRowSet->pForest so that they can be tested.
*/ */
int sqlite3RowSetTest(RowSet *pRowSet, u8 iBatch, sqlite3_int64 iRowid){ int sqlite3RowSetTest(RowSet *pRowSet, u8 iBatch, sqlite3_int64 iRowid){
struct RowSetEntry *p; struct RowSetEntry *p, *pTree;
/* This routine is never called after sqlite3RowSetNext() */
assert( pRowSet!=0 && (pRowSet->rsFlags & ROWSET_NEXT)==0 );
/* Sort entries into the forest on the first test of a new batch
*/
if( iBatch!=pRowSet->iBatch ){ if( iBatch!=pRowSet->iBatch ){
if( pRowSet->pEntry ){ p = pRowSet->pEntry;
rowSetToList(pRowSet); if( p ){
pRowSet->pTree = rowSetListToTree(pRowSet->pEntry); struct RowSetEntry **ppPrevTree = &pRowSet->pForest;
if( (pRowSet->rsFlags & ROWSET_SORTED)==0 ){
p = rowSetEntrySort(p);
}
for(pTree = pRowSet->pForest; pTree; pTree=pTree->pRight){
ppPrevTree = &pTree->pRight;
if( pTree->pLeft==0 ){
pTree->pLeft = rowSetListToTree(p);
break;
}else{
struct RowSetEntry *pAux, *pTail;
rowSetTreeToList(pTree->pLeft, &pAux, &pTail);
pTree->pLeft = 0;
p = rowSetEntryMerge(pAux, p);
}
}
if( pTree==0 ){
*ppPrevTree = pTree = rowSetEntryAlloc(pRowSet);
if( pTree ){
pTree->v = 0;
pTree->pRight = 0;
pTree->pLeft = rowSetListToTree(p);
}
}
pRowSet->pEntry = 0; pRowSet->pEntry = 0;
pRowSet->pLast = 0; pRowSet->pLast = 0;
pRowSet->rsFlags |= ROWSET_SORTED;
} }
pRowSet->iBatch = iBatch; pRowSet->iBatch = iBatch;
} }
p = pRowSet->pTree;
while( p ){ /* Test to see if the iRowid value appears anywhere in the forest.
if( p->v<iRowid ){ ** Return 1 if it does and 0 if not.
p = p->pRight; */
}else if( p->v>iRowid ){ for(pTree = pRowSet->pForest; pTree; pTree=pTree->pRight){
p = p->pLeft; p = pTree->pLeft;
}else{ while( p ){
return 1; if( p->v<iRowid ){
p = p->pRight;
}else if( p->v>iRowid ){
p = p->pLeft;
}else{
return 1;
}
} }
} }
return 0; return 0;

View File

@ -73,6 +73,7 @@ Select *sqlite3SelectNew(
pEList = sqlite3ExprListAppend(pParse, 0, sqlite3Expr(db,TK_ALL,0)); pEList = sqlite3ExprListAppend(pParse, 0, sqlite3Expr(db,TK_ALL,0));
} }
pNew->pEList = pEList; pNew->pEList = pEList;
if( pSrc==0 ) pSrc = sqlite3DbMallocZero(db, sizeof(*pSrc));
pNew->pSrc = pSrc; pNew->pSrc = pSrc;
pNew->pWhere = pWhere; pNew->pWhere = pWhere;
pNew->pGroupBy = pGroupBy; pNew->pGroupBy = pGroupBy;
@ -1257,9 +1258,17 @@ static int selectColumnsFromExprList(
char *zName; /* Column name */ char *zName; /* Column name */
int nName; /* Size of name in zName[] */ int nName; /* Size of name in zName[] */
*pnCol = nCol = pEList->nExpr; if( pEList ){
aCol = *paCol = sqlite3DbMallocZero(db, sizeof(aCol[0])*nCol); nCol = pEList->nExpr;
if( aCol==0 ) return SQLITE_NOMEM; aCol = sqlite3DbMallocZero(db, sizeof(aCol[0])*nCol);
testcase( aCol==0 );
}else{
nCol = 0;
aCol = 0;
}
*pnCol = nCol;
*paCol = aCol;
for(i=0, pCol=aCol; i<nCol; i++, pCol++){ for(i=0, pCol=aCol; i<nCol; i++, pCol++){
/* Get an appropriate name for the column /* Get an appropriate name for the column
*/ */
@ -1611,8 +1620,12 @@ static int multiSelect(
*/ */
assert( p->pEList && pPrior->pEList ); assert( p->pEList && pPrior->pEList );
if( p->pEList->nExpr!=pPrior->pEList->nExpr ){ if( p->pEList->nExpr!=pPrior->pEList->nExpr ){
sqlite3ErrorMsg(pParse, "SELECTs to the left and right of %s" if( p->selFlags & SF_Values ){
" do not have the same number of result columns", selectOpName(p->op)); sqlite3ErrorMsg(pParse, "all VALUES must have the same number of terms");
}else{
sqlite3ErrorMsg(pParse, "SELECTs to the left and right of %s"
" do not have the same number of result columns", selectOpName(p->op));
}
rc = 1; rc = 1;
goto multi_select_end; goto multi_select_end;
} }
@ -2228,7 +2241,7 @@ static int multiSelectOrderBy(
pNew->flags |= EP_IntValue; pNew->flags |= EP_IntValue;
pNew->u.iValue = i; pNew->u.iValue = i;
pOrderBy = sqlite3ExprListAppend(pParse, pOrderBy, pNew); pOrderBy = sqlite3ExprListAppend(pParse, pOrderBy, pNew);
pOrderBy->a[nOrderBy++].iOrderByCol = (u16)i; if( pOrderBy ) pOrderBy->a[nOrderBy++].iOrderByCol = (u16)i;
} }
} }
} }
@ -2838,7 +2851,8 @@ static int flattenSubquery(
/* Authorize the subquery */ /* Authorize the subquery */
pParse->zAuthContext = pSubitem->zName; pParse->zAuthContext = pSubitem->zName;
sqlite3AuthCheck(pParse, SQLITE_SELECT, 0, 0, 0); TESTONLY(i =) sqlite3AuthCheck(pParse, SQLITE_SELECT, 0, 0, 0);
testcase( i==SQLITE_DENY );
pParse->zAuthContext = zSavedAuthContext; pParse->zAuthContext = zSavedAuthContext;
/* If the sub-query is a compound SELECT statement, then (by restrictions /* If the sub-query is a compound SELECT statement, then (by restrictions
@ -3591,6 +3605,8 @@ static void finalizeAggFunctions(Parse *pParse, AggInfo *pAggInfo){
static void updateAccumulator(Parse *pParse, AggInfo *pAggInfo){ static void updateAccumulator(Parse *pParse, AggInfo *pAggInfo){
Vdbe *v = pParse->pVdbe; Vdbe *v = pParse->pVdbe;
int i; int i;
int regHit = 0;
int addrHitTest = 0;
struct AggInfo_func *pF; struct AggInfo_func *pF;
struct AggInfo_col *pC; struct AggInfo_col *pC;
@ -3626,7 +3642,8 @@ static void updateAccumulator(Parse *pParse, AggInfo *pAggInfo){
if( !pColl ){ if( !pColl ){
pColl = pParse->db->pDfltColl; pColl = pParse->db->pDfltColl;
} }
sqlite3VdbeAddOp4(v, OP_CollSeq, 0, 0, 0, (char *)pColl, P4_COLLSEQ); if( regHit==0 && pAggInfo->nAccumulator ) regHit = ++pParse->nMem;
sqlite3VdbeAddOp4(v, OP_CollSeq, regHit, 0, 0, (char *)pColl, P4_COLLSEQ);
} }
sqlite3VdbeAddOp4(v, OP_AggStep, 0, regAgg, pF->iMem, sqlite3VdbeAddOp4(v, OP_AggStep, 0, regAgg, pF->iMem,
(void*)pF->pFunc, P4_FUNCDEF); (void*)pF->pFunc, P4_FUNCDEF);
@ -3649,12 +3666,18 @@ static void updateAccumulator(Parse *pParse, AggInfo *pAggInfo){
** Another solution would be to change the OP_SCopy used to copy cached ** Another solution would be to change the OP_SCopy used to copy cached
** values to an OP_Copy. ** values to an OP_Copy.
*/ */
if( regHit ){
addrHitTest = sqlite3VdbeAddOp1(v, OP_If, regHit);
}
sqlite3ExprCacheClear(pParse); sqlite3ExprCacheClear(pParse);
for(i=0, pC=pAggInfo->aCol; i<pAggInfo->nAccumulator; i++, pC++){ for(i=0, pC=pAggInfo->aCol; i<pAggInfo->nAccumulator; i++, pC++){
sqlite3ExprCode(pParse, pC->pExpr, pC->iMem); sqlite3ExprCode(pParse, pC->pExpr, pC->iMem);
} }
pAggInfo->directMode = 0; pAggInfo->directMode = 0;
sqlite3ExprCacheClear(pParse); sqlite3ExprCacheClear(pParse);
if( addrHitTest ){
sqlite3VdbeJumpHere(v, addrHitTest);
}
} }
/* /*
@ -4214,7 +4237,7 @@ int sqlite3Select(
int r2; int r2;
r2 = sqlite3ExprCodeGetColumn(pParse, r2 = sqlite3ExprCodeGetColumn(pParse,
pCol->pTab, pCol->iColumn, pCol->iTable, r1); pCol->pTab, pCol->iColumn, pCol->iTable, r1, 0);
if( r1!=r2 ){ if( r1!=r2 ){
sqlite3VdbeAddOp2(v, OP_SCopy, r2, r1); sqlite3VdbeAddOp2(v, OP_SCopy, r2, r1);
} }
@ -4595,4 +4618,4 @@ void sqlite3ExplainSelect(Vdbe *pVdbe, Select *p){
/* End of the structure debug printing code /* End of the structure debug printing code
*****************************************************************************/ *****************************************************************************/
#endif /* defined(SQLITE_TEST) || defined(SQLITE_DEBUG) */ #endif /* defined(SQLITE_ENABLE_TREE_EXPLAIN) */

View File

@ -57,7 +57,7 @@
# include <readline/history.h> # include <readline/history.h>
#endif #endif
#if !defined(HAVE_EDITLINE) && (!defined(HAVE_READLINE) || HAVE_READLINE!=1) #if !defined(HAVE_EDITLINE) && (!defined(HAVE_READLINE) || HAVE_READLINE!=1)
# define readline(p) local_getline(p,stdin) # define readline(p) local_getline(p,stdin,0)
# define add_history(X) # define add_history(X)
# define read_history(X) # define read_history(X)
# define write_history(X) # define write_history(X)
@ -68,6 +68,8 @@
# include <io.h> # include <io.h>
#define isatty(h) _isatty(h) #define isatty(h) _isatty(h)
#define access(f,m) _access((f),(m)) #define access(f,m) _access((f),(m))
#define popen(a,b) _popen((a),(b))
#define pclose(x) _pclose(x)
#else #else
/* Make sure isatty() has a prototype. /* Make sure isatty() has a prototype.
*/ */
@ -334,10 +336,11 @@ static void shellstaticFunc(
** The interface is like "readline" but no command-line editing ** The interface is like "readline" but no command-line editing
** is done. ** is done.
*/ */
static char *local_getline(char *zPrompt, FILE *in){ static char *local_getline(char *zPrompt, FILE *in, int csvFlag){
char *zLine; char *zLine;
int nLine; int nLine;
int n; int n;
int inQuote = 0;
if( zPrompt && *zPrompt ){ if( zPrompt && *zPrompt ){
printf("%s",zPrompt); printf("%s",zPrompt);
@ -361,8 +364,11 @@ static char *local_getline(char *zPrompt, FILE *in){
zLine[n] = 0; zLine[n] = 0;
break; break;
} }
while( zLine[n] ){ n++; } while( zLine[n] ){
if( n>0 && zLine[n-1]=='\n' ){ if( zLine[n]=='"' ) inQuote = !inQuote;
n++;
}
if( n>0 && zLine[n-1]=='\n' && (!inQuote || !csvFlag) ){
n--; n--;
if( n>0 && zLine[n-1]=='\r' ) n--; if( n>0 && zLine[n-1]=='\r' ) n--;
zLine[n] = 0; zLine[n] = 0;
@ -383,7 +389,7 @@ static char *one_input_line(const char *zPrior, FILE *in){
char *zPrompt; char *zPrompt;
char *zResult; char *zResult;
if( in!=0 ){ if( in!=0 ){
return local_getline(0, in); return local_getline(0, in, 0);
} }
if( zPrior && zPrior[0] ){ if( zPrior && zPrior[0] ){
zPrompt = continuePrompt; zPrompt = continuePrompt;
@ -415,6 +421,7 @@ struct callback_data {
int statsOn; /* True to display memory stats before each finalize */ int statsOn; /* True to display memory stats before each finalize */
int cnt; /* Number of records displayed so far */ int cnt; /* Number of records displayed so far */
FILE *out; /* Write results here */ FILE *out; /* Write results here */
FILE *traceOut; /* Output for sqlite3_trace() */
int nErr; /* Number of errors seen */ int nErr; /* Number of errors seen */
int mode; /* An output mode setting */ int mode; /* An output mode setting */
int writableSchema; /* True if PRAGMA writable_schema=ON */ int writableSchema; /* True if PRAGMA writable_schema=ON */
@ -492,7 +499,7 @@ static void output_hex_blob(FILE *out, const void *pBlob, int nBlob){
int i; int i;
char *zBlob = (char *)pBlob; char *zBlob = (char *)pBlob;
fprintf(out,"X'"); fprintf(out,"X'");
for(i=0; i<nBlob; i++){ fprintf(out,"%02x",zBlob[i]); } for(i=0; i<nBlob; i++){ fprintf(out,"%02x",zBlob[i]&0xff); }
fprintf(out,"'"); fprintf(out,"'");
} }
@ -614,8 +621,7 @@ static const char needCsvQuote[] = {
/* /*
** Output a single term of CSV. Actually, p->separator is used for ** Output a single term of CSV. Actually, p->separator is used for
** the separator, which may or may not be a comma. p->nullvalue is ** the separator, which may or may not be a comma. p->nullvalue is
** the null value. Strings are quoted using ANSI-C rules. Numbers ** the null value. Strings are quoted if necessary.
** appear outside of quotes.
*/ */
static void output_csv(struct callback_data *p, const char *z, int bSep){ static void output_csv(struct callback_data *p, const char *z, int bSep){
FILE *out = p->out; FILE *out = p->out;
@ -934,11 +940,14 @@ static char *appendText(char *zIn, char const *zAppend, char quote){
/* /*
** Execute a query statement that has a single result column. Print ** Execute a query statement that will generate SQL output. Print
** that result column on a line by itself with a semicolon terminator. ** the result columns, comma-separated, on a line and then add a
** semicolon terminator to the end of that line.
** **
** This is used, for example, to show the schema of the database by ** If the number of columns is 1 and that column contains text "--"
** querying the SQLITE_MASTER table. ** then write the semicolon on a separate line. That way, if a
** "--" comment occurs at the end of the statement, the comment
** won't consume the semicolon terminator.
*/ */
static int run_table_dump_query( static int run_table_dump_query(
struct callback_data *p, /* Query context */ struct callback_data *p, /* Query context */
@ -947,6 +956,9 @@ static int run_table_dump_query(
){ ){
sqlite3_stmt *pSelect; sqlite3_stmt *pSelect;
int rc; int rc;
int nResult;
int i;
const char *z;
rc = sqlite3_prepare(p->db, zSelect, -1, &pSelect, 0); rc = sqlite3_prepare(p->db, zSelect, -1, &pSelect, 0);
if( rc!=SQLITE_OK || !pSelect ){ if( rc!=SQLITE_OK || !pSelect ){
fprintf(p->out, "/**** ERROR: (%d) %s *****/\n", rc, sqlite3_errmsg(p->db)); fprintf(p->out, "/**** ERROR: (%d) %s *****/\n", rc, sqlite3_errmsg(p->db));
@ -954,12 +966,24 @@ static int run_table_dump_query(
return rc; return rc;
} }
rc = sqlite3_step(pSelect); rc = sqlite3_step(pSelect);
nResult = sqlite3_column_count(pSelect);
while( rc==SQLITE_ROW ){ while( rc==SQLITE_ROW ){
if( zFirstRow ){ if( zFirstRow ){
fprintf(p->out, "%s", zFirstRow); fprintf(p->out, "%s", zFirstRow);
zFirstRow = 0; zFirstRow = 0;
} }
fprintf(p->out, "%s;\n", sqlite3_column_text(pSelect, 0)); z = (const char*)sqlite3_column_text(pSelect, 0);
fprintf(p->out, "%s", z);
for(i=1; i<nResult; i++){
fprintf(p->out, ",%s", sqlite3_column_text(pSelect, i));
}
if( z==0 ) z = "";
while( z[0] && (z[0]!='-' || z[1]!='-') ) z++;
if( z[0] ){
fprintf(p->out, "\n;\n");
}else{
fprintf(p->out, ";\n");
}
rc = sqlite3_step(pSelect); rc = sqlite3_step(pSelect);
} }
rc = sqlite3_finalize(pSelect); rc = sqlite3_finalize(pSelect);
@ -1056,6 +1080,9 @@ static int display_stats(
sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_MISS, &iCur, &iHiwtr, 1); sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_MISS, &iCur, &iHiwtr, 1);
fprintf(pArg->out, "Page cache misses: %d\n", iCur); fprintf(pArg->out, "Page cache misses: %d\n", iCur);
iHiwtr = iCur = -1; iHiwtr = iCur = -1;
sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_WRITE, &iCur, &iHiwtr, 1);
fprintf(pArg->out, "Page cache writes: %d\n", iCur);
iHiwtr = iCur = -1;
sqlite3_db_status(db, SQLITE_DBSTATUS_SCHEMA_USED, &iCur, &iHiwtr, bReset); sqlite3_db_status(db, SQLITE_DBSTATUS_SCHEMA_USED, &iCur, &iHiwtr, bReset);
fprintf(pArg->out, "Schema Heap Usage: %d bytes\n", iCur); fprintf(pArg->out, "Schema Heap Usage: %d bytes\n", iCur);
iHiwtr = iCur = -1; iHiwtr = iCur = -1;
@ -1278,9 +1305,12 @@ static int dump_callback(void *pArg, int nArg, char **azArg, char **azCol){
} }
zSelect = appendText(zSelect, "SELECT 'INSERT INTO ' || ", 0); zSelect = appendText(zSelect, "SELECT 'INSERT INTO ' || ", 0);
/* Always quote the table name, even if it appears to be pure ascii,
** in case it is a keyword. Ex: INSERT INTO "table" ... */
zTmp = appendText(zTmp, zTable, '"'); zTmp = appendText(zTmp, zTable, '"');
if( zTmp ){ if( zTmp ){
zSelect = appendText(zSelect, zTmp, '\''); zSelect = appendText(zSelect, zTmp, '\'');
free(zTmp);
} }
zSelect = appendText(zSelect, " || ' VALUES(' || ", 0); zSelect = appendText(zSelect, " || ' VALUES(' || ", 0);
rc = sqlite3_step(pTableInfo); rc = sqlite3_step(pTableInfo);
@ -1290,7 +1320,7 @@ static int dump_callback(void *pArg, int nArg, char **azArg, char **azCol){
zSelect = appendText(zSelect, zText, '"'); zSelect = appendText(zSelect, zText, '"');
rc = sqlite3_step(pTableInfo); rc = sqlite3_step(pTableInfo);
if( rc==SQLITE_ROW ){ if( rc==SQLITE_ROW ){
zSelect = appendText(zSelect, ") || ',' || ", 0); zSelect = appendText(zSelect, "), ", 0);
}else{ }else{
zSelect = appendText(zSelect, ") ", 0); zSelect = appendText(zSelect, ") ", 0);
} }
@ -1309,7 +1339,7 @@ static int dump_callback(void *pArg, int nArg, char **azArg, char **azCol){
zSelect = appendText(zSelect, " ORDER BY rowid DESC", 0); zSelect = appendText(zSelect, " ORDER BY rowid DESC", 0);
run_table_dump_query(p, zSelect, 0); run_table_dump_query(p, zSelect, 0);
} }
if( zSelect ) free(zSelect); free(zSelect);
} }
return 0; return 0;
} }
@ -1339,7 +1369,7 @@ static int run_schema_dump_query(
} }
zQ2 = malloc( len+100 ); zQ2 = malloc( len+100 );
if( zQ2==0 ) return rc; if( zQ2==0 ) return rc;
sqlite3_snprintf(sizeof(zQ2), zQ2, "%s ORDER BY rowid DESC", zQuery); sqlite3_snprintf(len+100, zQ2, "%s ORDER BY rowid DESC", zQuery);
rc = sqlite3_exec(p->db, zQ2, dump_callback, p, &zErr); rc = sqlite3_exec(p->db, zQ2, dump_callback, p, &zErr);
if( rc ){ if( rc ){
fprintf(p->out, "/****** ERROR: %s ******/\n", zErr); fprintf(p->out, "/****** ERROR: %s ******/\n", zErr);
@ -1405,6 +1435,7 @@ static char zHelp[] =
" If TABLE specified, only list tables matching\n" " If TABLE specified, only list tables matching\n"
" LIKE pattern TABLE.\n" " LIKE pattern TABLE.\n"
".timeout MS Try opening locked tables for MS milliseconds\n" ".timeout MS Try opening locked tables for MS milliseconds\n"
".trace FILE|off Output each SQL statement as it is run\n"
".vfsname ?AUX? Print the name of the VFS stack\n" ".vfsname ?AUX? Print the name of the VFS stack\n"
".width NUM1 NUM2 ... Set column widths for \"column\" mode\n" ".width NUM1 NUM2 ... Set column widths for \"column\" mode\n"
; ;
@ -1494,6 +1525,52 @@ static int booleanValue(char *zArg){
return val; return val;
} }
/*
** Close an output file, assuming it is not stderr or stdout
*/
static void output_file_close(FILE *f){
if( f && f!=stdout && f!=stderr ) fclose(f);
}
/*
** Try to open an output file. The names "stdout" and "stderr" are
** recognized and do the right thing. NULL is returned if the output
** filename is "off".
*/
static FILE *output_file_open(const char *zFile){
FILE *f;
if( strcmp(zFile,"stdout")==0 ){
f = stdout;
}else if( strcmp(zFile, "stderr")==0 ){
f = stderr;
}else if( strcmp(zFile, "off")==0 ){
f = 0;
}else{
f = fopen(zFile, "wb");
if( f==0 ){
fprintf(stderr, "Error: cannot open \"%s\"\n", zFile);
}
}
return f;
}
/*
** A routine for handling output from sqlite3_trace().
*/
static void sql_trace_callback(void *pArg, const char *z){
FILE *f = (FILE*)pArg;
if( f ) fprintf(f, "%s\n", z);
}
/*
** A no-op routine that runs with the ".breakpoint" doc-command. This is
** a useful spot to set a debugger breakpoint.
*/
static void test_breakpoint(void){
static int nCall = 0;
nCall++;
}
/* /*
** If an input line begins with "." then invoke this routine to ** If an input line begins with "." then invoke this routine to
** process that line. ** process that line.
@ -1573,6 +1650,13 @@ static int do_meta_command(char *zLine, struct callback_data *p){
bail_on_error = booleanValue(azArg[1]); bail_on_error = booleanValue(azArg[1]);
}else }else
/* The undocumented ".breakpoint" command causes a call to the no-op
** routine named test_breakpoint().
*/
if( c=='b' && n>=3 && strncmp(azArg[0], "breakpoint", n)==0 ){
test_breakpoint();
}else
if( c=='d' && n>1 && strncmp(azArg[0], "databases", n)==0 && nArg==1 ){ if( c=='d' && n>1 && strncmp(azArg[0], "databases", n)==0 && nArg==1 ){
struct callback_data data; struct callback_data data;
char *zErrMsg = 0; char *zErrMsg = 0;
@ -1769,12 +1853,15 @@ static int do_meta_command(char *zLine, struct callback_data *p){
} }
sqlite3_exec(p->db, "BEGIN", 0, 0, 0); sqlite3_exec(p->db, "BEGIN", 0, 0, 0);
zCommit = "COMMIT"; zCommit = "COMMIT";
while( (zLine = local_getline(0, in))!=0 ){ while( (zLine = local_getline(0, in, 1))!=0 ){
char *z; char *z, c;
int inQuote = 0;
lineno++; lineno++;
azCol[0] = zLine; azCol[0] = zLine;
for(i=0, z=zLine; *z && *z!='\n' && *z!='\r'; z++){ for(i=0, z=zLine; (c = *z)!=0; z++){
if( *z==p->separator[0] && strncmp(z, p->separator, nSep)==0 ){ if( c=='"' ) inQuote = !inQuote;
if( c=='\n' ) lineno++;
if( !inQuote && c==p->separator[0] && strncmp(z,p->separator,nSep)==0 ){
*z = 0; *z = 0;
i++; i++;
if( i<nCol ){ if( i<nCol ){
@ -1794,6 +1881,14 @@ static int do_meta_command(char *zLine, struct callback_data *p){
break; /* from while */ break; /* from while */
} }
for(i=0; i<nCol; i++){ for(i=0; i<nCol; i++){
if( azCol[i][0]=='"' ){
int k;
for(z=azCol[i], j=1, k=0; z[j]; j++){
if( z[j]=='"' ){ j++; if( z[j]==0 ) break; }
z[k++] = z[j];
}
z[k] = 0;
}
sqlite3_bind_text(pStmt, i+1, azCol[i], -1, SQLITE_STATIC); sqlite3_bind_text(pStmt, i+1, azCol[i], -1, SQLITE_STATIC);
} }
sqlite3_step(pStmt); sqlite3_step(pStmt);
@ -1893,22 +1988,8 @@ static int do_meta_command(char *zLine, struct callback_data *p){
if( c=='l' && strncmp(azArg[0], "log", n)==0 && nArg>=2 ){ if( c=='l' && strncmp(azArg[0], "log", n)==0 && nArg>=2 ){
const char *zFile = azArg[1]; const char *zFile = azArg[1];
if( p->pLog && p->pLog!=stdout && p->pLog!=stderr ){ output_file_close(p->pLog);
fclose(p->pLog); p->pLog = output_file_open(zFile);
p->pLog = 0;
}
if( strcmp(zFile,"stdout")==0 ){
p->pLog = stdout;
}else if( strcmp(zFile, "stderr")==0 ){
p->pLog = stderr;
}else if( strcmp(zFile, "off")==0 ){
p->pLog = 0;
}else{
p->pLog = fopen(zFile, "w");
if( p->pLog==0 ){
fprintf(stderr, "Error: cannot open \"%s\"\n", zFile);
}
}
}else }else
if( c=='m' && strncmp(azArg[0], "mode", n)==0 && nArg==2 ){ if( c=='m' && strncmp(azArg[0], "mode", n)==0 && nArg==2 ){
@ -1961,20 +2042,31 @@ static int do_meta_command(char *zLine, struct callback_data *p){
}else }else
if( c=='o' && strncmp(azArg[0], "output", n)==0 && nArg==2 ){ if( c=='o' && strncmp(azArg[0], "output", n)==0 && nArg==2 ){
if( p->out!=stdout ){ if( p->outfile[0]=='|' ){
fclose(p->out); pclose(p->out);
}
if( strcmp(azArg[1],"stdout")==0 ){
p->out = stdout;
sqlite3_snprintf(sizeof(p->outfile), p->outfile, "stdout");
}else{ }else{
p->out = fopen(azArg[1], "wb"); output_file_close(p->out);
}
p->outfile[0] = 0;
if( azArg[1][0]=='|' ){
p->out = popen(&azArg[1][1], "w");
if( p->out==0 ){ if( p->out==0 ){
fprintf(stderr,"Error: cannot write to \"%s\"\n", azArg[1]); fprintf(stderr,"Error: cannot open pipe \"%s\"\n", &azArg[1][1]);
p->out = stdout;
rc = 1;
}else{
sqlite3_snprintf(sizeof(p->outfile), p->outfile, "%s", azArg[1]);
}
}else{
p->out = output_file_open(azArg[1]);
if( p->out==0 ){
if( strcmp(azArg[1],"off")!=0 ){
fprintf(stderr,"Error: cannot write to \"%s\"\n", azArg[1]);
}
p->out = stdout; p->out = stdout;
rc = 1; rc = 1;
} else { } else {
sqlite3_snprintf(sizeof(p->outfile), p->outfile, "%s", azArg[1]); sqlite3_snprintf(sizeof(p->outfile), p->outfile, "%s", azArg[1]);
} }
} }
}else }else
@ -2156,46 +2248,71 @@ static int do_meta_command(char *zLine, struct callback_data *p){
}else }else
if( c=='t' && n>1 && strncmp(azArg[0], "tables", n)==0 && nArg<3 ){ if( c=='t' && n>1 && strncmp(azArg[0], "tables", n)==0 && nArg<3 ){
sqlite3_stmt *pStmt;
char **azResult; char **azResult;
int nRow; int nRow, nAlloc;
char *zErrMsg; char *zSql = 0;
int ii;
open_db(p); open_db(p);
if( nArg==1 ){ rc = sqlite3_prepare_v2(p->db, "PRAGMA database_list", -1, &pStmt, 0);
rc = sqlite3_get_table(p->db, if( rc ) return rc;
"SELECT name FROM sqlite_master " zSql = sqlite3_mprintf(
"WHERE type IN ('table','view') AND name NOT LIKE 'sqlite_%' " "SELECT name FROM sqlite_master"
"UNION ALL " " WHERE type IN ('table','view')"
"SELECT name FROM sqlite_temp_master " " AND name NOT LIKE 'sqlite_%%'"
"WHERE type IN ('table','view') " " AND name LIKE ?1");
"ORDER BY 1", while( sqlite3_step(pStmt)==SQLITE_ROW ){
&azResult, &nRow, 0, &zErrMsg const char *zDbName = (const char*)sqlite3_column_text(pStmt, 1);
); if( zDbName==0 || strcmp(zDbName,"main")==0 ) continue;
}else{ if( strcmp(zDbName,"temp")==0 ){
zShellStatic = azArg[1]; zSql = sqlite3_mprintf(
rc = sqlite3_get_table(p->db, "%z UNION ALL "
"SELECT name FROM sqlite_master " "SELECT 'temp.' || name FROM sqlite_temp_master"
"WHERE type IN ('table','view') AND name LIKE shellstatic() " " WHERE type IN ('table','view')"
"UNION ALL " " AND name NOT LIKE 'sqlite_%%'"
"SELECT name FROM sqlite_temp_master " " AND name LIKE ?1", zSql);
"WHERE type IN ('table','view') AND name LIKE shellstatic() " }else{
"ORDER BY 1", zSql = sqlite3_mprintf(
&azResult, &nRow, 0, &zErrMsg "%z UNION ALL "
); "SELECT '%q.' || name FROM \"%w\".sqlite_master"
zShellStatic = 0; " WHERE type IN ('table','view')"
" AND name NOT LIKE 'sqlite_%%'"
" AND name LIKE ?1", zSql, zDbName, zDbName);
}
} }
if( zErrMsg ){ sqlite3_finalize(pStmt);
fprintf(stderr,"Error: %s\n", zErrMsg); zSql = sqlite3_mprintf("%z ORDER BY 1", zSql);
sqlite3_free(zErrMsg); rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0);
rc = 1; sqlite3_free(zSql);
}else if( rc != SQLITE_OK ){ if( rc ) return rc;
fprintf(stderr,"Error: querying sqlite_master and sqlite_temp_master\n"); nRow = nAlloc = 0;
rc = 1; azResult = 0;
if( nArg>1 ){
sqlite3_bind_text(pStmt, 1, azArg[1], -1, SQLITE_TRANSIENT);
}else{ }else{
sqlite3_bind_text(pStmt, 1, "%", -1, SQLITE_STATIC);
}
while( sqlite3_step(pStmt)==SQLITE_ROW ){
if( nRow>=nAlloc ){
char **azNew;
int n = nAlloc*2 + 10;
azNew = sqlite3_realloc(azResult, sizeof(azResult[0])*n);
if( azNew==0 ){
fprintf(stderr, "Error: out of memory\n");
break;
}
nAlloc = n;
azResult = azNew;
}
azResult[nRow] = sqlite3_mprintf("%s", sqlite3_column_text(pStmt, 0));
if( azResult[nRow] ) nRow++;
}
sqlite3_finalize(pStmt);
if( nRow>0 ){
int len, maxlen = 0; int len, maxlen = 0;
int i, j; int i, j;
int nPrintCol, nPrintRow; int nPrintCol, nPrintRow;
for(i=1; i<=nRow; i++){ for(i=0; i<nRow; i++){
if( azResult[i]==0 ) continue;
len = strlen30(azResult[i]); len = strlen30(azResult[i]);
if( len>maxlen ) maxlen = len; if( len>maxlen ) maxlen = len;
} }
@ -2203,14 +2320,15 @@ static int do_meta_command(char *zLine, struct callback_data *p){
if( nPrintCol<1 ) nPrintCol = 1; if( nPrintCol<1 ) nPrintCol = 1;
nPrintRow = (nRow + nPrintCol - 1)/nPrintCol; nPrintRow = (nRow + nPrintCol - 1)/nPrintCol;
for(i=0; i<nPrintRow; i++){ for(i=0; i<nPrintRow; i++){
for(j=i+1; j<=nRow; j+=nPrintRow){ for(j=i; j<nRow; j+=nPrintRow){
char *zSp = j<=nPrintRow ? "" : " "; char *zSp = j<nPrintRow ? "" : " ";
printf("%s%-*s", zSp, maxlen, azResult[j] ? azResult[j] : ""); printf("%s%-*s", zSp, maxlen, azResult[j] ? azResult[j] : "");
} }
printf("\n"); printf("\n");
} }
} }
sqlite3_free_table(azResult); for(ii=0; ii<nRow; ii++) sqlite3_free(azResult[ii]);
sqlite3_free(azResult);
}else }else
if( c=='t' && n>=8 && strncmp(azArg[0], "testctrl", n)==0 && nArg>=2 ){ if( c=='t' && n>=8 && strncmp(azArg[0], "testctrl", n)==0 && nArg>=2 ){
@ -2344,6 +2462,19 @@ static int do_meta_command(char *zLine, struct callback_data *p){
enableTimer = booleanValue(azArg[1]); enableTimer = booleanValue(azArg[1]);
}else }else
if( c=='t' && strncmp(azArg[0], "trace", n)==0 && nArg>1 ){
open_db(p);
output_file_close(p->traceOut);
p->traceOut = output_file_open(azArg[1]);
#ifndef SQLITE_OMIT_TRACE
if( p->traceOut==0 ){
sqlite3_trace(p->db, 0, 0);
}else{
sqlite3_trace(p->db, sql_trace_callback, p->traceOut);
}
#endif
}else
if( c=='v' && strncmp(azArg[0], "version", n)==0 ){ if( c=='v' && strncmp(azArg[0], "version", n)==0 ){
printf("SQLite %s %s\n" /*extra-version-info*/, printf("SQLite %s %s\n" /*extra-version-info*/,
sqlite3_libversion(), sqlite3_sourceid()); sqlite3_libversion(), sqlite3_sourceid());
@ -2468,7 +2599,9 @@ static int process_input(struct callback_data *p, FILE *in){
free(zLine); free(zLine);
zLine = one_input_line(zSql, in); zLine = one_input_line(zSql, in);
if( zLine==0 ){ if( zLine==0 ){
break; /* We have reached EOF */ /* End of input */
if( stdin_is_interactive ) printf("\n");
break;
} }
if( seenInterrupt ){ if( seenInterrupt ){
if( in!=0 ) break; if( in!=0 ) break;
@ -2555,12 +2688,11 @@ static int process_input(struct callback_data *p, FILE *in){
/* /*
** Return a pathname which is the user's home directory. A ** Return a pathname which is the user's home directory. A
** 0 return indicates an error of some kind. Space to hold the ** 0 return indicates an error of some kind.
** resulting string is obtained from malloc(). The calling
** function should free the result.
*/ */
static char *find_home_dir(void){ static char *find_home_dir(void){
char *home_dir = NULL; static char *home_dir = NULL;
if( home_dir ) return home_dir;
#if !defined(_WIN32) && !defined(WIN32) && !defined(__OS2__) && !defined(_WIN32_WCE) && !defined(__RTP__) && !defined(_WRS_KERNEL) #if !defined(_WIN32) && !defined(WIN32) && !defined(__OS2__) && !defined(_WIN32_WCE) && !defined(__RTP__) && !defined(_WRS_KERNEL)
struct passwd *pwent; struct passwd *pwent;
@ -2573,7 +2705,7 @@ static char *find_home_dir(void){
#if defined(_WIN32_WCE) #if defined(_WIN32_WCE)
/* Windows CE (arm-wince-mingw32ce-gcc) does not provide getenv() /* Windows CE (arm-wince-mingw32ce-gcc) does not provide getenv()
*/ */
home_dir = strdup("/"); home_dir = "/";
#else #else
#if defined(_WIN32) || defined(WIN32) || defined(__OS2__) #if defined(_WIN32) || defined(WIN32) || defined(__OS2__)
@ -2629,7 +2761,6 @@ static int process_sqliterc(
const char *sqliterc = sqliterc_override; const char *sqliterc = sqliterc_override;
char *zBuf = 0; char *zBuf = 0;
FILE *in = NULL; FILE *in = NULL;
int nBuf;
int rc = 0; int rc = 0;
if (sqliterc == NULL) { if (sqliterc == NULL) {
@ -2640,15 +2771,8 @@ static int process_sqliterc(
#endif #endif
return 1; return 1;
} }
nBuf = strlen30(home_dir) + 16; zBuf = sqlite3_mprintf("%s/.sqliterc",home_dir);
zBuf = malloc( nBuf ); sqliterc = zBuf;
if( zBuf==0 ){
fprintf(stderr,"%s: Error: out of memory\n",Argv0);
return 1;
}
sqlite3_snprintf(nBuf, zBuf,"%s/.sqliterc",home_dir);
free(home_dir);
sqliterc = (const char*)zBuf;
} }
in = fopen(sqliterc,"rb"); in = fopen(sqliterc,"rb");
if( in ){ if( in ){
@ -2658,7 +2782,7 @@ static int process_sqliterc(
rc = process_input(p,in); rc = process_input(p,in);
fclose(in); fclose(in);
} }
free(zBuf); sqlite3_free(zBuf);
return rc; return rc;
} }
@ -2666,29 +2790,30 @@ static int process_sqliterc(
** Show available command line options ** Show available command line options
*/ */
static const char zOptions[] = static const char zOptions[] =
" -help show this message\n"
" -init filename read/process named file\n"
" -echo print commands before execution\n"
" -[no]header turn headers on or off\n"
" -bail stop after hitting an error\n" " -bail stop after hitting an error\n"
" -interactive force interactive I/O\n"
" -batch force batch I/O\n" " -batch force batch I/O\n"
" -column set output mode to 'column'\n" " -column set output mode to 'column'\n"
" -cmd command run \"command\" before reading stdin\n"
" -csv set output mode to 'csv'\n" " -csv set output mode to 'csv'\n"
" -echo print commands before execution\n"
" -init filename read/process named file\n"
" -[no]header turn headers on or off\n"
" -help show this message\n"
" -html set output mode to HTML\n" " -html set output mode to HTML\n"
" -interactive force interactive I/O\n"
" -line set output mode to 'line'\n" " -line set output mode to 'line'\n"
" -list set output mode to 'list'\n" " -list set output mode to 'list'\n"
#ifdef SQLITE_ENABLE_MULTIPLEX
" -multiplex enable the multiplexor VFS\n"
#endif
" -nullvalue 'text' set text string for NULL values\n"
" -separator 'x' set output field separator (|)\n" " -separator 'x' set output field separator (|)\n"
" -stats print memory stats before each finalize\n" " -stats print memory stats before each finalize\n"
" -nullvalue 'text' set text string for NULL values\n"
" -version show SQLite version\n" " -version show SQLite version\n"
" -vfs NAME use NAME as the default VFS\n" " -vfs NAME use NAME as the default VFS\n"
#ifdef SQLITE_ENABLE_VFSTRACE #ifdef SQLITE_ENABLE_VFSTRACE
" -vfstrace enable tracing of all VFS calls\n" " -vfstrace enable tracing of all VFS calls\n"
#endif #endif
#ifdef SQLITE_ENABLE_MULTIPLEX
" -multiplex enable the multiplexor VFS\n"
#endif
; ;
static void usage(int showDetail){ static void usage(int showDetail){
fprintf(stderr, fprintf(stderr,
@ -2751,19 +2876,22 @@ int main(int argc, char **argv){
char *z; char *z;
if( argv[i][0]!='-' ) break; if( argv[i][0]!='-' ) break;
z = argv[i]; z = argv[i];
if( z[0]=='-' && z[1]=='-' ) z++; if( z[1]=='-' ) z++;
if( strcmp(argv[i],"-separator")==0 || strcmp(argv[i],"-nullvalue")==0 ){ if( strcmp(z,"-separator")==0
|| strcmp(z,"-nullvalue")==0
|| strcmp(z,"-cmd")==0
){
i++; i++;
}else if( strcmp(argv[i],"-init")==0 ){ }else if( strcmp(z,"-init")==0 ){
i++; i++;
zInitFile = argv[i]; zInitFile = argv[i];
/* Need to check for batch mode here to so we can avoid printing /* Need to check for batch mode here to so we can avoid printing
** informational messages (like from process_sqliterc) before ** informational messages (like from process_sqliterc) before
** we do the actual processing of arguments later in a second pass. ** we do the actual processing of arguments later in a second pass.
*/ */
}else if( strcmp(argv[i],"-batch")==0 ){ }else if( strcmp(z,"-batch")==0 ){
stdin_is_interactive = 0; stdin_is_interactive = 0;
}else if( strcmp(argv[i],"-heap")==0 ){ }else if( strcmp(z,"-heap")==0 ){
#if defined(SQLITE_ENABLE_MEMSYS3) || defined(SQLITE_ENABLE_MEMSYS5) #if defined(SQLITE_ENABLE_MEMSYS3) || defined(SQLITE_ENABLE_MEMSYS5)
int j, c; int j, c;
const char *zSize; const char *zSize;
@ -2780,7 +2908,7 @@ int main(int argc, char **argv){
sqlite3_config(SQLITE_CONFIG_HEAP, malloc((int)szHeap), (int)szHeap, 64); sqlite3_config(SQLITE_CONFIG_HEAP, malloc((int)szHeap), (int)szHeap, 64);
#endif #endif
#ifdef SQLITE_ENABLE_VFSTRACE #ifdef SQLITE_ENABLE_VFSTRACE
}else if( strcmp(argv[i],"-vfstrace")==0 ){ }else if( strcmp(z,"-vfstrace")==0 ){
extern int vfstrace_register( extern int vfstrace_register(
const char *zTraceName, const char *zTraceName,
const char *zOldVfsName, const char *zOldVfsName,
@ -2791,11 +2919,11 @@ int main(int argc, char **argv){
vfstrace_register("trace",0,(int(*)(const char*,void*))fputs,stderr,1); vfstrace_register("trace",0,(int(*)(const char*,void*))fputs,stderr,1);
#endif #endif
#ifdef SQLITE_ENABLE_MULTIPLEX #ifdef SQLITE_ENABLE_MULTIPLEX
}else if( strcmp(argv[i],"-multiplex")==0 ){ }else if( strcmp(z,"-multiplex")==0 ){
extern int sqlite3_multiple_initialize(const char*,int); extern int sqlite3_multiple_initialize(const char*,int);
sqlite3_multiplex_initialize(0, 1); sqlite3_multiplex_initialize(0, 1);
#endif #endif
}else if( strcmp(argv[i],"-vfs")==0 ){ }else if( strcmp(z,"-vfs")==0 ){
sqlite3_vfs *pVfs = sqlite3_vfs_find(argv[++i]); sqlite3_vfs *pVfs = sqlite3_vfs_find(argv[++i]);
if( pVfs ){ if( pVfs ){
sqlite3_vfs_register(pVfs, 1); sqlite3_vfs_register(pVfs, 1);
@ -2877,7 +3005,8 @@ int main(int argc, char **argv){
}else if( strcmp(z,"-separator")==0 ){ }else if( strcmp(z,"-separator")==0 ){
i++; i++;
if(i>=argc){ if(i>=argc){
fprintf(stderr,"%s: Error: missing argument for option: %s\n", Argv0, z); fprintf(stderr,"%s: Error: missing argument for option: %s\n",
Argv0, z);
fprintf(stderr,"Use -help for a list of options.\n"); fprintf(stderr,"Use -help for a list of options.\n");
return 1; return 1;
} }
@ -2886,7 +3015,8 @@ int main(int argc, char **argv){
}else if( strcmp(z,"-nullvalue")==0 ){ }else if( strcmp(z,"-nullvalue")==0 ){
i++; i++;
if(i>=argc){ if(i>=argc){
fprintf(stderr,"%s: Error: missing argument for option: %s\n", Argv0, z); fprintf(stderr,"%s: Error: missing argument for option: %s\n",
Argv0, z);
fprintf(stderr,"Use -help for a list of options.\n"); fprintf(stderr,"Use -help for a list of options.\n");
return 1; return 1;
} }
@ -2921,8 +3051,26 @@ int main(int argc, char **argv){
}else if( strcmp(z,"-multiplex")==0 ){ }else if( strcmp(z,"-multiplex")==0 ){
i++; i++;
#endif #endif
}else if( strcmp(z,"-help")==0 || strcmp(z, "--help")==0 ){ }else if( strcmp(z,"-help")==0 ){
usage(1); usage(1);
}else if( strcmp(z,"-cmd")==0 ){
if( i==argc-1 ) break;
i++;
z = argv[i];
if( z[0]=='.' ){
rc = do_meta_command(z, &data);
if( rc && bail_on_error ) return rc;
}else{
open_db(&data);
rc = shell_exec(data.db, z, shell_callback, &data, &zErrMsg);
if( zErrMsg!=0 ){
fprintf(stderr,"Error: %s\n", zErrMsg);
if( bail_on_error ) return rc!=0 ? rc : 1;
}else if( rc!=0 ){
fprintf(stderr,"Error: unable to process SQL \"%s\"\n", z);
if( bail_on_error ) return rc;
}
}
}else{ }else{
fprintf(stderr,"%s: Error: unknown option: %s\n", Argv0, z); fprintf(stderr,"%s: Error: unknown option: %s\n", Argv0, z);
fprintf(stderr,"Use -help for a list of options.\n"); fprintf(stderr,"Use -help for a list of options.\n");
@ -2975,7 +3123,6 @@ int main(int argc, char **argv){
write_history(zHistory); write_history(zHistory);
free(zHistory); free(zHistory);
} }
free(zHome);
}else{ }else{
rc = process_input(&data, stdin); rc = process_input(&data, stdin);
} }

View File

@ -453,9 +453,11 @@ int sqlite3_exec(
#define SQLITE_LOCKED_SHAREDCACHE (SQLITE_LOCKED | (1<<8)) #define SQLITE_LOCKED_SHAREDCACHE (SQLITE_LOCKED | (1<<8))
#define SQLITE_BUSY_RECOVERY (SQLITE_BUSY | (1<<8)) #define SQLITE_BUSY_RECOVERY (SQLITE_BUSY | (1<<8))
#define SQLITE_CANTOPEN_NOTEMPDIR (SQLITE_CANTOPEN | (1<<8)) #define SQLITE_CANTOPEN_NOTEMPDIR (SQLITE_CANTOPEN | (1<<8))
#define SQLITE_CANTOPEN_ISDIR (SQLITE_CANTOPEN | (2<<8))
#define SQLITE_CORRUPT_VTAB (SQLITE_CORRUPT | (1<<8)) #define SQLITE_CORRUPT_VTAB (SQLITE_CORRUPT | (1<<8))
#define SQLITE_READONLY_RECOVERY (SQLITE_READONLY | (1<<8)) #define SQLITE_READONLY_RECOVERY (SQLITE_READONLY | (1<<8))
#define SQLITE_READONLY_CANTLOCK (SQLITE_READONLY | (2<<8)) #define SQLITE_READONLY_CANTLOCK (SQLITE_READONLY | (2<<8))
#define SQLITE_ABORT_ROLLBACK (SQLITE_ABORT | (2<<8))
/* /*
** CAPI3REF: Flags For File Open Operations ** CAPI3REF: Flags For File Open Operations
@ -711,7 +713,8 @@ struct sqlite3_io_methods {
** into an integer that the pArg argument points to. This capability ** into an integer that the pArg argument points to. This capability
** is used during testing and only needs to be supported when SQLITE_TEST ** is used during testing and only needs to be supported when SQLITE_TEST
** is defined. ** is defined.
** ** <ul>
** <li>[[SQLITE_FCNTL_SIZE_HINT]]
** The [SQLITE_FCNTL_SIZE_HINT] opcode is used by SQLite to give the VFS ** The [SQLITE_FCNTL_SIZE_HINT] opcode is used by SQLite to give the VFS
** layer a hint of how large the database file will grow to be during the ** layer a hint of how large the database file will grow to be during the
** current transaction. This hint is not guaranteed to be accurate but it ** current transaction. This hint is not guaranteed to be accurate but it
@ -719,6 +722,7 @@ struct sqlite3_io_methods {
** file space based on this hint in order to help writes to the database ** file space based on this hint in order to help writes to the database
** file run faster. ** file run faster.
** **
** <li>[[SQLITE_FCNTL_CHUNK_SIZE]]
** The [SQLITE_FCNTL_CHUNK_SIZE] opcode is used to request that the VFS ** The [SQLITE_FCNTL_CHUNK_SIZE] opcode is used to request that the VFS
** extends and truncates the database file in chunks of a size specified ** extends and truncates the database file in chunks of a size specified
** by the user. The fourth argument to [sqlite3_file_control()] should ** by the user. The fourth argument to [sqlite3_file_control()] should
@ -727,11 +731,13 @@ struct sqlite3_io_methods {
** chunks (say 1MB at a time), may reduce file-system fragmentation and ** chunks (say 1MB at a time), may reduce file-system fragmentation and
** improve performance on some systems. ** improve performance on some systems.
** **
** <li>[[SQLITE_FCNTL_FILE_POINTER]]
** The [SQLITE_FCNTL_FILE_POINTER] opcode is used to obtain a pointer ** The [SQLITE_FCNTL_FILE_POINTER] opcode is used to obtain a pointer
** to the [sqlite3_file] object associated with a particular database ** to the [sqlite3_file] object associated with a particular database
** connection. See the [sqlite3_file_control()] documentation for ** connection. See the [sqlite3_file_control()] documentation for
** additional information. ** additional information.
** **
** <li>[[SQLITE_FCNTL_SYNC_OMITTED]]
** ^(The [SQLITE_FCNTL_SYNC_OMITTED] opcode is generated internally by ** ^(The [SQLITE_FCNTL_SYNC_OMITTED] opcode is generated internally by
** SQLite and sent to all VFSes in place of a call to the xSync method ** SQLite and sent to all VFSes in place of a call to the xSync method
** when the database connection has [PRAGMA synchronous] set to OFF.)^ ** when the database connection has [PRAGMA synchronous] set to OFF.)^
@ -742,6 +748,7 @@ struct sqlite3_io_methods {
** opcode as doing so may disrupt the operation of the specialized VFSes ** opcode as doing so may disrupt the operation of the specialized VFSes
** that do require it. ** that do require it.
** **
** <li>[[SQLITE_FCNTL_WIN32_AV_RETRY]]
** ^The [SQLITE_FCNTL_WIN32_AV_RETRY] opcode is used to configure automatic ** ^The [SQLITE_FCNTL_WIN32_AV_RETRY] opcode is used to configure automatic
** retry counts and intervals for certain disk I/O operations for the ** retry counts and intervals for certain disk I/O operations for the
** windows [VFS] in order to provide robustness in the presence of ** windows [VFS] in order to provide robustness in the presence of
@ -758,8 +765,9 @@ struct sqlite3_io_methods {
** into the array entry, allowing the current retry settings to be ** into the array entry, allowing the current retry settings to be
** interrogated. The zDbName parameter is ignored. ** interrogated. The zDbName parameter is ignored.
** **
** <li>[[SQLITE_FCNTL_PERSIST_WAL]]
** ^The [SQLITE_FCNTL_PERSIST_WAL] opcode is used to set or query the ** ^The [SQLITE_FCNTL_PERSIST_WAL] opcode is used to set or query the
** persistent [WAL | Write AHead Log] setting. By default, the auxiliary ** persistent [WAL | Write Ahead Log] setting. By default, the auxiliary
** write ahead log and shared memory files used for transaction control ** write ahead log and shared memory files used for transaction control
** are automatically deleted when the latest connection to the database ** are automatically deleted when the latest connection to the database
** closes. Setting persistent WAL mode causes those files to persist after ** closes. Setting persistent WAL mode causes those files to persist after
@ -772,6 +780,7 @@ struct sqlite3_io_methods {
** WAL mode. If the integer is -1, then it is overwritten with the current ** WAL mode. If the integer is -1, then it is overwritten with the current
** WAL persistence setting. ** WAL persistence setting.
** **
** <li>[[SQLITE_FCNTL_POWERSAFE_OVERWRITE]]
** ^The [SQLITE_FCNTL_POWERSAFE_OVERWRITE] opcode is used to set or query the ** ^The [SQLITE_FCNTL_POWERSAFE_OVERWRITE] opcode is used to set or query the
** persistent "powersafe-overwrite" or "PSOW" setting. The PSOW setting ** persistent "powersafe-overwrite" or "PSOW" setting. The PSOW setting
** determines the [SQLITE_IOCAP_POWERSAFE_OVERWRITE] bit of the ** determines the [SQLITE_IOCAP_POWERSAFE_OVERWRITE] bit of the
@ -781,11 +790,13 @@ struct sqlite3_io_methods {
** mode. If the integer is -1, then it is overwritten with the current ** mode. If the integer is -1, then it is overwritten with the current
** zero-damage mode setting. ** zero-damage mode setting.
** **
** <li>[[SQLITE_FCNTL_OVERWRITE]]
** ^The [SQLITE_FCNTL_OVERWRITE] opcode is invoked by SQLite after opening ** ^The [SQLITE_FCNTL_OVERWRITE] opcode is invoked by SQLite after opening
** a write transaction to indicate that, unless it is rolled back for some ** a write transaction to indicate that, unless it is rolled back for some
** reason, the entire database file will be overwritten by the current ** reason, the entire database file will be overwritten by the current
** transaction. This is used by VACUUM operations. ** transaction. This is used by VACUUM operations.
** **
** <li>[[SQLITE_FCNTL_VFSNAME]]
** ^The [SQLITE_FCNTL_VFSNAME] opcode can be used to obtain the names of ** ^The [SQLITE_FCNTL_VFSNAME] opcode can be used to obtain the names of
** all [VFSes] in the VFS stack. The names are of all VFS shims and the ** all [VFSes] in the VFS stack. The names are of all VFS shims and the
** final bottom-level VFS are written into memory obtained from ** final bottom-level VFS are written into memory obtained from
@ -796,6 +807,30 @@ struct sqlite3_io_methods {
** do anything. Callers should initialize the char* variable to a NULL ** do anything. Callers should initialize the char* variable to a NULL
** pointer in case this file-control is not implemented. This file-control ** pointer in case this file-control is not implemented. This file-control
** is intended for diagnostic use only. ** is intended for diagnostic use only.
**
** <li>[[SQLITE_FCNTL_PRAGMA]]
** ^Whenever a [PRAGMA] statement is parsed, an [SQLITE_FCNTL_PRAGMA]
** file control is sent to the open [sqlite3_file] object corresponding
** to the database file to which the pragma statement refers. ^The argument
** to the [SQLITE_FCNTL_PRAGMA] file control is an array of
** pointers to strings (char**) in which the second element of the array
** is the name of the pragma and the third element is the argument to the
** pragma or NULL if the pragma has no argument. ^The handler for an
** [SQLITE_FCNTL_PRAGMA] file control can optionally make the first element
** of the char** argument point to a string obtained from [sqlite3_mprintf()]
** or the equivalent and that string will become the result of the pragma or
** the error message if the pragma fails. ^If the
** [SQLITE_FCNTL_PRAGMA] file control returns [SQLITE_NOTFOUND], then normal
** [PRAGMA] processing continues. ^If the [SQLITE_FCNTL_PRAGMA]
** file control returns [SQLITE_OK], then the parser assumes that the
** VFS has handled the PRAGMA itself and the parser generates a no-op
** prepared statement. ^If the [SQLITE_FCNTL_PRAGMA] file control returns
** any result code other than [SQLITE_OK] or [SQLITE_NOTFOUND], that means
** that the VFS encountered an error while handling the [PRAGMA] and the
** compilation of the PRAGMA fails with an error. ^The [SQLITE_FCNTL_PRAGMA]
** file control occurs at the beginning of pragma statement analysis and so
** it is able to override built-in [PRAGMA] statements.
** </ul>
*/ */
#define SQLITE_FCNTL_LOCKSTATE 1 #define SQLITE_FCNTL_LOCKSTATE 1
#define SQLITE_GET_LOCKPROXYFILE 2 #define SQLITE_GET_LOCKPROXYFILE 2
@ -810,6 +845,7 @@ struct sqlite3_io_methods {
#define SQLITE_FCNTL_OVERWRITE 11 #define SQLITE_FCNTL_OVERWRITE 11
#define SQLITE_FCNTL_VFSNAME 12 #define SQLITE_FCNTL_VFSNAME 12
#define SQLITE_FCNTL_POWERSAFE_OVERWRITE 13 #define SQLITE_FCNTL_POWERSAFE_OVERWRITE 13
#define SQLITE_FCNTL_PRAGMA 14
/* /*
** CAPI3REF: Mutex Handle ** CAPI3REF: Mutex Handle
@ -1507,7 +1543,7 @@ struct sqlite3_mem_methods {
** [SQLITE_USE_URI] symbol defined. ** [SQLITE_USE_URI] symbol defined.
** **
** [[SQLITE_CONFIG_PCACHE]] [[SQLITE_CONFIG_GETPCACHE]] ** [[SQLITE_CONFIG_PCACHE]] [[SQLITE_CONFIG_GETPCACHE]]
** <dt>SQLITE_CONFIG_PCACHE and SQLITE_CONFNIG_GETPCACHE ** <dt>SQLITE_CONFIG_PCACHE and SQLITE_CONFIG_GETPCACHE
** <dd> These options are obsolete and should not be used by new code. ** <dd> These options are obsolete and should not be used by new code.
** They are retained for backwards compatibility but are now no-ops. ** They are retained for backwards compatibility but are now no-ops.
** </dl> ** </dl>
@ -2639,9 +2675,14 @@ int sqlite3_open_v2(
** **
** The sqlite3_uri_boolean(F,P,B) routine assumes that P is a boolean ** The sqlite3_uri_boolean(F,P,B) routine assumes that P is a boolean
** parameter and returns true (1) or false (0) according to the value ** parameter and returns true (1) or false (0) according to the value
** of P. The value of P is true if it is "yes" or "true" or "on" or ** of P. The sqlite3_uri_boolean(F,P,B) routine returns true (1) if the
** a non-zero number and is false otherwise. If P is not a query parameter ** value of query parameter P is one of "yes", "true", or "on" in any
** on F then sqlite3_uri_boolean(F,P,B) returns (B!=0). ** case or if the value begins with a non-zero number. The
** sqlite3_uri_boolean(F,P,B) routines returns false (0) if the value of
** query parameter P is one of "no", "false", or "off" in any case or
** if the value begins with a numeric zero. If P is not a query
** parameter on F or if the value of P is does not match any of the
** above, then sqlite3_uri_boolean(F,P,B) returns (B!=0).
** **
** The sqlite3_uri_int64(F,P,D) routine converts the value of P into a ** The sqlite3_uri_int64(F,P,D) routine converts the value of P into a
** 64-bit signed integer and returns that integer, or D if P does not ** 64-bit signed integer and returns that integer, or D if P does not
@ -4454,6 +4495,15 @@ sqlite3 *sqlite3_db_handle(sqlite3_stmt*);
*/ */
const char *sqlite3_db_filename(sqlite3 *db, const char *zDbName); const char *sqlite3_db_filename(sqlite3 *db, const char *zDbName);
/*
** CAPI3REF: Determine if a database is read-only
**
** ^The sqlite3_db_readonly(D,N) interface returns 1 if the database N
** of connection D is read-only, 0 if it is read/write, or -1 if N is not
** the name of a database on connection D.
*/
int sqlite3_db_readonly(sqlite3 *db, const char *zDbName);
/* /*
** CAPI3REF: Find the next prepared statement ** CAPI3REF: Find the next prepared statement
** **
@ -5952,6 +6002,17 @@ int sqlite3_db_status(sqlite3*, int op, int *pCur, int *pHiwtr, int resetFlg);
** occurred.)^ ^The highwater mark associated with SQLITE_DBSTATUS_CACHE_MISS ** occurred.)^ ^The highwater mark associated with SQLITE_DBSTATUS_CACHE_MISS
** is always 0. ** is always 0.
** </dd> ** </dd>
**
** [[SQLITE_DBSTATUS_CACHE_WRITE]] ^(<dt>SQLITE_DBSTATUS_CACHE_WRITE</dt>
** <dd>This parameter returns the number of dirty cache entries that have
** been written to disk. Specifically, the number of pages written to the
** wal file in wal mode databases, or the number of pages written to the
** database file in rollback mode databases. Any pages written as part of
** transaction rollback or database recovery operations are not included.
** If an IO or other error occurs while writing a page to disk, the effect
** on subsequent SQLITE_DBSTATUS_CACHE_WRITE requests is undefined.)^ ^The
** highwater mark associated with SQLITE_DBSTATUS_CACHE_WRITE is always 0.
** </dd>
** </dl> ** </dl>
*/ */
#define SQLITE_DBSTATUS_LOOKASIDE_USED 0 #define SQLITE_DBSTATUS_LOOKASIDE_USED 0
@ -5963,7 +6024,8 @@ int sqlite3_db_status(sqlite3*, int op, int *pCur, int *pHiwtr, int resetFlg);
#define SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL 6 #define SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL 6
#define SQLITE_DBSTATUS_CACHE_HIT 7 #define SQLITE_DBSTATUS_CACHE_HIT 7
#define SQLITE_DBSTATUS_CACHE_MISS 8 #define SQLITE_DBSTATUS_CACHE_MISS 8
#define SQLITE_DBSTATUS_MAX 8 /* Largest defined DBSTATUS */ #define SQLITE_DBSTATUS_CACHE_WRITE 9
#define SQLITE_DBSTATUS_MAX 9 /* Largest defined DBSTATUS */
/* /*
@ -6579,11 +6641,12 @@ int sqlite3_unlock_notify(
/* /*
** CAPI3REF: String Comparison ** CAPI3REF: String Comparison
** **
** ^The [sqlite3_strnicmp()] API allows applications and extensions to ** ^The [sqlite3_stricmp()] and [sqlite3_strnicmp()] APIs allow applications
** compare the contents of two buffers containing UTF-8 strings in a ** and extensions to compare the contents of two buffers containing UTF-8
** case-independent fashion, using the same definition of case independence ** strings in a case-independent fashion, using the same definition of "case
** that SQLite uses internally when comparing identifiers. ** independence" that SQLite uses internally when comparing identifiers.
*/ */
int sqlite3_stricmp(const char *, const char *);
int sqlite3_strnicmp(const char *, const char *, int); int sqlite3_strnicmp(const char *, const char *, int);
/* /*

View File

@ -561,9 +561,13 @@ struct BusyHandler {
/* /*
** The following value as a destructor means to use sqlite3DbFree(). ** The following value as a destructor means to use sqlite3DbFree().
** This is an internal extension to SQLITE_STATIC and SQLITE_TRANSIENT. ** The sqlite3DbFree() routine requires two parameters instead of the
** one parameter that destructors normally want. So we have to introduce
** this magic value that the code knows to handle differently. Any
** pointer will work here as long as it is distinct from SQLITE_STATIC
** and SQLITE_TRANSIENT.
*/ */
#define SQLITE_DYNAMIC ((sqlite3_destructor_type)sqlite3DbFree) #define SQLITE_DYNAMIC ((sqlite3_destructor_type)sqlite3MallocSize)
/* /*
** When SQLITE_OMIT_WSD is defined, it means that the target platform does ** When SQLITE_OMIT_WSD is defined, it means that the target platform does
@ -793,35 +797,16 @@ struct FuncDefHash {
/* /*
** Each database connection is an instance of the following structure. ** Each database connection is an instance of the following structure.
**
** The sqlite.lastRowid records the last insert rowid generated by an
** insert statement. Inserts on views do not affect its value. Each
** trigger has its own context, so that lastRowid can be updated inside
** triggers as usual. The previous value will be restored once the trigger
** exits. Upon entering a before or instead of trigger, lastRowid is no
** longer (since after version 2.8.12) reset to -1.
**
** The sqlite.nChange does not count changes within triggers and keeps no
** context. It is reset at start of sqlite3_exec.
** The sqlite.lsChange represents the number of changes made by the last
** insert, update, or delete statement. It remains constant throughout the
** length of a statement and is then updated by OP_SetCounts. It keeps a
** context stack just like lastRowid so that the count of changes
** within a trigger is not seen outside the trigger. Changes to views do not
** affect the value of lsChange.
** The sqlite.csChange keeps track of the number of current changes (since
** the last statement) and is used to update sqlite_lsChange.
**
** The member variables sqlite.errCode, sqlite.zErrMsg and sqlite.zErrMsg16
** store the most recent error code and, if applicable, string. The
** internal function sqlite3Error() is used to set these variables
** consistently.
*/ */
struct sqlite3 { struct sqlite3 {
sqlite3_vfs *pVfs; /* OS Interface */ sqlite3_vfs *pVfs; /* OS Interface */
int nDb; /* Number of backends currently in use */ struct Vdbe *pVdbe; /* List of active virtual machines */
CollSeq *pDfltColl; /* The default collating sequence (BINARY) */
sqlite3_mutex *mutex; /* Connection mutex */
Db *aDb; /* All backends */ Db *aDb; /* All backends */
int nDb; /* Number of backends currently in use */
int flags; /* Miscellaneous flags. See below */ int flags; /* Miscellaneous flags. See below */
i64 lastRowid; /* ROWID of most recent insert (see above) */
unsigned int openFlags; /* Flags passed to sqlite3_vfs.xOpen() */ unsigned int openFlags; /* Flags passed to sqlite3_vfs.xOpen() */
int errCode; /* Most recent error code (SQLITE_*) */ int errCode; /* Most recent error code (SQLITE_*) */
int errMask; /* & result codes with this before returning */ int errMask; /* & result codes with this before returning */
@ -832,27 +817,23 @@ struct sqlite3 {
signed char nextAutovac; /* Autovac setting after VACUUM if >=0 */ signed char nextAutovac; /* Autovac setting after VACUUM if >=0 */
u8 suppressErr; /* Do not issue error messages if true */ u8 suppressErr; /* Do not issue error messages if true */
u8 vtabOnConflict; /* Value to return for s3_vtab_on_conflict() */ u8 vtabOnConflict; /* Value to return for s3_vtab_on_conflict() */
u8 isTransactionSavepoint; /* True if the outermost savepoint is a TS */
int nextPagesize; /* Pagesize after VACUUM if >0 */ int nextPagesize; /* Pagesize after VACUUM if >0 */
int nTable; /* Number of tables in the database */
CollSeq *pDfltColl; /* The default collating sequence (BINARY) */
i64 lastRowid; /* ROWID of most recent insert (see above) */
u32 magic; /* Magic number for detect library misuse */ u32 magic; /* Magic number for detect library misuse */
int nChange; /* Value returned by sqlite3_changes() */ int nChange; /* Value returned by sqlite3_changes() */
int nTotalChange; /* Value returned by sqlite3_total_changes() */ int nTotalChange; /* Value returned by sqlite3_total_changes() */
sqlite3_mutex *mutex; /* Connection mutex */
int aLimit[SQLITE_N_LIMIT]; /* Limits */ int aLimit[SQLITE_N_LIMIT]; /* Limits */
struct sqlite3InitInfo { /* Information used during initialization */ struct sqlite3InitInfo { /* Information used during initialization */
int iDb; /* When back is being initialized */
int newTnum; /* Rootpage of table being initialized */ int newTnum; /* Rootpage of table being initialized */
u8 iDb; /* Which db file is being initialized */
u8 busy; /* TRUE if currently initializing */ u8 busy; /* TRUE if currently initializing */
u8 orphanTrigger; /* Last statement is orphaned TEMP trigger */ u8 orphanTrigger; /* Last statement is orphaned TEMP trigger */
} init; } init;
int nExtension; /* Number of loaded extensions */
void **aExtension; /* Array of shared library handles */
struct Vdbe *pVdbe; /* List of active virtual machines */
int activeVdbeCnt; /* Number of VDBEs currently executing */ int activeVdbeCnt; /* Number of VDBEs currently executing */
int writeVdbeCnt; /* Number of active VDBEs that are writing */ int writeVdbeCnt; /* Number of active VDBEs that are writing */
int vdbeExecCnt; /* Number of nested calls to VdbeExec() */ int vdbeExecCnt; /* Number of nested calls to VdbeExec() */
int nExtension; /* Number of loaded extensions */
void **aExtension; /* Array of shared library handles */
void (*xTrace)(void*,const char*); /* Trace function */ void (*xTrace)(void*,const char*); /* Trace function */
void *pTraceArg; /* Argument to the trace function */ void *pTraceArg; /* Argument to the trace function */
void (*xProfile)(void*,const char*,u64); /* Profiling function */ void (*xProfile)(void*,const char*,u64); /* Profiling function */
@ -889,21 +870,20 @@ struct sqlite3 {
int nProgressOps; /* Number of opcodes for progress callback */ int nProgressOps; /* Number of opcodes for progress callback */
#endif #endif
#ifndef SQLITE_OMIT_VIRTUALTABLE #ifndef SQLITE_OMIT_VIRTUALTABLE
int nVTrans; /* Allocated size of aVTrans */
Hash aModule; /* populated by sqlite3_create_module() */ Hash aModule; /* populated by sqlite3_create_module() */
VtabCtx *pVtabCtx; /* Context for active vtab connect/create */ VtabCtx *pVtabCtx; /* Context for active vtab connect/create */
VTable **aVTrans; /* Virtual tables with open transactions */ VTable **aVTrans; /* Virtual tables with open transactions */
int nVTrans; /* Allocated size of aVTrans */
VTable *pDisconnect; /* Disconnect these in next sqlite3_prepare() */ VTable *pDisconnect; /* Disconnect these in next sqlite3_prepare() */
#endif #endif
FuncDefHash aFunc; /* Hash table of connection functions */ FuncDefHash aFunc; /* Hash table of connection functions */
Hash aCollSeq; /* All collating sequences */ Hash aCollSeq; /* All collating sequences */
BusyHandler busyHandler; /* Busy callback */ BusyHandler busyHandler; /* Busy callback */
int busyTimeout; /* Busy handler timeout, in msec */
Db aDbStatic[2]; /* Static space for the 2 default backends */ Db aDbStatic[2]; /* Static space for the 2 default backends */
Savepoint *pSavepoint; /* List of active savepoints */ Savepoint *pSavepoint; /* List of active savepoints */
int busyTimeout; /* Busy handler timeout, in msec */
int nSavepoint; /* Number of non-transaction savepoints */ int nSavepoint; /* Number of non-transaction savepoints */
int nStatement; /* Number of nested statement-transactions */ int nStatement; /* Number of nested statement-transactions */
u8 isTransactionSavepoint; /* True if the outermost savepoint is a TS */
i64 nDeferredCons; /* Net deferred constraints this transaction. */ i64 nDeferredCons; /* Net deferred constraints this transaction. */
int *pnBytesFreed; /* If not NULL, increment this in DbFree() */ int *pnBytesFreed; /* If not NULL, increment this in DbFree() */
@ -946,8 +926,7 @@ struct sqlite3 {
#define SQLITE_SqlTrace 0x00004000 /* Debug print SQL as it executes */ #define SQLITE_SqlTrace 0x00004000 /* Debug print SQL as it executes */
#define SQLITE_VdbeListing 0x00008000 /* Debug listings of VDBE programs */ #define SQLITE_VdbeListing 0x00008000 /* Debug listings of VDBE programs */
#define SQLITE_WriteSchema 0x00010000 /* OK to update SQLITE_MASTER */ #define SQLITE_WriteSchema 0x00010000 /* OK to update SQLITE_MASTER */
#define SQLITE_NoReadlock 0x00020000 /* Readlocks are omitted when /* 0x00020000 Unused */
** accessing read-only databases */
#define SQLITE_IgnoreChecks 0x00040000 /* Do not enforce check constraints */ #define SQLITE_IgnoreChecks 0x00040000 /* Do not enforce check constraints */
#define SQLITE_ReadUncommitted 0x0080000 /* For shared-cache mode */ #define SQLITE_ReadUncommitted 0x0080000 /* For shared-cache mode */
#define SQLITE_LegacyFileFmt 0x00100000 /* Create new databases in format 1 */ #define SQLITE_LegacyFileFmt 0x00100000 /* Create new databases in format 1 */
@ -1030,15 +1009,18 @@ struct FuncDestructor {
}; };
/* /*
** Possible values for FuncDef.flags ** Possible values for FuncDef.flags. Note that the _LENGTH and _TYPEOF
** values must correspond to OPFLAG_LENGTHARG and OPFLAG_TYPEOFARG. There
** are assert() statements in the code to verify this.
*/ */
#define SQLITE_FUNC_LIKE 0x01 /* Candidate for the LIKE optimization */ #define SQLITE_FUNC_LIKE 0x01 /* Candidate for the LIKE optimization */
#define SQLITE_FUNC_CASE 0x02 /* Case-sensitive LIKE-type function */ #define SQLITE_FUNC_CASE 0x02 /* Case-sensitive LIKE-type function */
#define SQLITE_FUNC_EPHEM 0x04 /* Ephemeral. Delete with VDBE */ #define SQLITE_FUNC_EPHEM 0x04 /* Ephemeral. Delete with VDBE */
#define SQLITE_FUNC_NEEDCOLL 0x08 /* sqlite3GetFuncCollSeq() might be called */ #define SQLITE_FUNC_NEEDCOLL 0x08 /* sqlite3GetFuncCollSeq() might be called */
#define SQLITE_FUNC_PRIVATE 0x10 /* Allowed for internal use only */ #define SQLITE_FUNC_COUNT 0x10 /* Built-in count(*) aggregate */
#define SQLITE_FUNC_COUNT 0x20 /* Built-in count(*) aggregate */ #define SQLITE_FUNC_COALESCE 0x20 /* Built-in coalesce() or ifnull() function */
#define SQLITE_FUNC_COALESCE 0x40 /* Built-in coalesce() or ifnull() function */ #define SQLITE_FUNC_LENGTH 0x40 /* Built-in length() function */
#define SQLITE_FUNC_TYPEOF 0x80 /* Built-in typeof() function */
/* /*
** The following three macros, FUNCTION(), LIKEFUNC() and AGGREGATE() are ** The following three macros, FUNCTION(), LIKEFUNC() and AGGREGATE() are
@ -1066,7 +1048,10 @@ struct FuncDestructor {
** parameter. ** parameter.
*/ */
#define FUNCTION(zName, nArg, iArg, bNC, xFunc) \ #define FUNCTION(zName, nArg, iArg, bNC, xFunc) \
{nArg, SQLITE_UTF8, bNC*SQLITE_FUNC_NEEDCOLL, \ {nArg, SQLITE_UTF8, (bNC*SQLITE_FUNC_NEEDCOLL), \
SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, 0, #zName, 0, 0}
#define FUNCTION2(zName, nArg, iArg, bNC, xFunc, extraFlags) \
{nArg, SQLITE_UTF8, (bNC*SQLITE_FUNC_NEEDCOLL)|extraFlags, \
SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, 0, #zName, 0, 0} SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, 0, #zName, 0, 0}
#define STR_FUNCTION(zName, nArg, pArg, bNC, xFunc) \ #define STR_FUNCTION(zName, nArg, pArg, bNC, xFunc) \
{nArg, SQLITE_UTF8, bNC*SQLITE_FUNC_NEEDCOLL, \ {nArg, SQLITE_UTF8, bNC*SQLITE_FUNC_NEEDCOLL, \
@ -1296,7 +1281,7 @@ struct Table {
FKey *pFKey; /* Linked list of all foreign keys in this table */ FKey *pFKey; /* Linked list of all foreign keys in this table */
char *zColAff; /* String defining the affinity of each column */ char *zColAff; /* String defining the affinity of each column */
#ifndef SQLITE_OMIT_CHECK #ifndef SQLITE_OMIT_CHECK
Expr *pCheck; /* The AND of all CHECK constraints */ ExprList *pCheck; /* All CHECK constraints */
#endif #endif
#ifndef SQLITE_OMIT_ALTERTABLE #ifndef SQLITE_OMIT_ALTERTABLE
int addColOffset; /* Offset in CREATE TABLE stmt to add a new column */ int addColOffset; /* Offset in CREATE TABLE stmt to add a new column */
@ -1319,8 +1304,6 @@ struct Table {
#define TF_HasPrimaryKey 0x04 /* Table has a primary key */ #define TF_HasPrimaryKey 0x04 /* Table has a primary key */
#define TF_Autoincrement 0x08 /* Integer primary key is autoincrement */ #define TF_Autoincrement 0x08 /* Integer primary key is autoincrement */
#define TF_Virtual 0x10 /* Is a virtual table */ #define TF_Virtual 0x10 /* Is a virtual table */
#define TF_NeedMetadata 0x20 /* aCol[].zType and aCol[].pColl missing */
/* /*
@ -1482,19 +1465,19 @@ struct UnpackedRecord {
*/ */
struct Index { struct Index {
char *zName; /* Name of this index */ char *zName; /* Name of this index */
int nColumn; /* Number of columns in the table used by this index */
int *aiColumn; /* Which columns are used by this index. 1st is 0 */ int *aiColumn; /* Which columns are used by this index. 1st is 0 */
tRowcnt *aiRowEst; /* Result of ANALYZE: Est. rows selected by each column */ tRowcnt *aiRowEst; /* Result of ANALYZE: Est. rows selected by each column */
Table *pTable; /* The SQL table being indexed */ Table *pTable; /* The SQL table being indexed */
int tnum; /* Page containing root of this index in database file */
u8 onError; /* OE_Abort, OE_Ignore, OE_Replace, or OE_None */
u8 autoIndex; /* True if is automatically created (ex: by UNIQUE) */
u8 bUnordered; /* Use this index for == or IN queries only */
char *zColAff; /* String defining the affinity of each column */ char *zColAff; /* String defining the affinity of each column */
Index *pNext; /* The next index associated with the same table */ Index *pNext; /* The next index associated with the same table */
Schema *pSchema; /* Schema containing this index */ Schema *pSchema; /* Schema containing this index */
u8 *aSortOrder; /* Array of size Index.nColumn. True==DESC, False==ASC */ u8 *aSortOrder; /* Array of size Index.nColumn. True==DESC, False==ASC */
char **azColl; /* Array of collation sequence names for index */ char **azColl; /* Array of collation sequence names for index */
int nColumn; /* Number of columns in the table used by this index */
int tnum; /* Page containing root of this index in database file */
u8 onError; /* OE_Abort, OE_Ignore, OE_Replace, or OE_None */
u8 autoIndex; /* True if is automatically created (ex: by UNIQUE) */
u8 bUnordered; /* Use this index for == or IN queries only */
#ifdef SQLITE_ENABLE_STAT3 #ifdef SQLITE_ENABLE_STAT3
int nSample; /* Number of elements in aSample[] */ int nSample; /* Number of elements in aSample[] */
tRowcnt avgEq; /* Average nEq value for key values not in aSample */ tRowcnt avgEq; /* Average nEq value for key values not in aSample */
@ -1553,8 +1536,8 @@ struct AggInfo {
** than the source table */ ** than the source table */
int sortingIdx; /* Cursor number of the sorting index */ int sortingIdx; /* Cursor number of the sorting index */
int sortingIdxPTab; /* Cursor number of pseudo-table */ int sortingIdxPTab; /* Cursor number of pseudo-table */
ExprList *pGroupBy; /* The group by clause */
int nSortingColumn; /* Number of columns in the sorting index */ int nSortingColumn; /* Number of columns in the sorting index */
ExprList *pGroupBy; /* The group by clause */
struct AggInfo_col { /* For each column used in source tables */ struct AggInfo_col { /* For each column used in source tables */
Table *pTab; /* Source table */ Table *pTab; /* Source table */
int iTable; /* Cursor number of the source table */ int iTable; /* Cursor number of the source table */
@ -1564,7 +1547,6 @@ struct AggInfo {
Expr *pExpr; /* The original expression */ Expr *pExpr; /* The original expression */
} *aCol; } *aCol;
int nColumn; /* Number of used entries in aCol[] */ int nColumn; /* Number of used entries in aCol[] */
int nColumnAlloc; /* Number of slots allocated for aCol[] */
int nAccumulator; /* Number of columns that show through to the output. int nAccumulator; /* Number of columns that show through to the output.
** Additional columns are used only as parameters to ** Additional columns are used only as parameters to
** aggregate functions */ ** aggregate functions */
@ -1575,7 +1557,6 @@ struct AggInfo {
int iDistinct; /* Ephemeral table used to enforce DISTINCT */ int iDistinct; /* Ephemeral table used to enforce DISTINCT */
} *aFunc; } *aFunc;
int nFunc; /* Number of entries in aFunc[] */ int nFunc; /* Number of entries in aFunc[] */
int nFuncAlloc; /* Number of slots allocated for aFunc[] */
}; };
/* /*
@ -1693,6 +1674,7 @@ struct Expr {
i16 iRightJoinTable; /* If EP_FromJoin, the right table of the join */ i16 iRightJoinTable; /* If EP_FromJoin, the right table of the join */
u8 flags2; /* Second set of flags. EP2_... */ u8 flags2; /* Second set of flags. EP2_... */
u8 op2; /* If a TK_REGISTER, the original value of Expr.op */ u8 op2; /* If a TK_REGISTER, the original value of Expr.op */
/* If TK_COLUMN, the value of p5 for OP_Column */
AggInfo *pAggInfo; /* Used by TK_AGG_COLUMN and TK_AGG_FUNCTION */ AggInfo *pAggInfo; /* Used by TK_AGG_COLUMN and TK_AGG_FUNCTION */
Table *pTab; /* Table for TK_COLUMN expressions. */ Table *pTab; /* Table for TK_COLUMN expressions. */
#if SQLITE_MAX_EXPR_DEPTH>0 #if SQLITE_MAX_EXPR_DEPTH>0
@ -1715,7 +1697,7 @@ struct Expr {
#define EP_FixedDest 0x0200 /* Result needed in a specific register */ #define EP_FixedDest 0x0200 /* Result needed in a specific register */
#define EP_IntValue 0x0400 /* Integer value contained in u.iValue */ #define EP_IntValue 0x0400 /* Integer value contained in u.iValue */
#define EP_xIsSelect 0x0800 /* x.pSelect is valid (otherwise x.pList is) */ #define EP_xIsSelect 0x0800 /* x.pSelect is valid (otherwise x.pList is) */
#define EP_Hint 0x1000 /* Optimizer hint. Not required for correctness */ #define EP_Hint 0x1000 /* Not used */
#define EP_Reduced 0x2000 /* Expr struct is EXPR_REDUCEDSIZE bytes only */ #define EP_Reduced 0x2000 /* Expr struct is EXPR_REDUCEDSIZE bytes only */
#define EP_TokenOnly 0x4000 /* Expr struct is EXPR_TOKENONLYSIZE bytes only */ #define EP_TokenOnly 0x4000 /* Expr struct is EXPR_TOKENONLYSIZE bytes only */
#define EP_Static 0x8000 /* Held in memory not obtained from malloc() */ #define EP_Static 0x8000 /* Held in memory not obtained from malloc() */
@ -1772,9 +1754,8 @@ struct Expr {
*/ */
struct ExprList { struct ExprList {
int nExpr; /* Number of expressions on the list */ int nExpr; /* Number of expressions on the list */
int nAlloc; /* Number of entries allocated below */
int iECursor; /* VDBE Cursor associated with this ExprList */ int iECursor; /* VDBE Cursor associated with this ExprList */
struct ExprList_item { struct ExprList_item { /* For each expression in the list */
Expr *pExpr; /* The list of expressions */ Expr *pExpr; /* The list of expressions */
char *zName; /* Token associated with this expression */ char *zName; /* Token associated with this expression */
char *zSpan; /* Original text of the expression */ char *zSpan; /* Original text of the expression */
@ -1782,7 +1763,7 @@ struct ExprList {
u8 done; /* A flag to indicate when processing is finished */ u8 done; /* A flag to indicate when processing is finished */
u16 iOrderByCol; /* For ORDER BY, column number in result set */ u16 iOrderByCol; /* For ORDER BY, column number in result set */
u16 iAlias; /* Index into Parse.aAlias[] for zName */ u16 iAlias; /* Index into Parse.aAlias[] for zName */
} *a; /* One entry for each expression */ } *a; /* Alloc a power of two greater or equal to nExpr */
}; };
/* /*
@ -1817,7 +1798,6 @@ struct IdList {
int idx; /* Index in some Table.aCol[] of a column named zName */ int idx; /* Index in some Table.aCol[] of a column named zName */
} *a; } *a;
int nId; /* Number of identifiers on the list */ int nId; /* Number of identifiers on the list */
int nAlloc; /* Number of entries allocated for a[] below */
}; };
/* /*
@ -2031,7 +2011,6 @@ struct NameContext {
u8 allowAgg; /* Aggregate functions allowed here */ u8 allowAgg; /* Aggregate functions allowed here */
u8 hasAgg; /* True if aggregates are seen */ u8 hasAgg; /* True if aggregates are seen */
u8 isCheck; /* True if resolving names in a CHECK constraint */ u8 isCheck; /* True if resolving names in a CHECK constraint */
int nDepth; /* Depth of subquery recursion. 1 for no recursion */
AggInfo *pAggInfo; /* Information about aggregates at this level */ AggInfo *pAggInfo; /* Information about aggregates at this level */
NameContext *pNext; /* Next outer name context. NULL for outermost */ NameContext *pNext; /* Next outer name context. NULL for outermost */
}; };
@ -2061,6 +2040,9 @@ struct Select {
u8 op; /* One of: TK_UNION TK_ALL TK_INTERSECT TK_EXCEPT */ u8 op; /* One of: TK_UNION TK_ALL TK_INTERSECT TK_EXCEPT */
char affinity; /* MakeRecord with this affinity for SRT_Set */ char affinity; /* MakeRecord with this affinity for SRT_Set */
u16 selFlags; /* Various SF_* values */ u16 selFlags; /* Various SF_* values */
int iLimit, iOffset; /* Memory registers holding LIMIT & OFFSET counters */
int addrOpenEphm[3]; /* OP_OpenEphem opcodes related to this select */
double nSelectRow; /* Estimated number of result rows */
SrcList *pSrc; /* The FROM clause */ SrcList *pSrc; /* The FROM clause */
Expr *pWhere; /* The WHERE clause */ Expr *pWhere; /* The WHERE clause */
ExprList *pGroupBy; /* The GROUP BY clause */ ExprList *pGroupBy; /* The GROUP BY clause */
@ -2071,9 +2053,6 @@ struct Select {
Select *pRightmost; /* Right-most select in a compound select statement */ Select *pRightmost; /* Right-most select in a compound select statement */
Expr *pLimit; /* LIMIT expression. NULL means not used. */ Expr *pLimit; /* LIMIT expression. NULL means not used. */
Expr *pOffset; /* OFFSET expression. NULL means not used. */ Expr *pOffset; /* OFFSET expression. NULL means not used. */
int iLimit, iOffset; /* Memory registers holding LIMIT & OFFSET counters */
int addrOpenEphm[3]; /* OP_OpenEphem opcodes related to this select */
double nSelectRow; /* Estimated number of result rows */
}; };
/* /*
@ -2087,6 +2066,7 @@ struct Select {
#define SF_Expanded 0x10 /* sqlite3SelectExpand() called on this */ #define SF_Expanded 0x10 /* sqlite3SelectExpand() called on this */
#define SF_HasTypeInfo 0x20 /* FROM subqueries have Table metadata */ #define SF_HasTypeInfo 0x20 /* FROM subqueries have Table metadata */
#define SF_UseSorter 0x40 /* Sort using a sorter */ #define SF_UseSorter 0x40 /* Sort using a sorter */
#define SF_Values 0x80 /* Synthesized from VALUES clause */
/* /*
@ -2164,10 +2144,10 @@ struct AutoincInfo {
*/ */
struct TriggerPrg { struct TriggerPrg {
Trigger *pTrigger; /* Trigger this program was coded from */ Trigger *pTrigger; /* Trigger this program was coded from */
int orconf; /* Default ON CONFLICT policy */
SubProgram *pProgram; /* Program implementing pTrigger/orconf */
u32 aColmask[2]; /* Masks of old.*, new.* columns accessed */
TriggerPrg *pNext; /* Next entry in Parse.pTriggerPrg list */ TriggerPrg *pNext; /* Next entry in Parse.pTriggerPrg list */
SubProgram *pProgram; /* Program implementing pTrigger/orconf */
int orconf; /* Default ON CONFLICT policy */
u32 aColmask[2]; /* Masks of old.*, new.* columns accessed */
}; };
/* /*
@ -2197,14 +2177,18 @@ struct TriggerPrg {
*/ */
struct Parse { struct Parse {
sqlite3 *db; /* The main database structure */ sqlite3 *db; /* The main database structure */
int rc; /* Return code from execution */
char *zErrMsg; /* An error message */ char *zErrMsg; /* An error message */
Vdbe *pVdbe; /* An engine for executing database bytecode */ Vdbe *pVdbe; /* An engine for executing database bytecode */
int rc; /* Return code from execution */
u8 colNamesSet; /* TRUE after OP_ColumnName has been issued to pVdbe */ u8 colNamesSet; /* TRUE after OP_ColumnName has been issued to pVdbe */
u8 checkSchema; /* Causes schema cookie check after an error */ u8 checkSchema; /* Causes schema cookie check after an error */
u8 nested; /* Number of nested calls to the parser/code generator */ u8 nested; /* Number of nested calls to the parser/code generator */
u8 nTempReg; /* Number of temporary registers in aTempReg[] */ u8 nTempReg; /* Number of temporary registers in aTempReg[] */
u8 nTempInUse; /* Number of aTempReg[] currently checked out */ u8 nTempInUse; /* Number of aTempReg[] currently checked out */
u8 nColCache; /* Number of entries in aColCache[] */
u8 iColCache; /* Next entry in aColCache[] to replace */
u8 isMultiWrite; /* True if statement may modify/insert multiple rows */
u8 mayAbort; /* True if statement may throw an ABORT exception */
int aTempReg[8]; /* Holding area for temporary registers */ int aTempReg[8]; /* Holding area for temporary registers */
int nRangeReg; /* Size of the temporary register block */ int nRangeReg; /* Size of the temporary register block */
int iRangeReg; /* First register in temporary register block */ int iRangeReg; /* First register in temporary register block */
@ -2216,8 +2200,6 @@ struct Parse {
int ckBase; /* Base register of data during check constraints */ int ckBase; /* Base register of data during check constraints */
int iCacheLevel; /* ColCache valid when aColCache[].iLevel<=iCacheLevel */ int iCacheLevel; /* ColCache valid when aColCache[].iLevel<=iCacheLevel */
int iCacheCnt; /* Counter used to generate aColCache[].lru values */ int iCacheCnt; /* Counter used to generate aColCache[].lru values */
u8 nColCache; /* Number of entries in aColCache[] */
u8 iColCache; /* Next entry in aColCache[] to replace */
struct yColCache { struct yColCache {
int iTable; /* Table cursor number */ int iTable; /* Table cursor number */
int iColumn; /* Table column number */ int iColumn; /* Table column number */
@ -2228,61 +2210,64 @@ struct Parse {
} aColCache[SQLITE_N_COLCACHE]; /* One for each column cache entry */ } aColCache[SQLITE_N_COLCACHE]; /* One for each column cache entry */
yDbMask writeMask; /* Start a write transaction on these databases */ yDbMask writeMask; /* Start a write transaction on these databases */
yDbMask cookieMask; /* Bitmask of schema verified databases */ yDbMask cookieMask; /* Bitmask of schema verified databases */
u8 isMultiWrite; /* True if statement may affect/insert multiple rows */
u8 mayAbort; /* True if statement may throw an ABORT exception */
int cookieGoto; /* Address of OP_Goto to cookie verifier subroutine */ int cookieGoto; /* Address of OP_Goto to cookie verifier subroutine */
int cookieValue[SQLITE_MAX_ATTACHED+2]; /* Values of cookies to verify */ int cookieValue[SQLITE_MAX_ATTACHED+2]; /* Values of cookies to verify */
int regRowid; /* Register holding rowid of CREATE TABLE entry */
int regRoot; /* Register holding root page number for new objects */
int nMaxArg; /* Max args passed to user function by sub-program */
Token constraintName;/* Name of the constraint currently being parsed */
#ifndef SQLITE_OMIT_SHARED_CACHE #ifndef SQLITE_OMIT_SHARED_CACHE
int nTableLock; /* Number of locks in aTableLock */ int nTableLock; /* Number of locks in aTableLock */
TableLock *aTableLock; /* Required table locks for shared-cache mode */ TableLock *aTableLock; /* Required table locks for shared-cache mode */
#endif #endif
int regRowid; /* Register holding rowid of CREATE TABLE entry */
int regRoot; /* Register holding root page number for new objects */
AutoincInfo *pAinc; /* Information about AUTOINCREMENT counters */ AutoincInfo *pAinc; /* Information about AUTOINCREMENT counters */
int nMaxArg; /* Max args passed to user function by sub-program */
/* Information used while coding trigger programs. */ /* Information used while coding trigger programs. */
Parse *pToplevel; /* Parse structure for main program (or NULL) */ Parse *pToplevel; /* Parse structure for main program (or NULL) */
Table *pTriggerTab; /* Table triggers are being coded for */ Table *pTriggerTab; /* Table triggers are being coded for */
double nQueryLoop; /* Estimated number of iterations of a query */
u32 oldmask; /* Mask of old.* columns referenced */ u32 oldmask; /* Mask of old.* columns referenced */
u32 newmask; /* Mask of new.* columns referenced */ u32 newmask; /* Mask of new.* columns referenced */
u8 eTriggerOp; /* TK_UPDATE, TK_INSERT or TK_DELETE */ u8 eTriggerOp; /* TK_UPDATE, TK_INSERT or TK_DELETE */
u8 eOrconf; /* Default ON CONFLICT policy for trigger steps */ u8 eOrconf; /* Default ON CONFLICT policy for trigger steps */
u8 disableTriggers; /* True to disable triggers */ u8 disableTriggers; /* True to disable triggers */
double nQueryLoop; /* Estimated number of iterations of a query */
/* Above is constant between recursions. Below is reset before and after /* Above is constant between recursions. Below is reset before and after
** each recursion */ ** each recursion */
int nVar; /* Number of '?' variables seen in the SQL so far */ int nVar; /* Number of '?' variables seen in the SQL so far */
int nzVar; /* Number of available slots in azVar[] */ int nzVar; /* Number of available slots in azVar[] */
char **azVar; /* Pointers to names of parameters */ u8 explain; /* True if the EXPLAIN flag is found on the query */
Vdbe *pReprepare; /* VM being reprepared (sqlite3Reprepare()) */ #ifndef SQLITE_OMIT_VIRTUALTABLE
int nAlias; /* Number of aliased result set columns */ u8 declareVtab; /* True if inside sqlite3_declare_vtab() */
int *aAlias; /* Register used to hold aliased result */ int nVtabLock; /* Number of virtual tables to lock */
u8 explain; /* True if the EXPLAIN flag is found on the query */ #endif
Token sNameToken; /* Token with unqualified schema object name */ int nAlias; /* Number of aliased result set columns */
Token sLastToken; /* The last token parsed */ int nHeight; /* Expression tree height of current sub-select */
const char *zTail; /* All SQL text past the last semicolon parsed */ #ifndef SQLITE_OMIT_EXPLAIN
Table *pNewTable; /* A table being constructed by CREATE TABLE */ int iSelectId; /* ID of current select for EXPLAIN output */
int iNextSelectId; /* Next available select ID for EXPLAIN output */
#endif
char **azVar; /* Pointers to names of parameters */
Vdbe *pReprepare; /* VM being reprepared (sqlite3Reprepare()) */
int *aAlias; /* Register used to hold aliased result */
const char *zTail; /* All SQL text past the last semicolon parsed */
Table *pNewTable; /* A table being constructed by CREATE TABLE */
Trigger *pNewTrigger; /* Trigger under construct by a CREATE TRIGGER */ Trigger *pNewTrigger; /* Trigger under construct by a CREATE TRIGGER */
const char *zAuthContext; /* The 6th parameter to db->xAuth callbacks */ const char *zAuthContext; /* The 6th parameter to db->xAuth callbacks */
Token sNameToken; /* Token with unqualified schema object name */
Token sLastToken; /* The last token parsed */
#ifndef SQLITE_OMIT_VIRTUALTABLE #ifndef SQLITE_OMIT_VIRTUALTABLE
Token sArg; /* Complete text of a module argument */ Token sArg; /* Complete text of a module argument */
u8 declareVtab; /* True if inside sqlite3_declare_vtab() */ Table **apVtabLock; /* Pointer to virtual tables needing locking */
int nVtabLock; /* Number of virtual tables to lock */
Table **apVtabLock; /* Pointer to virtual tables needing locking */
#endif
int nHeight; /* Expression tree height of current sub-select */
Table *pZombieTab; /* List of Table objects to delete after code gen */
TriggerPrg *pTriggerPrg; /* Linked list of coded triggers */
#ifndef SQLITE_OMIT_EXPLAIN
int iSelectId;
int iNextSelectId;
#endif #endif
Table *pZombieTab; /* List of Table objects to delete after code gen */
TriggerPrg *pTriggerPrg; /* Linked list of coded triggers */
}; };
/*
** Return true if currently inside an sqlite3_declare_vtab() call.
*/
#ifdef SQLITE_OMIT_VIRTUALTABLE #ifdef SQLITE_OMIT_VIRTUALTABLE
#define IN_DECLARE_VTAB 0 #define IN_DECLARE_VTAB 0
#else #else
@ -2299,7 +2284,7 @@ struct AuthContext {
}; };
/* /*
** Bitfield flags for P5 value in OP_Insert and OP_Delete ** Bitfield flags for P5 value in various opcodes.
*/ */
#define OPFLAG_NCHANGE 0x01 /* Set to update db->nChange */ #define OPFLAG_NCHANGE 0x01 /* Set to update db->nChange */
#define OPFLAG_LASTROWID 0x02 /* Set to update db->lastRowid */ #define OPFLAG_LASTROWID 0x02 /* Set to update db->lastRowid */
@ -2307,6 +2292,8 @@ struct AuthContext {
#define OPFLAG_APPEND 0x08 /* This is likely to be an append */ #define OPFLAG_APPEND 0x08 /* This is likely to be an append */
#define OPFLAG_USESEEKRESULT 0x10 /* Try to avoid a seek in BtreeInsert() */ #define OPFLAG_USESEEKRESULT 0x10 /* Try to avoid a seek in BtreeInsert() */
#define OPFLAG_CLEARCACHE 0x20 /* Clear pseudo-table cache in OP_Column */ #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() */
/* /*
* Each trigger present in the database schema is stored as an instance of * Each trigger present in the database schema is stored as an instance of
@ -2433,8 +2420,8 @@ struct StrAccum {
*/ */
typedef struct { typedef struct {
sqlite3 *db; /* The database being initialized */ sqlite3 *db; /* The database being initialized */
int iDb; /* 0 for main database. 1 for TEMP, 2.. for ATTACHed */
char **pzErrMsg; /* Error message stored here */ char **pzErrMsg; /* Error message stored here */
int iDb; /* 0 for main database. 1 for TEMP, 2.. for ATTACHed */
int rc; /* Result code stored here */ int rc; /* Result code stored here */
} InitData; } InitData;
@ -2489,6 +2476,7 @@ struct Walker {
union { /* Extra data for callback */ union { /* Extra data for callback */
NameContext *pNC; /* Naming context */ NameContext *pNC; /* Naming context */
int i; /* Integer value */ int i; /* Integer value */
SrcList *pSrcList; /* FROM clause */
} u; } u;
}; };
@ -2576,7 +2564,7 @@ int sqlite3CantopenError(int);
/* /*
** Internal function prototypes ** Internal function prototypes
*/ */
int sqlite3StrICmp(const char *, const char *); #define sqlite3StrICmp sqlite3_stricmp
int sqlite3Strlen30(const char*); int sqlite3Strlen30(const char*);
#define sqlite3StrNICmp sqlite3_strnicmp #define sqlite3StrNICmp sqlite3_strnicmp
@ -2724,6 +2712,7 @@ void sqlite3AddCollateType(Parse*, Token*);
void sqlite3EndTable(Parse*,Token*,Token*,Select*); void sqlite3EndTable(Parse*,Token*,Token*,Select*);
int sqlite3ParseUri(const char*,const char*,unsigned int*, int sqlite3ParseUri(const char*,const char*,unsigned int*,
sqlite3_vfs**,char**,char **); sqlite3_vfs**,char**,char **);
Btree *sqlite3DbNameToBtree(sqlite3*,const char*);
int sqlite3CodeOnce(Parse *); int sqlite3CodeOnce(Parse *);
Bitvec *sqlite3BitvecCreate(u32); Bitvec *sqlite3BitvecCreate(u32);
@ -2759,7 +2748,7 @@ void sqlite3DeleteTable(sqlite3*, Table*);
# define sqlite3AutoincrementEnd(X) # define sqlite3AutoincrementEnd(X)
#endif #endif
void sqlite3Insert(Parse*, SrcList*, ExprList*, Select*, IdList*, int); void sqlite3Insert(Parse*, SrcList*, ExprList*, Select*, IdList*, int);
void *sqlite3ArrayAllocate(sqlite3*,void*,int,int,int*,int*,int*); void *sqlite3ArrayAllocate(sqlite3*,void*,int,int*,int*);
IdList *sqlite3IdListAppend(sqlite3*, IdList*, Token*); IdList *sqlite3IdListAppend(sqlite3*, IdList*, Token*);
int sqlite3IdListIndex(IdList*,const char*); int sqlite3IdListIndex(IdList*,const char*);
SrcList *sqlite3SrcListEnlarge(sqlite3*, SrcList*, int, int); SrcList *sqlite3SrcListEnlarge(sqlite3*, SrcList*, int, int);
@ -2789,7 +2778,7 @@ void sqlite3DeleteFrom(Parse*, SrcList*, Expr*);
void sqlite3Update(Parse*, SrcList*, ExprList*, Expr*, int); void sqlite3Update(Parse*, SrcList*, ExprList*, Expr*, int);
WhereInfo *sqlite3WhereBegin(Parse*, SrcList*, Expr*, ExprList**,ExprList*,u16); WhereInfo *sqlite3WhereBegin(Parse*, SrcList*, Expr*, ExprList**,ExprList*,u16);
void sqlite3WhereEnd(WhereInfo*); void sqlite3WhereEnd(WhereInfo*);
int sqlite3ExprCodeGetColumn(Parse*, Table*, int, int, int); int sqlite3ExprCodeGetColumn(Parse*, Table*, int, int, int, u8);
void sqlite3ExprCodeGetColumnOfTable(Vdbe*, Table*, int, int, int); void sqlite3ExprCodeGetColumnOfTable(Vdbe*, Table*, int, int, int);
void sqlite3ExprCodeMove(Parse*, int, int, int); void sqlite3ExprCodeMove(Parse*, int, int, int);
void sqlite3ExprCodeCopy(Parse*, int, int, int); void sqlite3ExprCodeCopy(Parse*, int, int, int);
@ -2823,7 +2812,7 @@ Vdbe *sqlite3GetVdbe(Parse*);
void sqlite3PrngSaveState(void); void sqlite3PrngSaveState(void);
void sqlite3PrngRestoreState(void); void sqlite3PrngRestoreState(void);
void sqlite3PrngResetState(void); void sqlite3PrngResetState(void);
void sqlite3RollbackAll(sqlite3*); void sqlite3RollbackAll(sqlite3*,int);
void sqlite3CodeVerifySchema(Parse*, int); void sqlite3CodeVerifySchema(Parse*, int);
void sqlite3CodeVerifyNamedSchema(Parse*, const char *zDb); void sqlite3CodeVerifyNamedSchema(Parse*, const char *zDb);
void sqlite3BeginTransaction(Parse*, int); void sqlite3BeginTransaction(Parse*, int);
@ -2856,7 +2845,7 @@ SrcList *sqlite3SrcListDup(sqlite3*,SrcList*,int);
IdList *sqlite3IdListDup(sqlite3*,IdList*); IdList *sqlite3IdListDup(sqlite3*,IdList*);
Select *sqlite3SelectDup(sqlite3*,Select*,int); Select *sqlite3SelectDup(sqlite3*,Select*,int);
void sqlite3FuncDefInsert(FuncDefHash*, FuncDef*); void sqlite3FuncDefInsert(FuncDefHash*, FuncDef*);
FuncDef *sqlite3FindFunction(sqlite3*,const char*,int,int,u8,int); FuncDef *sqlite3FindFunction(sqlite3*,const char*,int,int,u8,u8);
void sqlite3RegisterBuiltinFunctions(sqlite3*); void sqlite3RegisterBuiltinFunctions(sqlite3*);
void sqlite3RegisterDateTimeFunctions(void); void sqlite3RegisterDateTimeFunctions(void);
void sqlite3RegisterGlobalFunctions(void); void sqlite3RegisterGlobalFunctions(void);
@ -2997,7 +2986,7 @@ void sqlite3FileSuffix3(const char*, char*);
#else #else
# define sqlite3FileSuffix3(X,Y) # define sqlite3FileSuffix3(X,Y)
#endif #endif
u8 sqlite3GetBoolean(const char *z); u8 sqlite3GetBoolean(const char *z,int);
const void *sqlite3ValueText(sqlite3_value*, u8); const void *sqlite3ValueText(sqlite3_value*, u8);
int sqlite3ValueBytes(sqlite3_value*, u8); int sqlite3ValueBytes(sqlite3_value*, u8);
@ -3123,7 +3112,7 @@ void sqlite3AutoLoadExtensions(sqlite3*);
# define sqlite3VtabInSync(db) ((db)->nVTrans>0 && (db)->aVTrans==0) # define sqlite3VtabInSync(db) ((db)->nVTrans>0 && (db)->aVTrans==0)
#endif #endif
void sqlite3VtabMakeWritable(Parse*,Table*); void sqlite3VtabMakeWritable(Parse*,Table*);
void sqlite3VtabBeginParse(Parse*, Token*, Token*, Token*); void sqlite3VtabBeginParse(Parse*, Token*, Token*, Token*, int);
void sqlite3VtabFinishParse(Parse*, Token*); void sqlite3VtabFinishParse(Parse*, Token*);
void sqlite3VtabArgInit(Parse*); void sqlite3VtabArgInit(Parse*);
void sqlite3VtabArgExtend(Parse*, Token*); void sqlite3VtabArgExtend(Parse*, Token*);

View File

@ -224,10 +224,12 @@ int sqlite3_db_status(
** to zero. ** to zero.
*/ */
case SQLITE_DBSTATUS_CACHE_HIT: case SQLITE_DBSTATUS_CACHE_HIT:
case SQLITE_DBSTATUS_CACHE_MISS: { case SQLITE_DBSTATUS_CACHE_MISS:
case SQLITE_DBSTATUS_CACHE_WRITE:{
int i; int i;
int nRet = 0; int nRet = 0;
assert( SQLITE_DBSTATUS_CACHE_MISS==SQLITE_DBSTATUS_CACHE_HIT+1 ); assert( SQLITE_DBSTATUS_CACHE_MISS==SQLITE_DBSTATUS_CACHE_HIT+1 );
assert( SQLITE_DBSTATUS_CACHE_WRITE==SQLITE_DBSTATUS_CACHE_HIT+2 );
for(i=0; i<db->nDb; i++){ for(i=0; i<db->nDb; i++){
if( db->aDb[i].pBt ){ if( db->aDb[i].pBt ){

View File

@ -1163,7 +1163,7 @@ static int dbPrepareAndBind(
memset(pPreStmt, 0, nByte); memset(pPreStmt, 0, nByte);
pPreStmt->pStmt = pStmt; pPreStmt->pStmt = pStmt;
pPreStmt->nSql = (*pzOut - zSql); pPreStmt->nSql = (int)(*pzOut - zSql);
pPreStmt->zSql = sqlite3_sql(pStmt); pPreStmt->zSql = sqlite3_sql(pStmt);
pPreStmt->apParm = (Tcl_Obj **)&pPreStmt[1]; pPreStmt->apParm = (Tcl_Obj **)&pPreStmt[1];
#ifdef SQLITE_TEST #ifdef SQLITE_TEST
@ -3108,23 +3108,19 @@ EXTERN int Sqlite3_Init(Tcl_Interp *interp){
return TCL_OK; return TCL_OK;
} }
EXTERN int Tclsqlite3_Init(Tcl_Interp *interp){ return Sqlite3_Init(interp); } EXTERN int Tclsqlite3_Init(Tcl_Interp *interp){ return Sqlite3_Init(interp); }
EXTERN int Sqlite3_SafeInit(Tcl_Interp *interp){ return TCL_OK; }
EXTERN int Tclsqlite3_SafeInit(Tcl_Interp *interp){ return TCL_OK; }
EXTERN int Sqlite3_Unload(Tcl_Interp *interp, int flags){ return TCL_OK; } EXTERN int Sqlite3_Unload(Tcl_Interp *interp, int flags){ return TCL_OK; }
EXTERN int Tclsqlite3_Unload(Tcl_Interp *interp, int flags){ return TCL_OK; } EXTERN int Tclsqlite3_Unload(Tcl_Interp *interp, int flags){ return TCL_OK; }
EXTERN int Sqlite3_SafeUnload(Tcl_Interp *interp, int flags){ return TCL_OK; }
EXTERN int Tclsqlite3_SafeUnload(Tcl_Interp *interp, int flags){ return TCL_OK;}
/* Because it accesses the file-system and uses persistent state, SQLite
** is not considered appropriate for safe interpreters. Hence, we deliberately
** omit the _SafeInit() interfaces.
*/
#ifndef SQLITE_3_SUFFIX_ONLY #ifndef SQLITE_3_SUFFIX_ONLY
int Sqlite_Init(Tcl_Interp *interp){ return Sqlite3_Init(interp); } int Sqlite_Init(Tcl_Interp *interp){ return Sqlite3_Init(interp); }
int Tclsqlite_Init(Tcl_Interp *interp){ return Sqlite3_Init(interp); } int Tclsqlite_Init(Tcl_Interp *interp){ return Sqlite3_Init(interp); }
int Sqlite_SafeInit(Tcl_Interp *interp){ return TCL_OK; }
int Tclsqlite_SafeInit(Tcl_Interp *interp){ return TCL_OK; }
int Sqlite_Unload(Tcl_Interp *interp, int flags){ return TCL_OK; } int Sqlite_Unload(Tcl_Interp *interp, int flags){ return TCL_OK; }
int Tclsqlite_Unload(Tcl_Interp *interp, int flags){ return TCL_OK; } int Tclsqlite_Unload(Tcl_Interp *interp, int flags){ return TCL_OK; }
int Sqlite_SafeUnload(Tcl_Interp *interp, int flags){ return TCL_OK; }
int Tclsqlite_SafeUnload(Tcl_Interp *interp, int flags){ return TCL_OK;}
#endif #endif
#ifdef TCLSH #ifdef TCLSH
@ -3474,7 +3470,7 @@ static int md5file_cmd(void*cd, Tcl_Interp*interp, int argc, const char **argv){
MD5Init(&ctx); MD5Init(&ctx);
for(;;){ for(;;){
int n; int n;
n = fread(zBuf, 1, sizeof(zBuf), in); n = (int)fread(zBuf, 1, sizeof(zBuf), in);
if( n<=0 ) break; if( n<=0 ) break;
MD5Update(&ctx, (unsigned char*)zBuf, (unsigned)n); MD5Update(&ctx, (unsigned char*)zBuf, (unsigned)n);
} }
@ -3520,7 +3516,7 @@ static void md5step(sqlite3_context *context, int argc, sqlite3_value **argv){
for(i=0; i<argc; i++){ for(i=0; i<argc; i++){
const char *zData = (char*)sqlite3_value_text(argv[i]); const char *zData = (char*)sqlite3_value_text(argv[i]);
if( zData ){ if( zData ){
MD5Update(p, (unsigned char*)zData, strlen(zData)); MD5Update(p, (unsigned char*)zData, (int)strlen(zData));
} }
} }
} }

View File

@ -668,6 +668,7 @@ static int test_key(
int argc, /* Number of arguments */ int argc, /* Number of arguments */
char **argv /* Text of each argument */ char **argv /* Text of each argument */
){ ){
#ifdef SQLITE_HAS_CODEC
sqlite3 *db; sqlite3 *db;
const char *zKey; const char *zKey;
int nKey; int nKey;
@ -679,7 +680,6 @@ static int test_key(
if( getDbPointer(interp, argv[1], &db) ) return TCL_ERROR; if( getDbPointer(interp, argv[1], &db) ) return TCL_ERROR;
zKey = argv[2]; zKey = argv[2];
nKey = strlen(zKey); nKey = strlen(zKey);
#ifdef SQLITE_HAS_CODEC
sqlite3_key(db, zKey, nKey); sqlite3_key(db, zKey, nKey);
#endif #endif
return TCL_OK; return TCL_OK;
@ -696,6 +696,7 @@ static int test_rekey(
int argc, /* Number of arguments */ int argc, /* Number of arguments */
char **argv /* Text of each argument */ char **argv /* Text of each argument */
){ ){
#ifdef SQLITE_HAS_CODEC
sqlite3 *db; sqlite3 *db;
const char *zKey; const char *zKey;
int nKey; int nKey;
@ -707,7 +708,6 @@ static int test_rekey(
if( getDbPointer(interp, argv[1], &db) ) return TCL_ERROR; if( getDbPointer(interp, argv[1], &db) ) return TCL_ERROR;
zKey = argv[2]; zKey = argv[2];
nKey = strlen(zKey); nKey = strlen(zKey);
#ifdef SQLITE_HAS_CODEC
sqlite3_rekey(db, zKey, nKey); sqlite3_rekey(db, zKey, nKey);
#endif #endif
return TCL_OK; return TCL_OK;
@ -800,7 +800,7 @@ struct dstr {
** Append text to a dstr ** Append text to a dstr
*/ */
static void dstrAppend(struct dstr *p, const char *z, int divider){ static void dstrAppend(struct dstr *p, const char *z, int divider){
int n = strlen(z); int n = (int)strlen(z);
if( p->nUsed + n + 2 > p->nAlloc ){ if( p->nUsed + n + 2 > p->nAlloc ){
char *zNew; char *zNew;
p->nAlloc = p->nAlloc*2 + n + 200; p->nAlloc = p->nAlloc*2 + n + 200;
@ -2369,7 +2369,6 @@ static int uses_stmt_journal(
Tcl_Obj *CONST objv[] Tcl_Obj *CONST objv[]
){ ){
sqlite3_stmt *pStmt; sqlite3_stmt *pStmt;
int rc;
if( objc!=2 ){ if( objc!=2 ){
Tcl_AppendResult(interp, "wrong # args: should be \"", Tcl_AppendResult(interp, "wrong # args: should be \"",
@ -2378,7 +2377,7 @@ static int uses_stmt_journal(
} }
if( getStmtPointer(interp, Tcl_GetString(objv[1]), &pStmt) ) return TCL_ERROR; if( getStmtPointer(interp, Tcl_GetString(objv[1]), &pStmt) ) return TCL_ERROR;
rc = sqlite3_stmt_readonly(pStmt); sqlite3_stmt_readonly(pStmt);
Tcl_SetObjResult(interp, Tcl_NewBooleanObj(((Vdbe *)pStmt)->usesStmtJournal)); Tcl_SetObjResult(interp, Tcl_NewBooleanObj(((Vdbe *)pStmt)->usesStmtJournal));
return TCL_OK; return TCL_OK;
} }
@ -3264,7 +3263,7 @@ static int test_bind_text16(
char *value; char *value;
int rc; int rc;
void (*xDel)() = (objc==6?SQLITE_STATIC:SQLITE_TRANSIENT); void (*xDel)(void*) = (objc==6?SQLITE_STATIC:SQLITE_TRANSIENT);
Tcl_Obj *oStmt = objv[objc-4]; Tcl_Obj *oStmt = objv[objc-4];
Tcl_Obj *oN = objv[objc-3]; Tcl_Obj *oN = objv[objc-3];
Tcl_Obj *oString = objv[objc-2]; Tcl_Obj *oString = objv[objc-2];
@ -3610,10 +3609,10 @@ static int test_prepare(
if( sqlite3TestErrCode(interp, db, rc) ) return TCL_ERROR; if( sqlite3TestErrCode(interp, db, rc) ) return TCL_ERROR;
if( zTail && objc>=5 ){ if( zTail && objc>=5 ){
if( bytes>=0 ){ if( bytes>=0 ){
bytes = bytes - (zTail-zSql); bytes = bytes - (int)(zTail-zSql);
} }
if( strlen(zTail)<bytes ){ if( (int)strlen(zTail)<bytes ){
bytes = strlen(zTail); bytes = (int)strlen(zTail);
} }
Tcl_ObjSetVar2(interp, objv[4], 0, Tcl_NewStringObj(zTail, bytes), 0); Tcl_ObjSetVar2(interp, objv[4], 0, Tcl_NewStringObj(zTail, bytes), 0);
} }
@ -3668,7 +3667,7 @@ static int test_prepare_v2(
if( sqlite3TestErrCode(interp, db, rc) ) return TCL_ERROR; if( sqlite3TestErrCode(interp, db, rc) ) return TCL_ERROR;
if( zTail && objc>=5 ){ if( zTail && objc>=5 ){
if( bytes>=0 ){ if( bytes>=0 ){
bytes = bytes - (zTail-zSql); bytes = bytes - (int)(zTail-zSql);
} }
Tcl_ObjSetVar2(interp, objv[4], 0, Tcl_NewStringObj(zTail, bytes), 0); Tcl_ObjSetVar2(interp, objv[4], 0, Tcl_NewStringObj(zTail, bytes), 0);
} }
@ -3769,7 +3768,7 @@ static int test_prepare16(
if( objc>=5 ){ if( objc>=5 ){
if( zTail ){ if( zTail ){
objlen = objlen - ((u8 *)zTail-(u8 *)zSql); objlen = objlen - (int)((u8 *)zTail-(u8 *)zSql);
}else{ }else{
objlen = 0; objlen = 0;
} }
@ -3829,7 +3828,7 @@ static int test_prepare16_v2(
if( objc>=5 ){ if( objc>=5 ){
if( zTail ){ if( zTail ){
objlen = objlen - ((u8 *)zTail-(u8 *)zSql); objlen = objlen - (int)((u8 *)zTail-(u8 *)zSql);
}else{ }else{
objlen = 0; objlen = 0;
} }
@ -3858,7 +3857,6 @@ static int test_open(
){ ){
const char *zFilename; const char *zFilename;
sqlite3 *db; sqlite3 *db;
int rc;
char zBuf[100]; char zBuf[100];
if( objc!=3 && objc!=2 && objc!=1 ){ if( objc!=3 && objc!=2 && objc!=1 ){
@ -3868,7 +3866,7 @@ static int test_open(
} }
zFilename = objc>1 ? Tcl_GetString(objv[1]) : 0; zFilename = objc>1 ? Tcl_GetString(objv[1]) : 0;
rc = sqlite3_open(zFilename, &db); sqlite3_open(zFilename, &db);
if( sqlite3TestMakePointerStr(interp, zBuf, db) ) return TCL_ERROR; if( sqlite3TestMakePointerStr(interp, zBuf, db) ) return TCL_ERROR;
Tcl_AppendResult(interp, zBuf, 0); Tcl_AppendResult(interp, zBuf, 0);
@ -3957,7 +3955,6 @@ static int test_open16(
#ifndef SQLITE_OMIT_UTF16 #ifndef SQLITE_OMIT_UTF16
const void *zFilename; const void *zFilename;
sqlite3 *db; sqlite3 *db;
int rc;
char zBuf[100]; char zBuf[100];
if( objc!=3 ){ if( objc!=3 ){
@ -3967,7 +3964,7 @@ static int test_open16(
} }
zFilename = Tcl_GetByteArrayFromObj(objv[1], 0); zFilename = Tcl_GetByteArrayFromObj(objv[1], 0);
rc = sqlite3_open16(zFilename, &db); sqlite3_open16(zFilename, &db);
if( sqlite3TestMakePointerStr(interp, zBuf, db) ) return TCL_ERROR; if( sqlite3TestMakePointerStr(interp, zBuf, db) ) return TCL_ERROR;
Tcl_AppendResult(interp, zBuf, 0); Tcl_AppendResult(interp, zBuf, 0);
@ -4668,6 +4665,30 @@ static int test_db_filename(
return TCL_OK; return TCL_OK;
} }
/*
** Usage: sqlite3_db_readonly DB DBNAME
**
** Return 1 or 0 if DBNAME is readonly or not. Return -1 if DBNAME does
** not exist.
*/
static int test_db_readonly(
void * clientData,
Tcl_Interp *interp,
int objc,
Tcl_Obj *CONST objv[]
){
sqlite3 *db;
const char *zDbName;
if( objc!=3 ){
Tcl_WrongNumArgs(interp, 1, objv, "DB DBNAME");
return TCL_ERROR;
}
if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
zDbName = Tcl_GetString(objv[2]);
Tcl_SetObjResult(interp, Tcl_NewIntObj(sqlite3_db_readonly(db, zDbName)));
return TCL_OK;
}
/* /*
** Usage: sqlite3_soft_heap_limit ?N? ** Usage: sqlite3_soft_heap_limit ?N?
** **
@ -5114,8 +5135,6 @@ static int file_control_lockproxy_test(
Tcl_Obj *CONST objv[] /* Command arguments */ Tcl_Obj *CONST objv[] /* Command arguments */
){ ){
sqlite3 *db; sqlite3 *db;
const char *zPwd;
int nPwd;
if( objc!=3 ){ if( objc!=3 ){
Tcl_AppendResult(interp, "wrong # args: should be \"", Tcl_AppendResult(interp, "wrong # args: should be \"",
@ -5125,7 +5144,6 @@ static int file_control_lockproxy_test(
if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ){ if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ){
return TCL_ERROR; return TCL_ERROR;
} }
zPwd = Tcl_GetStringFromObj(objv[2], &nPwd);
#if !defined(SQLITE_ENABLE_LOCKING_STYLE) #if !defined(SQLITE_ENABLE_LOCKING_STYLE)
# if defined(__APPLE__) # if defined(__APPLE__)
@ -5138,8 +5156,11 @@ static int file_control_lockproxy_test(
{ {
char *testPath; char *testPath;
int rc; int rc;
int nPwd;
const char *zPwd;
char proxyPath[400]; char proxyPath[400];
zPwd = Tcl_GetStringFromObj(objv[2], &nPwd);
if( sizeof(proxyPath)<nPwd+20 ){ if( sizeof(proxyPath)<nPwd+20 ){
Tcl_AppendResult(interp, "PWD too big", (void*)0); Tcl_AppendResult(interp, "PWD too big", (void*)0);
return TCL_ERROR; return TCL_ERROR;
@ -5795,6 +5816,7 @@ struct win32FileLocker {
#if SQLITE_OS_WIN #if SQLITE_OS_WIN
#include <process.h>
/* /*
** The background thread that does file locking. ** The background thread that does file locking.
*/ */
@ -6058,6 +6080,7 @@ int Sqlitetest1_Init(Tcl_Interp *interp){
{ "sqlite3_release_memory", test_release_memory, 0}, { "sqlite3_release_memory", test_release_memory, 0},
{ "sqlite3_db_release_memory", test_db_release_memory, 0}, { "sqlite3_db_release_memory", test_db_release_memory, 0},
{ "sqlite3_db_filename", test_db_filename, 0}, { "sqlite3_db_filename", test_db_filename, 0},
{ "sqlite3_db_readonly", test_db_readonly, 0},
{ "sqlite3_soft_heap_limit", test_soft_heap_limit, 0}, { "sqlite3_soft_heap_limit", test_soft_heap_limit, 0},
{ "sqlite3_thread_cleanup", test_thread_cleanup, 0}, { "sqlite3_thread_cleanup", test_thread_cleanup, 0},
{ "sqlite3_pager_refcounts", test_pager_refcounts, 0}, { "sqlite3_pager_refcounts", test_pager_refcounts, 0},
@ -6106,7 +6129,7 @@ int Sqlitetest1_Init(Tcl_Interp *interp){
#endif #endif
#ifdef SQLITE_ENABLE_COLUMN_METADATA #ifdef SQLITE_ENABLE_COLUMN_METADATA
{"sqlite3_column_database_name16", {"sqlite3_column_database_name16",
test_stmt_utf16, sqlite3_column_database_name16}, test_stmt_utf16, (void*)sqlite3_column_database_name16},
{"sqlite3_column_table_name16", test_stmt_utf16, (void*)sqlite3_column_table_name16}, {"sqlite3_column_table_name16", test_stmt_utf16, (void*)sqlite3_column_table_name16},
{"sqlite3_column_origin_name16", test_stmt_utf16, (void*)sqlite3_column_origin_name16}, {"sqlite3_column_origin_name16", test_stmt_utf16, (void*)sqlite3_column_origin_name16},
#endif #endif

View File

@ -547,7 +547,7 @@ static int fake_big_file(
if( Tcl_GetInt(interp, argv[1], &n) ) return TCL_ERROR; if( Tcl_GetInt(interp, argv[1], &n) ) return TCL_ERROR;
pVfs = sqlite3_vfs_find(0); pVfs = sqlite3_vfs_find(0);
nFile = strlen(argv[2]); nFile = (int)strlen(argv[2]);
zFile = sqlite3_malloc( nFile+2 ); zFile = sqlite3_malloc( nFile+2 );
if( zFile==0 ) return TCL_ERROR; if( zFile==0 ) return TCL_ERROR;
memcpy(zFile, argv[2], nFile+1); memcpy(zFile, argv[2], nFile+1);

View File

@ -80,7 +80,7 @@ static int btree_open(
sDb.mutex = sqlite3MutexAlloc(SQLITE_MUTEX_RECURSIVE); sDb.mutex = sqlite3MutexAlloc(SQLITE_MUTEX_RECURSIVE);
sqlite3_mutex_enter(sDb.mutex); sqlite3_mutex_enter(sDb.mutex);
} }
n = strlen(argv[1]); n = (int)strlen(argv[1]);
zFilename = sqlite3_malloc( n+2 ); zFilename = sqlite3_malloc( n+2 );
if( zFilename==0 ) return TCL_ERROR; if( zFilename==0 ) return TCL_ERROR;
memcpy(zFilename, argv[1], n+1); memcpy(zFilename, argv[1], n+1);
@ -465,7 +465,7 @@ static int btree_varint_test(
if( Tcl_GetInt(interp, argv[4], (int*)&incr) ) return TCL_ERROR; if( Tcl_GetInt(interp, argv[4], (int*)&incr) ) return TCL_ERROR;
in = start; in = start;
in *= mult; in *= mult;
for(i=0; i<count; i++){ for(i=0; i<(int)count; i++){
char zErr[200]; char zErr[200];
n1 = putVarint(zBuf, in); n1 = putVarint(zBuf, in);
if( n1>9 || n1<1 ){ if( n1>9 || n1<1 ){

View File

@ -64,7 +64,6 @@ static int test_value_overhead(
int repeat_count; int repeat_count;
int i; int i;
Mem val; Mem val;
const char *zVal;
if( objc!=3 ){ if( objc!=3 ){
Tcl_AppendResult(interp, "wrong # args: should be \"", Tcl_AppendResult(interp, "wrong # args: should be \"",
@ -82,7 +81,7 @@ static int test_value_overhead(
for(i=0; i<repeat_count; i++){ for(i=0; i<repeat_count; i++){
if( do_calls ){ if( do_calls ){
zVal = (char*)sqlite3_value_text(&val); sqlite3_value_text(&val);
} }
} }

View File

@ -177,7 +177,7 @@ static int writeDbFile(CrashFile *p, u8 *z, i64 iAmt, i64 iOff){
iSkip = 512; iSkip = 512;
} }
if( (iAmt-iSkip)>0 ){ if( (iAmt-iSkip)>0 ){
rc = sqlite3OsWrite(p->pRealFile, &z[iSkip], iAmt-iSkip, iOff+iSkip); rc = sqlite3OsWrite(p->pRealFile, &z[iSkip], (int)(iAmt-iSkip), iOff+iSkip);
} }
return rc; return rc;
} }
@ -306,8 +306,8 @@ static int writeListSync(CrashFile *pFile, int isCrash){
} }
case 3: { /* Trash sectors */ case 3: { /* Trash sectors */
u8 *zGarbage; u8 *zGarbage;
int iFirst = (pWrite->iOffset/g.iSectorSize); int iFirst = (int)(pWrite->iOffset/g.iSectorSize);
int iLast = (pWrite->iOffset+pWrite->nBuf-1)/g.iSectorSize; int iLast = (int)((pWrite->iOffset+pWrite->nBuf-1)/g.iSectorSize);
assert(pWrite->zBuf); assert(pWrite->zBuf);
@ -430,7 +430,7 @@ static int cfWrite(
){ ){
CrashFile *pCrash = (CrashFile *)pFile; CrashFile *pCrash = (CrashFile *)pFile;
if( iAmt+iOfst>pCrash->iSize ){ if( iAmt+iOfst>pCrash->iSize ){
pCrash->iSize = iAmt+iOfst; pCrash->iSize = (int)(iAmt+iOfst);
} }
while( pCrash->iSize>pCrash->nData ){ while( pCrash->iSize>pCrash->nData ){
u8 *zNew; u8 *zNew;
@ -454,7 +454,7 @@ static int cfTruncate(sqlite3_file *pFile, sqlite_int64 size){
CrashFile *pCrash = (CrashFile *)pFile; CrashFile *pCrash = (CrashFile *)pFile;
assert(size>=0); assert(size>=0);
if( pCrash->iSize>size ){ if( pCrash->iSize>size ){
pCrash->iSize = size; pCrash->iSize = (int)size;
} }
return writeListAppend(pFile, size, 0, 0); return writeListAppend(pFile, size, 0, 0);
} }
@ -468,15 +468,23 @@ static int cfSync(sqlite3_file *pFile, int flags){
const char *zName = pCrash->zName; const char *zName = pCrash->zName;
const char *zCrashFile = g.zCrashFile; const char *zCrashFile = g.zCrashFile;
int nName = strlen(zName); int nName = (int)strlen(zName);
int nCrashFile = strlen(zCrashFile); int nCrashFile = (int)strlen(zCrashFile);
if( nCrashFile>0 && zCrashFile[nCrashFile-1]=='*' ){ if( nCrashFile>0 && zCrashFile[nCrashFile-1]=='*' ){
nCrashFile--; nCrashFile--;
if( nName>nCrashFile ) nName = nCrashFile; if( nName>nCrashFile ) nName = nCrashFile;
} }
#ifdef TRACE_CRASHTEST
printf("cfSync(): nName = %d, nCrashFile = %d, zName = %s, zCrashFile = %s\n",
nName, nCrashFile, zName, zCrashFile);
#endif
if( nName==nCrashFile && 0==memcmp(zName, zCrashFile, nName) ){ if( nName==nCrashFile && 0==memcmp(zName, zCrashFile, nName) ){
#ifdef TRACE_CRASHTEST
printf("cfSync(): name matched, g.iCrash = %d\n", g.iCrash);
#endif
if( (--g.iCrash)==0 ) isCrash = 1; if( (--g.iCrash)==0 ) isCrash = 1;
} }
@ -510,7 +518,7 @@ static int cfFileControl(sqlite3_file *pFile, int op, void *pArg){
i64 nByte = *(i64 *)pArg; i64 nByte = *(i64 *)pArg;
if( nByte>pCrash->iSize ){ if( nByte>pCrash->iSize ){
if( SQLITE_OK==writeListAppend(pFile, nByte, 0, 0) ){ if( SQLITE_OK==writeListAppend(pFile, nByte, 0, 0) ){
pCrash->iSize = nByte; pCrash->iSize = (int)nByte;
} }
} }
return SQLITE_OK; return SQLITE_OK;
@ -627,11 +635,11 @@ static int cfOpen(
iChunk = PENDING_BYTE; iChunk = PENDING_BYTE;
} }
memset(pWrapper->zData, 0, pWrapper->nData); memset(pWrapper->zData, 0, pWrapper->nData);
rc = sqlite3OsRead(pReal, pWrapper->zData, iChunk, 0); rc = sqlite3OsRead(pReal, pWrapper->zData, (int)iChunk, 0);
if( SQLITE_OK==rc && pWrapper->iSize>(PENDING_BYTE+512) && isDb ){ if( SQLITE_OK==rc && pWrapper->iSize>(PENDING_BYTE+512) && isDb ){
i64 iOff = PENDING_BYTE+512; i64 iOff = PENDING_BYTE+512;
iChunk = pWrapper->iSize - iOff; iChunk = pWrapper->iSize - iOff;
rc = sqlite3OsRead(pReal, &pWrapper->zData[iOff], iChunk, iOff); rc = sqlite3OsRead(pReal, &pWrapper->zData[iOff], (int)iChunk, iOff);
} }
}else{ }else{
rc = SQLITE_NOMEM; rc = SQLITE_NOMEM;

View File

@ -192,7 +192,7 @@ static int getColumnNames(
rc = SQLITE_NOMEM; rc = SQLITE_NOMEM;
goto out; goto out;
} }
nBytes += strlen(zName)+1; nBytes += (int)strlen(zName)+1;
} }
aCol = (char **)sqlite3MallocZero(nBytes); aCol = (char **)sqlite3MallocZero(nBytes);
if( !aCol ){ if( !aCol ){
@ -831,13 +831,10 @@ static int echoBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
if( !isIgnoreUsable && !pConstraint->usable ) continue; if( !isIgnoreUsable && !pConstraint->usable ) continue;
iCol = pConstraint->iColumn; iCol = pConstraint->iColumn;
if( pVtab->aIndex[iCol] || iCol<0 ){ if( iCol<0 || pVtab->aIndex[iCol] ){
char *zCol = pVtab->aCol[iCol]; char *zCol = iCol>=0 ? pVtab->aCol[iCol] : "rowid";
char *zOp = 0; char *zOp = 0;
useIdx = 1; useIdx = 1;
if( iCol<0 ){
zCol = "rowid";
}
switch( pConstraint->op ){ switch( pConstraint->op ){
case SQLITE_INDEX_CONSTRAINT_EQ: case SQLITE_INDEX_CONSTRAINT_EQ:
zOp = "="; break; zOp = "="; break;
@ -870,13 +867,12 @@ static int echoBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
** on a column that this virtual table has an index for, then consume ** on a column that this virtual table has an index for, then consume
** the ORDER BY clause. ** the ORDER BY clause.
*/ */
if( pIdxInfo->nOrderBy==1 && pVtab->aIndex[pIdxInfo->aOrderBy->iColumn] ){ if( pIdxInfo->nOrderBy==1 && (
pIdxInfo->aOrderBy->iColumn<0 ||
pVtab->aIndex[pIdxInfo->aOrderBy->iColumn]) ){
int iCol = pIdxInfo->aOrderBy->iColumn; int iCol = pIdxInfo->aOrderBy->iColumn;
char *zCol = pVtab->aCol[iCol]; char *zCol = iCol>=0 ? pVtab->aCol[iCol] : "rowid";
char *zDir = pIdxInfo->aOrderBy->desc?"DESC":"ASC"; char *zDir = pIdxInfo->aOrderBy->desc?"DESC":"ASC";
if( iCol<0 ){
zCol = "rowid";
}
zNew = sqlite3_mprintf(" ORDER BY %s %s", zCol, zDir); zNew = sqlite3_mprintf(" ORDER BY %s %s", zCol, zDir);
string_concat(&zQuery, zNew, 1, &rc); string_concat(&zQuery, zNew, 1, &rc);
pIdxInfo->orderByConsumed = 1; pIdxInfo->orderByConsumed = 1;
@ -1221,7 +1217,7 @@ static int echoRename(sqlite3_vtab *vtab, const char *zNewName){
} }
if( p->isPattern ){ if( p->isPattern ){
int nThis = strlen(p->zThis); int nThis = (int)strlen(p->zThis);
char *zSql = sqlite3_mprintf("ALTER TABLE %s RENAME TO %s%s", char *zSql = sqlite3_mprintf("ALTER TABLE %s RENAME TO %s%s",
p->zTableName, zNewName, &p->zTableName[nThis] p->zTableName, zNewName, &p->zTableName[nThis]
); );

View File

@ -420,6 +420,12 @@ Tcl_SetVar2(interp, "sqlite_options", "long_double",
Tcl_SetVar2(interp, "sqlite_options", "rtree", "0", TCL_GLOBAL_ONLY); Tcl_SetVar2(interp, "sqlite_options", "rtree", "0", TCL_GLOBAL_ONLY);
#endif #endif
#ifdef SQLITE_RTREE_INT_ONLY
Tcl_SetVar2(interp, "sqlite_options", "rtree_int_only", "1", TCL_GLOBAL_ONLY);
#else
Tcl_SetVar2(interp, "sqlite_options", "rtree_int_only", "0", TCL_GLOBAL_ONLY);
#endif
#ifdef SQLITE_OMIT_SCHEMA_PRAGMAS #ifdef SQLITE_OMIT_SCHEMA_PRAGMAS
Tcl_SetVar2(interp, "sqlite_options", "schema_pragmas", "0", TCL_GLOBAL_ONLY); Tcl_SetVar2(interp, "sqlite_options", "schema_pragmas", "0", TCL_GLOBAL_ONLY);
#else #else

View File

@ -202,7 +202,7 @@ static void test_auxdata(
}else { }else {
zRet[i*2] = '0'; zRet[i*2] = '0';
} }
n = strlen(z) + 1; n = (int)strlen(z) + 1;
zAux = testContextMalloc(pCtx, n); zAux = testContextMalloc(pCtx, n);
if( zAux ){ if( zAux ){
memcpy(zAux, z, n); memcpy(zAux, z, n);

View File

@ -10,43 +10,58 @@
** **
************************************************************************* *************************************************************************
** **
** Code for demonstartion virtual table that generates variations ** Code for a demonstration virtual table that generates variations
** on an input word at increasing edit distances from the original. ** on an input word at increasing edit distances from the original.
** **
** A fuzzer virtual table is created like this: ** A fuzzer virtual table is created like this:
** **
** CREATE VIRTUAL TABLE temp.f USING fuzzer; ** CREATE VIRTUAL TABLE f USING fuzzer(<fuzzer-data-table>);
** **
** The name of the new virtual table in the example above is "f". ** When it is created, the new fuzzer table must be supplied with the
** Note that all fuzzer virtual tables must be TEMP tables. The ** name of a "fuzzer data table", which must reside in the same database
** "temp." prefix in front of the table name is required when the ** file as the new fuzzer table. The fuzzer data table contains the various
** table is being created. The "temp." prefix can be omitted when ** transformations and their costs that the fuzzer logic uses to generate
** using the table as long as the name is unambiguous. ** variations.
** **
** Before being used, the fuzzer needs to be programmed by giving it ** The fuzzer data table must contain exactly four columns (more precisely,
** character transformations and a cost associated with each transformation. ** the statement "SELECT * FROM <fuzzer_data_table>" must return records
** Examples: ** that consist of four columns). It does not matter what the columns are
** named.
** **
** INSERT INTO f(cFrom,cTo,Cost) VALUES('','a',100); ** Each row in the fuzzer data table represents a single character
** transformation. The left most column of the row (column 0) contains an
** integer value - the identifier of the ruleset to which the transformation
** rule belongs (see "MULTIPLE RULE SETS" below). The second column of the
** row (column 0) contains the input character or characters. The third
** column contains the output character or characters. And the fourth column
** contains the integer cost of making the transformation. For example:
** **
** The above statement says that the cost of inserting a letter 'a' is ** CREATE TABLE f_data(ruleset, cFrom, cTo, Cost);
** 100. (All costs are integers. We recommend that costs be scaled so ** INSERT INTO f_data(ruleset, cFrom, cTo, Cost) VALUES(0, '', 'a', 100);
** that the average cost is around 100.) ** INSERT INTO f_data(ruleset, cFrom, cTo, Cost) VALUES(0, 'b', '', 87);
** INSERT INTO f_data(ruleset, cFrom, cTo, Cost) VALUES(0, 'o', 'oe', 38);
** INSERT INTO f_data(ruleset, cFrom, cTo, Cost) VALUES(0, 'oe', 'o', 40);
** **
** INSERT INTO f(cFrom,cTo,Cost) VALUES('b','',87); ** The first row inserted into the fuzzer data table by the SQL script
** ** above indicates that the cost of inserting a letter 'a' is 100. (All
** The above statement says that the cost of deleting a single letter ** costs are integers. We recommend that costs be scaled so that the
** 'b' is 87. ** average cost is around 100.) The second INSERT statement creates a rule
** ** saying that the cost of deleting a single letter 'b' is 87. The third
** INSERT INTO f(cFrom,cTo,Cost) VALUES('o','oe',38); ** and fourth INSERT statements mean that the cost of transforming a
** INSERT INTO f(cFrom,cTo,Cost) VALUES('oe','o',40); ** single letter "o" into the two-letter sequence "oe" is 38 and that the
**
** This third example says that the cost of transforming the single
** letter "o" into the two-letter sequence "oe" is 38 and that the
** cost of transforming "oe" back into "o" is 40. ** cost of transforming "oe" back into "o" is 40.
** **
** After all the transformation costs have been set, the fuzzer table ** The contents of the fuzzer data table are loaded into main memory when
** can be queried as follows: ** a fuzzer table is first created, and may be internally reloaded by the
** system at any subsequent time. Therefore, the fuzzer data table should be
** populated before the fuzzer table is created and not modified thereafter.
** If you do need to modify the contents of the fuzzer data table, it is
** recommended that the associated fuzzer table be dropped, the fuzzer data
** table edited, and the fuzzer table recreated within a single transaction.
** Alternatively, the fuzzer data table can be edited then the database
** connection can be closed and reopened.
**
** Once it has been created, the fuzzer table can be queried as follows:
** **
** SELECT word, distance FROM f ** SELECT word, distance FROM f
** WHERE word MATCH 'abcdefg' ** WHERE word MATCH 'abcdefg'
@ -61,6 +76,9 @@
** the one that is returned. In the example, the search is limited to ** the one that is returned. In the example, the search is limited to
** strings with a total distance of less than 200. ** strings with a total distance of less than 200.
** **
** The fuzzer is a read-only table. Any attempt to DELETE, INSERT, or
** UPDATE on a fuzzer table will throw an error.
**
** It is important to put some kind of a limit on the fuzzer output. This ** It is important to put some kind of a limit on the fuzzer output. This
** can be either in the form of a LIMIT clause at the end of the query, ** can be either in the form of a LIMIT clause at the end of the query,
** or better, a "distance<NNN" constraint where NNN is some number. The ** or better, a "distance<NNN" constraint where NNN is some number. The
@ -93,7 +111,42 @@
** **
** This last query will show up to 50 words out of the vocabulary that ** This last query will show up to 50 words out of the vocabulary that
** match or nearly match the $prefix. ** match or nearly match the $prefix.
**
** MULTIPLE RULE SETS
**
** Normally, the "ruleset" value associated with all character transformations
** in the fuzzer data table is zero. However, if required, the fuzzer table
** allows multiple rulesets to be defined. Each query uses only a single
** ruleset. This allows, for example, a single fuzzer table to support
** multiple languages.
**
** By default, only the rules from ruleset 0 are used. To specify an
** alternative ruleset, a "ruleset = ?" expression must be added to the
** WHERE clause of a SELECT, where ? is the identifier of the desired
** ruleset. For example:
**
** SELECT vocabulary.w FROM f, vocabulary
** WHERE f.word MATCH $word
** AND f.distance<=200
** AND f.word=vocabulary.w
** AND f.ruleset=1 -- Specify the ruleset to use here
** LIMIT 20
**
** If no "ruleset = ?" constraint is specified in the WHERE clause, ruleset
** 0 is used.
**
** LIMITS
**
** The maximum ruleset number is 2147483647. The maximum length of either
** of the strings in the second or third column of the fuzzer data table
** is 50 bytes. The maximum cost on a rule is 1000.
*/ */
/* If SQLITE_DEBUG is not defined, disable assert statements. */
#if !defined(NDEBUG) && !defined(SQLITE_DEBUG)
# define NDEBUG
#endif
#include "sqlite3.h" #include "sqlite3.h"
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
@ -112,10 +165,25 @@ typedef struct fuzzer_seen fuzzer_seen;
typedef struct fuzzer_stem fuzzer_stem; typedef struct fuzzer_stem fuzzer_stem;
/* /*
** Type of the "cost" of an edit operation. Might be changed to ** Various types.
** "float" or "double" or "sqlite3_int64" in the future. **
** fuzzer_cost is the "cost" of an edit operation.
**
** fuzzer_len is the length of a matching string.
**
** fuzzer_ruleid is an ruleset identifier.
*/ */
typedef int fuzzer_cost; typedef int fuzzer_cost;
typedef signed char fuzzer_len;
typedef int fuzzer_ruleid;
/*
** Limits
*/
#define FUZZER_MX_LENGTH 50 /* Maximum length of a rule string */
#define FUZZER_MX_RULEID 2147483647 /* Maximum rule ID */
#define FUZZER_MX_COST 1000 /* Maximum single-rule cost */
#define FUZZER_MX_OUTPUT_LENGTH 100 /* Maximum length of an output string */
/* /*
@ -123,11 +191,12 @@ typedef int fuzzer_cost;
** All rules are kept on a linked list sorted by rCost. ** All rules are kept on a linked list sorted by rCost.
*/ */
struct fuzzer_rule { struct fuzzer_rule {
fuzzer_rule *pNext; /* Next rule in order of increasing rCost */ fuzzer_rule *pNext; /* Next rule in order of increasing rCost */
fuzzer_cost rCost; /* Cost of this transformation */ char *zFrom; /* Transform from */
int nFrom, nTo; /* Length of the zFrom and zTo strings */ fuzzer_cost rCost; /* Cost of this transformation */
char *zFrom; /* Transform from */ fuzzer_len nFrom, nTo; /* Length of the zFrom and zTo strings */
char zTo[4]; /* Transform to (extra space appended) */ fuzzer_ruleid iRuleset; /* The rule set to which this rule belongs */
char zTo[4]; /* Transform to (extra space appended) */
}; };
/* /*
@ -143,13 +212,13 @@ struct fuzzer_rule {
*/ */
struct fuzzer_stem { struct fuzzer_stem {
char *zBasis; /* Word being fuzzed */ char *zBasis; /* Word being fuzzed */
int nBasis; /* Length of the zBasis string */
const fuzzer_rule *pRule; /* Current rule to apply */ const fuzzer_rule *pRule; /* Current rule to apply */
int n; /* Apply pRule at this character offset */
fuzzer_cost rBaseCost; /* Base cost of getting to zBasis */
fuzzer_cost rCostX; /* Precomputed rBaseCost + pRule->rCost */
fuzzer_stem *pNext; /* Next stem in rCost order */ fuzzer_stem *pNext; /* Next stem in rCost order */
fuzzer_stem *pHash; /* Next stem with same hash on zBasis */ fuzzer_stem *pHash; /* Next stem with same hash on zBasis */
fuzzer_cost rBaseCost; /* Base cost of getting to zBasis */
fuzzer_cost rCostX; /* Precomputed rBaseCost + pRule->rCost */
fuzzer_len nBasis; /* Length of the zBasis string */
fuzzer_len n; /* Apply pRule at this character offset */
}; };
/* /*
@ -159,7 +228,6 @@ struct fuzzer_vtab {
sqlite3_vtab base; /* Base class - must be first */ sqlite3_vtab base; /* Base class - must be first */
char *zClassName; /* Name of this class. Default: "fuzzer" */ char *zClassName; /* Name of this class. Default: "fuzzer" */
fuzzer_rule *pRule; /* All active rules in this fuzzer */ fuzzer_rule *pRule; /* All active rules in this fuzzer */
fuzzer_rule *pNewRule; /* New rules to add when last cursor expires */
int nCursor; /* Number of active cursors */ int nCursor; /* Number of active cursors */
}; };
@ -179,54 +247,11 @@ struct fuzzer_cursor {
char *zBuf; /* Temporary use buffer */ char *zBuf; /* Temporary use buffer */
int nBuf; /* Bytes allocated for zBuf */ int nBuf; /* Bytes allocated for zBuf */
int nStem; /* Number of stems allocated */ int nStem; /* Number of stems allocated */
int iRuleset; /* Only process rules from this ruleset */
fuzzer_rule nullRule; /* Null rule used first */ fuzzer_rule nullRule; /* Null rule used first */
fuzzer_stem *apHash[FUZZER_HASH]; /* Hash of previously generated terms */ fuzzer_stem *apHash[FUZZER_HASH]; /* Hash of previously generated terms */
}; };
/* Methods for the fuzzer module */
static int fuzzerConnect(
sqlite3 *db,
void *pAux,
int argc, const char *const*argv,
sqlite3_vtab **ppVtab,
char **pzErr
){
fuzzer_vtab *pNew;
int n;
if( strcmp(argv[1],"temp")!=0 ){
*pzErr = sqlite3_mprintf("%s virtual tables must be TEMP", argv[0]);
return SQLITE_ERROR;
}
n = strlen(argv[0]) + 1;
pNew = sqlite3_malloc( sizeof(*pNew) + n );
if( pNew==0 ) return SQLITE_NOMEM;
pNew->zClassName = (char*)&pNew[1];
memcpy(pNew->zClassName, argv[0], n);
sqlite3_declare_vtab(db, "CREATE TABLE x(word,distance,cFrom,cTo,cost)");
memset(pNew, 0, sizeof(*pNew));
*ppVtab = &pNew->base;
return SQLITE_OK;
}
/* Note that for this virtual table, the xCreate and xConnect
** methods are identical. */
static int fuzzerDisconnect(sqlite3_vtab *pVtab){
fuzzer_vtab *p = (fuzzer_vtab*)pVtab;
assert( p->nCursor==0 );
do{
while( p->pRule ){
fuzzer_rule *pRule = p->pRule;
p->pRule = pRule->pNext;
sqlite3_free(pRule);
}
p->pRule = p->pNewRule;
p->pNewRule = 0;
}while( p->pRule );
sqlite3_free(p);
return SQLITE_OK;
}
/* The xDisconnect and xDestroy methods are also the same */
/* /*
** The two input rule lists are both sorted in order of increasing ** The two input rule lists are both sorted in order of increasing
** cost. Merge them together into a single list, sorted by cost, and ** cost. Merge them together into a single list, sorted by cost, and
@ -256,6 +281,270 @@ static fuzzer_rule *fuzzerMergeRules(fuzzer_rule *pA, fuzzer_rule *pB){
return head.pNext; return head.pNext;
} }
/*
** Statement pStmt currently points to a row in the fuzzer data table. This
** function allocates and populates a fuzzer_rule structure according to
** the content of the row.
**
** If successful, *ppRule is set to point to the new object and SQLITE_OK
** is returned. Otherwise, *ppRule is zeroed, *pzErr may be set to point
** to an error message and an SQLite error code returned.
*/
static int fuzzerLoadOneRule(
fuzzer_vtab *p, /* Fuzzer virtual table handle */
sqlite3_stmt *pStmt, /* Base rule on statements current row */
fuzzer_rule **ppRule, /* OUT: New rule object */
char **pzErr /* OUT: Error message */
){
sqlite3_int64 iRuleset = sqlite3_column_int64(pStmt, 0);
const char *zFrom = (const char *)sqlite3_column_text(pStmt, 1);
const char *zTo = (const char *)sqlite3_column_text(pStmt, 2);
int nCost = sqlite3_column_int(pStmt, 3);
int rc = SQLITE_OK; /* Return code */
int nFrom; /* Size of string zFrom, in bytes */
int nTo; /* Size of string zTo, in bytes */
fuzzer_rule *pRule = 0; /* New rule object to return */
if( zFrom==0 ) zFrom = "";
if( zTo==0 ) zTo = "";
nFrom = (int)strlen(zFrom);
nTo = (int)strlen(zTo);
/* Silently ignore null transformations */
if( strcmp(zFrom, zTo)==0 ){
*ppRule = 0;
return SQLITE_OK;
}
if( nCost<=0 || nCost>FUZZER_MX_COST ){
*pzErr = sqlite3_mprintf("%s: cost must be between 1 and %d",
p->zClassName, FUZZER_MX_COST
);
rc = SQLITE_ERROR;
}else
if( nFrom>FUZZER_MX_LENGTH || nTo>FUZZER_MX_LENGTH ){
*pzErr = sqlite3_mprintf("%s: maximum string length is %d",
p->zClassName, FUZZER_MX_LENGTH
);
rc = SQLITE_ERROR;
}else
if( iRuleset<0 || iRuleset>FUZZER_MX_RULEID ){
*pzErr = sqlite3_mprintf("%s: ruleset must be between 0 and %d",
p->zClassName, FUZZER_MX_RULEID
);
rc = SQLITE_ERROR;
}else{
pRule = sqlite3_malloc( sizeof(*pRule) + nFrom + nTo );
if( pRule==0 ){
rc = SQLITE_NOMEM;
}else{
memset(pRule, 0, sizeof(*pRule));
pRule->zFrom = &pRule->zTo[nTo+1];
pRule->nFrom = nFrom;
memcpy(pRule->zFrom, zFrom, nFrom+1);
memcpy(pRule->zTo, zTo, nTo+1);
pRule->nTo = nTo;
pRule->rCost = nCost;
pRule->iRuleset = (int)iRuleset;
}
}
*ppRule = pRule;
return rc;
}
/*
** Load the content of the fuzzer data table into memory.
*/
static int fuzzerLoadRules(
sqlite3 *db, /* Database handle */
fuzzer_vtab *p, /* Virtual fuzzer table to configure */
const char *zDb, /* Database containing rules data */
const char *zData, /* Table containing rules data */
char **pzErr /* OUT: Error message */
){
int rc = SQLITE_OK; /* Return code */
char *zSql; /* SELECT used to read from rules table */
fuzzer_rule *pHead = 0;
zSql = sqlite3_mprintf("SELECT * FROM %Q.%Q", zDb, zData);
if( zSql==0 ){
rc = SQLITE_NOMEM;
}else{
int rc2; /* finalize() return code */
sqlite3_stmt *pStmt = 0;
rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0);
if( rc!=SQLITE_OK ){
*pzErr = sqlite3_mprintf("%s: %s", p->zClassName, sqlite3_errmsg(db));
}else if( sqlite3_column_count(pStmt)!=4 ){
*pzErr = sqlite3_mprintf("%s: %s has %d columns, expected 4",
p->zClassName, zData, sqlite3_column_count(pStmt)
);
rc = SQLITE_ERROR;
}else{
while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pStmt) ){
fuzzer_rule *pRule = 0;
rc = fuzzerLoadOneRule(p, pStmt, &pRule, pzErr);
if( pRule ){
pRule->pNext = pHead;
pHead = pRule;
}
}
}
rc2 = sqlite3_finalize(pStmt);
if( rc==SQLITE_OK ) rc = rc2;
}
sqlite3_free(zSql);
/* All rules are now in a singly linked list starting at pHead. This
** block sorts them by cost and then sets fuzzer_vtab.pRule to point to
** point to the head of the sorted list.
*/
if( rc==SQLITE_OK ){
unsigned int i;
fuzzer_rule *pX;
fuzzer_rule *a[15];
for(i=0; i<sizeof(a)/sizeof(a[0]); i++) a[i] = 0;
while( (pX = pHead)!=0 ){
pHead = pX->pNext;
pX->pNext = 0;
for(i=0; a[i] && i<sizeof(a)/sizeof(a[0])-1; i++){
pX = fuzzerMergeRules(a[i], pX);
a[i] = 0;
}
a[i] = fuzzerMergeRules(a[i], pX);
}
for(pX=a[0], i=1; i<sizeof(a)/sizeof(a[0]); i++){
pX = fuzzerMergeRules(a[i], pX);
}
p->pRule = fuzzerMergeRules(p->pRule, pX);
}else{
/* An error has occurred. Setting p->pRule to point to the head of the
** allocated list ensures that the list will be cleaned up in this case.
*/
assert( p->pRule==0 );
p->pRule = pHead;
}
return rc;
}
/*
** This function converts an SQL quoted string into an unquoted string
** and returns a pointer to a buffer allocated using sqlite3_malloc()
** containing the result. The caller should eventually free this buffer
** using sqlite3_free.
**
** Examples:
**
** "abc" becomes abc
** 'xyz' becomes xyz
** [pqr] becomes pqr
** `mno` becomes mno
*/
static char *fuzzerDequote(const char *zIn){
int nIn; /* Size of input string, in bytes */
char *zOut; /* Output (dequoted) string */
nIn = (int)strlen(zIn);
zOut = sqlite3_malloc(nIn+1);
if( zOut ){
char q = zIn[0]; /* Quote character (if any ) */
if( q!='[' && q!= '\'' && q!='"' && q!='`' ){
memcpy(zOut, zIn, nIn+1);
}else{
int iOut = 0; /* Index of next byte to write to output */
int iIn; /* Index of next byte to read from input */
if( q=='[' ) q = ']';
for(iIn=1; iIn<nIn; iIn++){
if( zIn[iIn]==q ) iIn++;
zOut[iOut++] = zIn[iIn];
}
}
assert( (int)strlen(zOut)<=nIn );
}
return zOut;
}
/*
** xDisconnect/xDestroy method for the fuzzer module.
*/
static int fuzzerDisconnect(sqlite3_vtab *pVtab){
fuzzer_vtab *p = (fuzzer_vtab*)pVtab;
assert( p->nCursor==0 );
while( p->pRule ){
fuzzer_rule *pRule = p->pRule;
p->pRule = pRule->pNext;
sqlite3_free(pRule);
}
sqlite3_free(p);
return SQLITE_OK;
}
/*
** xConnect/xCreate method for the fuzzer module. Arguments are:
**
** argv[0] -> module name ("fuzzer")
** argv[1] -> database name
** argv[2] -> table name
** argv[3] -> fuzzer rule table name
*/
static int fuzzerConnect(
sqlite3 *db,
void *pAux,
int argc, const char *const*argv,
sqlite3_vtab **ppVtab,
char **pzErr
){
int rc = SQLITE_OK; /* Return code */
fuzzer_vtab *pNew = 0; /* New virtual table */
const char *zModule = argv[0];
const char *zDb = argv[1];
if( argc!=4 ){
*pzErr = sqlite3_mprintf(
"%s: wrong number of CREATE VIRTUAL TABLE arguments", zModule
);
rc = SQLITE_ERROR;
}else{
int nModule; /* Length of zModule, in bytes */
nModule = (int)strlen(zModule);
pNew = sqlite3_malloc( sizeof(*pNew) + nModule + 1);
if( pNew==0 ){
rc = SQLITE_NOMEM;
}else{
char *zTab; /* Dequoted name of fuzzer data table */
memset(pNew, 0, sizeof(*pNew));
pNew->zClassName = (char*)&pNew[1];
memcpy(pNew->zClassName, zModule, nModule+1);
zTab = fuzzerDequote(argv[3]);
if( zTab==0 ){
rc = SQLITE_NOMEM;
}else{
rc = fuzzerLoadRules(db, pNew, zDb, zTab, pzErr);
sqlite3_free(zTab);
}
if( rc==SQLITE_OK ){
rc = sqlite3_declare_vtab(db, "CREATE TABLE x(word,distance,ruleset)");
}
if( rc!=SQLITE_OK ){
fuzzerDisconnect((sqlite3_vtab *)pNew);
pNew = 0;
}
}
}
*ppVtab = (sqlite3_vtab *)pNew;
return rc;
}
/* /*
** Open a new fuzzer cursor. ** Open a new fuzzer cursor.
@ -268,25 +557,6 @@ static int fuzzerOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){
memset(pCur, 0, sizeof(*pCur)); memset(pCur, 0, sizeof(*pCur));
pCur->pVtab = p; pCur->pVtab = p;
*ppCursor = &pCur->base; *ppCursor = &pCur->base;
if( p->nCursor==0 && p->pNewRule ){
unsigned int i;
fuzzer_rule *pX;
fuzzer_rule *a[15];
for(i=0; i<sizeof(a)/sizeof(a[0]); i++) a[i] = 0;
while( (pX = p->pNewRule)!=0 ){
p->pNewRule = pX->pNext;
pX->pNext = 0;
for(i=0; a[i] && i<sizeof(a)/sizeof(a[0])-1; i++){
pX = fuzzerMergeRules(a[i], pX);
a[i] = 0;
}
a[i] = fuzzerMergeRules(a[i], pX);
}
for(pX=a[0], i=1; i<sizeof(a)/sizeof(a[0]); i++){
pX = fuzzerMergeRules(a[i], pX);
}
p->pRule = fuzzerMergeRules(p->pRule, pX);
}
p->nCursor++; p->nCursor++;
return SQLITE_OK; return SQLITE_OK;
} }
@ -343,8 +613,8 @@ static int fuzzerRender(
int *pnBuf /* Size of the buffer */ int *pnBuf /* Size of the buffer */
){ ){
const fuzzer_rule *pRule = pStem->pRule; const fuzzer_rule *pRule = pStem->pRule;
int n; int n; /* Size of output term without nul-term */
char *z; char *z; /* Buffer to assemble output term in */
n = pStem->nBasis + pRule->nTo - pRule->nFrom; n = pStem->nBasis + pRule->nTo - pRule->nFrom;
if( (*pnBuf)<n+1 ){ if( (*pnBuf)<n+1 ){
@ -362,6 +632,8 @@ static int fuzzerRender(
memcpy(&z[n+pRule->nTo], &pStem->zBasis[n+pRule->nFrom], memcpy(&z[n+pRule->nTo], &pStem->zBasis[n+pRule->nFrom],
pStem->nBasis-n-pRule->nFrom+1); pStem->nBasis-n-pRule->nFrom+1);
} }
assert( z[pStem->nBasis + pRule->nTo - pRule->nFrom]==0 );
return SQLITE_OK; return SQLITE_OK;
} }
@ -424,12 +696,31 @@ static int fuzzerSeen(fuzzer_cursor *pCur, fuzzer_stem *pStem){
} }
h = fuzzerHash(pCur->zBuf); h = fuzzerHash(pCur->zBuf);
pLookup = pCur->apHash[h]; pLookup = pCur->apHash[h];
while( pLookup && strcmp(pLookup->zBasis, pCur->zBuf)!=0 ){ while( pLookup && strcmp(pLookup->zBasis, pCur->zBuf)!=0 ){
pLookup = pLookup->pHash; pLookup = pLookup->pHash;
} }
return pLookup!=0; return pLookup!=0;
} }
/*
** If argument pRule is NULL, this function returns false.
**
** Otherwise, it returns true if rule pRule should be skipped. A rule
** should be skipped if it does not belong to rule-set iRuleset, or if
** applying it to stem pStem would create a string longer than
** FUZZER_MX_OUTPUT_LENGTH bytes.
*/
static int fuzzerSkipRule(
const fuzzer_rule *pRule, /* Determine whether or not to skip this */
fuzzer_stem *pStem, /* Stem rule may be applied to */
int iRuleset /* Rule-set used by the current query */
){
return pRule && (
(pRule->iRuleset!=iRuleset)
|| (pStem->nBasis + pRule->nTo - pRule->nFrom)>FUZZER_MX_OUTPUT_LENGTH
);
}
/* /*
** Advance a fuzzer_stem to its next value. Return 0 if there are ** Advance a fuzzer_stem to its next value. Return 0 if there are
** no more values that can be generated by this fuzzer_stem. Return ** no more values that can be generated by this fuzzer_stem. Return
@ -438,6 +729,7 @@ static int fuzzerSeen(fuzzer_cursor *pCur, fuzzer_stem *pStem){
static int fuzzerAdvance(fuzzer_cursor *pCur, fuzzer_stem *pStem){ static int fuzzerAdvance(fuzzer_cursor *pCur, fuzzer_stem *pStem){
const fuzzer_rule *pRule; const fuzzer_rule *pRule;
while( (pRule = pStem->pRule)!=0 ){ while( (pRule = pStem->pRule)!=0 ){
assert( pRule==&pCur->nullRule || pRule->iRuleset==pCur->iRuleset );
while( pStem->n < pStem->nBasis - pRule->nFrom ){ while( pStem->n < pStem->nBasis - pRule->nFrom ){
pStem->n++; pStem->n++;
if( pRule->nFrom==0 if( pRule->nFrom==0
@ -453,8 +745,11 @@ static int fuzzerAdvance(fuzzer_cursor *pCur, fuzzer_stem *pStem){
} }
} }
pStem->n = -1; pStem->n = -1;
pStem->pRule = pRule->pNext; do{
if( pStem->pRule && fuzzerCost(pStem)>pCur->rLimit ) pStem->pRule = 0; pRule = pRule->pNext;
}while( fuzzerSkipRule(pRule, pStem, pCur->iRuleset) );
pStem->pRule = pRule;
if( pRule && fuzzerCost(pStem)>pCur->rLimit ) pStem->pRule = 0;
} }
return 0; return 0;
} }
@ -572,15 +867,20 @@ static fuzzer_stem *fuzzerNewStem(
fuzzer_cost rBaseCost fuzzer_cost rBaseCost
){ ){
fuzzer_stem *pNew; fuzzer_stem *pNew;
fuzzer_rule *pRule;
unsigned int h; unsigned int h;
pNew = sqlite3_malloc( sizeof(*pNew) + strlen(zWord) + 1 ); pNew = sqlite3_malloc( sizeof(*pNew) + (int)strlen(zWord) + 1 );
if( pNew==0 ) return 0; if( pNew==0 ) return 0;
memset(pNew, 0, sizeof(*pNew)); memset(pNew, 0, sizeof(*pNew));
pNew->zBasis = (char*)&pNew[1]; pNew->zBasis = (char*)&pNew[1];
pNew->nBasis = strlen(zWord); pNew->nBasis = (int)strlen(zWord);
memcpy(pNew->zBasis, zWord, pNew->nBasis+1); memcpy(pNew->zBasis, zWord, pNew->nBasis+1);
pNew->pRule = pCur->pVtab->pRule; pRule = pCur->pVtab->pRule;
while( fuzzerSkipRule(pRule, pNew, pCur->iRuleset) ){
pRule = pRule->pNext;
}
pNew->pRule = pRule;
pNew->n = -1; pNew->n = -1;
pNew->rBaseCost = pNew->rCostX = rBaseCost; pNew->rBaseCost = pNew->rCostX = rBaseCost;
h = fuzzerHash(pNew->zBasis); h = fuzzerHash(pNew->zBasis);
@ -627,7 +927,10 @@ static int fuzzerNext(sqlite3_vtab_cursor *cur){
** stem list is the next lowest cost word. ** stem list is the next lowest cost word.
*/ */
while( (pStem = pCur->pStem)!=0 ){ while( (pStem = pCur->pStem)!=0 ){
if( fuzzerAdvance(pCur, pStem) ){ int res = fuzzerAdvance(pCur, pStem);
if( res<0 ){
return SQLITE_NOMEM;
}else if( res>0 ){
pCur->pStem = 0; pCur->pStem = 0;
pStem = fuzzerInsert(pCur, pStem); pStem = fuzzerInsert(pCur, pStem);
if( (rc = fuzzerSeen(pCur, pStem))!=0 ){ if( (rc = fuzzerSeen(pCur, pStem))!=0 ){
@ -665,30 +968,44 @@ static int fuzzerFilter(
int argc, sqlite3_value **argv int argc, sqlite3_value **argv
){ ){
fuzzer_cursor *pCur = (fuzzer_cursor *)pVtabCursor; fuzzer_cursor *pCur = (fuzzer_cursor *)pVtabCursor;
const char *zWord = 0; const char *zWord = "";
fuzzer_stem *pStem; fuzzer_stem *pStem;
int idx;
fuzzerClearCursor(pCur, 1); fuzzerClearCursor(pCur, 1);
pCur->rLimit = 2147483647; pCur->rLimit = 2147483647;
if( idxNum==1 ){ idx = 0;
if( idxNum & 1 ){
zWord = (const char*)sqlite3_value_text(argv[0]); zWord = (const char*)sqlite3_value_text(argv[0]);
}else if( idxNum==2 ){ idx++;
pCur->rLimit = (fuzzer_cost)sqlite3_value_int(argv[0]); }
}else if( idxNum==3 ){ if( idxNum & 2 ){
zWord = (const char*)sqlite3_value_text(argv[0]); pCur->rLimit = (fuzzer_cost)sqlite3_value_int(argv[idx]);
pCur->rLimit = (fuzzer_cost)sqlite3_value_int(argv[1]); idx++;
}
if( idxNum & 4 ){
pCur->iRuleset = (fuzzer_cost)sqlite3_value_int(argv[idx]);
idx++;
} }
if( zWord==0 ) zWord = "";
pCur->pStem = pStem = fuzzerNewStem(pCur, zWord, (fuzzer_cost)0);
if( pStem==0 ) return SQLITE_NOMEM;
pCur->nullRule.pNext = pCur->pVtab->pRule; pCur->nullRule.pNext = pCur->pVtab->pRule;
pCur->nullRule.rCost = 0; pCur->nullRule.rCost = 0;
pCur->nullRule.nFrom = 0; pCur->nullRule.nFrom = 0;
pCur->nullRule.nTo = 0; pCur->nullRule.nTo = 0;
pCur->nullRule.zFrom = ""; pCur->nullRule.zFrom = "";
pStem->pRule = &pCur->nullRule;
pStem->n = pStem->nBasis;
pCur->iRowid = 1; pCur->iRowid = 1;
assert( pCur->pStem==0 );
/* If the query term is longer than FUZZER_MX_OUTPUT_LENGTH bytes, this
** query will return zero rows. */
if( (int)strlen(zWord)<FUZZER_MX_OUTPUT_LENGTH ){
pCur->pStem = pStem = fuzzerNewStem(pCur, zWord, (fuzzer_cost)0);
if( pStem==0 ) return SQLITE_NOMEM;
pStem->pRule = &pCur->nullRule;
pStem->n = pStem->nBasis;
}else{
pCur->rLimit = 0;
}
return SQLITE_OK; return SQLITE_OK;
} }
@ -735,22 +1052,29 @@ static int fuzzerEof(sqlite3_vtab_cursor *cur){
/* /*
** Search for terms of these forms: ** Search for terms of these forms:
** **
** word MATCH $str ** (A) word MATCH $str
** distance < $value ** (B1) distance < $value
** distance <= $value ** (B2) distance <= $value
** (C) ruleid == $ruleid
** **
** The distance< and distance<= are both treated as distance<=. ** The distance< and distance<= are both treated as distance<=.
** The query plan number is as follows: ** The query plan number is a bit vector:
** **
** 0: None of the terms above are found ** bit 1: Term of the form (A) found
** 1: There is a "word MATCH" term with $str in filter.argv[0]. ** bit 2: Term like (B1) or (B2) found
** 2: There is a "distance<" term with $value in filter.argv[0]. ** bit 3: Term like (C) found
** 3: Both "word MATCH" and "distance<" with $str in argv[0] and **
** $value in argv[1]. ** If bit-1 is set, $str is always in filter.argv[0]. If bit-2 is set
** then $value is in filter.argv[0] if bit-1 is clear and is in
** filter.argv[1] if bit-1 is set. If bit-3 is set, then $ruleid is
** in filter.argv[0] if bit-1 and bit-2 are both zero, is in
** filter.argv[1] if exactly one of bit-1 and bit-2 are set, and is in
** filter.argv[2] if both bit-1 and bit-2 are set.
*/ */
static int fuzzerBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){ static int fuzzerBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
int iPlan = 0; int iPlan = 0;
int iDistTerm = -1; int iDistTerm = -1;
int iRulesetTerm = -1;
int i; int i;
const struct sqlite3_index_constraint *pConstraint; const struct sqlite3_index_constraint *pConstraint;
pConstraint = pIdxInfo->aConstraint; pConstraint = pIdxInfo->aConstraint;
@ -772,11 +1096,23 @@ static int fuzzerBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
iPlan |= 2; iPlan |= 2;
iDistTerm = i; iDistTerm = i;
} }
if( (iPlan & 4)==0
&& pConstraint->iColumn==2
&& pConstraint->op==SQLITE_INDEX_CONSTRAINT_EQ
){
iPlan |= 4;
pIdxInfo->aConstraintUsage[i].omit = 1;
iRulesetTerm = i;
}
} }
if( iPlan==2 ){ if( iPlan & 2 ){
pIdxInfo->aConstraintUsage[iDistTerm].argvIndex = 1; pIdxInfo->aConstraintUsage[iDistTerm].argvIndex = 1+((iPlan&1)!=0);
}else if( iPlan==3 ){ }
pIdxInfo->aConstraintUsage[iDistTerm].argvIndex = 2; if( iPlan & 4 ){
int idx = 1;
if( iPlan & 1 ) idx++;
if( iPlan & 2 ) idx++;
pIdxInfo->aConstraintUsage[iRulesetTerm].argvIndex = idx;
} }
pIdxInfo->idxNum = iPlan; pIdxInfo->idxNum = iPlan;
if( pIdxInfo->nOrderBy==1 if( pIdxInfo->nOrderBy==1
@ -791,72 +1127,7 @@ static int fuzzerBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
} }
/* /*
** Disallow all attempts to DELETE or UPDATE. Only INSERTs are allowed. ** A virtual table module that implements the "fuzzer".
**
** On an insert, the cFrom, cTo, and cost columns are used to construct
** a new rule. All other columns are ignored. The rule is ignored
** if cFrom and cTo are identical. A NULL value for cFrom or cTo is
** interpreted as an empty string. The cost must be positive.
*/
static int fuzzerUpdate(
sqlite3_vtab *pVTab,
int argc,
sqlite3_value **argv,
sqlite_int64 *pRowid
){
fuzzer_vtab *p = (fuzzer_vtab*)pVTab;
fuzzer_rule *pRule;
const char *zFrom;
int nFrom;
const char *zTo;
int nTo;
fuzzer_cost rCost;
if( argc!=7 ){
sqlite3_free(pVTab->zErrMsg);
pVTab->zErrMsg = sqlite3_mprintf("cannot delete from a %s virtual table",
p->zClassName);
return SQLITE_CONSTRAINT;
}
if( sqlite3_value_type(argv[0])!=SQLITE_NULL ){
sqlite3_free(pVTab->zErrMsg);
pVTab->zErrMsg = sqlite3_mprintf("cannot update a %s virtual table",
p->zClassName);
return SQLITE_CONSTRAINT;
}
zFrom = (char*)sqlite3_value_text(argv[4]);
if( zFrom==0 ) zFrom = "";
zTo = (char*)sqlite3_value_text(argv[5]);
if( zTo==0 ) zTo = "";
if( strcmp(zFrom,zTo)==0 ){
/* Silently ignore null transformations */
return SQLITE_OK;
}
rCost = sqlite3_value_int(argv[6]);
if( rCost<=0 ){
sqlite3_free(pVTab->zErrMsg);
pVTab->zErrMsg = sqlite3_mprintf("cost must be positive");
return SQLITE_CONSTRAINT;
}
nFrom = strlen(zFrom);
nTo = strlen(zTo);
pRule = sqlite3_malloc( sizeof(*pRule) + nFrom + nTo );
if( pRule==0 ){
return SQLITE_NOMEM;
}
pRule->zFrom = &pRule->zTo[nTo+1];
pRule->nFrom = nFrom;
memcpy(pRule->zFrom, zFrom, nFrom+1);
memcpy(pRule->zTo, zTo, nTo+1);
pRule->nTo = nTo;
pRule->rCost = rCost;
pRule->pNext = p->pNewRule;
p->pNewRule = pRule;
return SQLITE_OK;
}
/*
** A virtual table module that provides read-only access to a
** Tcl global variable namespace.
*/ */
static sqlite3_module fuzzerModule = { static sqlite3_module fuzzerModule = {
0, /* iVersion */ 0, /* iVersion */
@ -872,7 +1143,7 @@ static sqlite3_module fuzzerModule = {
fuzzerEof, /* xEof - check for end of scan */ fuzzerEof, /* xEof - check for end of scan */
fuzzerColumn, /* xColumn - read data */ fuzzerColumn, /* xColumn - read data */
fuzzerRowid, /* xRowid - read data */ fuzzerRowid, /* xRowid - read data */
fuzzerUpdate, /* xUpdate - INSERT */ 0, /* xUpdate */
0, /* xBegin */ 0, /* xBegin */
0, /* xSync */ 0, /* xSync */
0, /* xCommit */ 0, /* xCommit */
@ -916,7 +1187,7 @@ static int register_fuzzer_module(
Tcl_WrongNumArgs(interp, 1, objv, "DB"); Tcl_WrongNumArgs(interp, 1, objv, "DB");
return TCL_ERROR; return TCL_ERROR;
} }
if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR; getDbPointer(interp, Tcl_GetString(objv[1]), &db);
fuzzer_register(db); fuzzer_register(db);
return TCL_OK; return TCL_OK;
} }

View File

@ -126,7 +126,7 @@ static int hexio_read(
return TCL_ERROR; return TCL_ERROR;
} }
fseek(in, offset, SEEK_SET); fseek(in, offset, SEEK_SET);
got = fread(zBuf, 1, amt, in); got = (int)fread(zBuf, 1, amt, in);
fclose(in); fclose(in);
if( got<0 ){ if( got<0 ){
got = 0; got = 0;
@ -178,7 +178,7 @@ static int hexio_write(
return TCL_ERROR; return TCL_ERROR;
} }
fseek(out, offset, SEEK_SET); fseek(out, offset, SEEK_SET);
written = fwrite(aOut, 1, nOut, out); written = (int)fwrite(aOut, 1, nOut, out);
sqlite3_free(aOut); sqlite3_free(aOut);
fclose(out); fclose(out);
Tcl_SetObjResult(interp, Tcl_NewIntObj(written)); Tcl_SetObjResult(interp, Tcl_NewIntObj(written));

View File

@ -290,9 +290,9 @@ static jt_file *locateDatabaseHandle(const char *zJournal){
jt_file *pMain = 0; jt_file *pMain = 0;
enterJtMutex(); enterJtMutex();
for(pMain=g.pList; pMain; pMain=pMain->pNext){ for(pMain=g.pList; pMain; pMain=pMain->pNext){
int nName = strlen(zJournal) - strlen("-journal"); int nName = (int)(strlen(zJournal) - strlen("-journal"));
if( (pMain->flags&SQLITE_OPEN_MAIN_DB) if( (pMain->flags&SQLITE_OPEN_MAIN_DB)
&& (strlen(pMain->zName)==nName) && ((int)strlen(pMain->zName)==nName)
&& 0==memcmp(pMain->zName, zJournal, nName) && 0==memcmp(pMain->zName, zJournal, nName)
&& (pMain->eLock>=SQLITE_LOCK_RESERVED) && (pMain->eLock>=SQLITE_LOCK_RESERVED)
){ ){
@ -404,7 +404,7 @@ static int openTransaction(jt_file *pMain, jt_file *pJournal){
/* Calculate and store a checksum for each page in the database file. */ /* Calculate and store a checksum for each page in the database file. */
if( rc==SQLITE_OK ){ if( rc==SQLITE_OK ){
int ii; int ii;
for(ii=0; rc==SQLITE_OK && ii<pMain->nPage; ii++){ for(ii=0; rc==SQLITE_OK && ii<(int)pMain->nPage; ii++){
i64 iOff = (i64)(pMain->nPagesize) * (i64)ii; i64 iOff = (i64)(pMain->nPagesize) * (i64)ii;
if( iOff==PENDING_BYTE ) continue; if( iOff==PENDING_BYTE ) continue;
rc = sqlite3OsRead(pMain->pReal, aData, pMain->nPagesize, iOff); rc = sqlite3OsRead(pMain->pReal, aData, pMain->nPagesize, iOff);
@ -466,7 +466,7 @@ static int readJournalFile(jt_file *p, jt_file *pMain){
continue; continue;
} }
} }
nRec = (iSize-iOff) / (pMain->nPagesize+8); nRec = (u32)((iSize-iOff) / (pMain->nPagesize+8));
} }
/* Read all the records that follow the journal-header just read. */ /* Read all the records that follow the journal-header just read. */
@ -538,7 +538,7 @@ static int jtWrite(
} }
if( p->flags&SQLITE_OPEN_MAIN_DB && p->pWritable ){ if( p->flags&SQLITE_OPEN_MAIN_DB && p->pWritable ){
if( iAmt<p->nPagesize if( iAmt<(int)p->nPagesize
&& p->nPagesize%iAmt==0 && p->nPagesize%iAmt==0
&& iOfst>=(PENDING_BYTE+512) && iOfst>=(PENDING_BYTE+512)
&& iOfst+iAmt<=PENDING_BYTE+p->nPagesize && iOfst+iAmt<=PENDING_BYTE+p->nPagesize
@ -549,7 +549,7 @@ static int jtWrite(
** pending-byte page. ** pending-byte page.
*/ */
}else{ }else{
u32 pgno = iOfst/p->nPagesize + 1; u32 pgno = (u32)(iOfst/p->nPagesize + 1);
assert( (iAmt==1||iAmt==p->nPagesize) && ((iOfst+iAmt)%p->nPagesize)==0 ); assert( (iAmt==1||iAmt==p->nPagesize) && ((iOfst+iAmt)%p->nPagesize)==0 );
assert( pgno<=p->nPage || p->nSync>0 ); assert( pgno<=p->nPage || p->nSync>0 );
assert( pgno>p->nPage || sqlite3BitvecTest(p->pWritable, pgno) ); assert( pgno>p->nPage || sqlite3BitvecTest(p->pWritable, pgno) );
@ -578,7 +578,7 @@ static int jtTruncate(sqlite3_file *pFile, sqlite_int64 size){
if( p->flags&SQLITE_OPEN_MAIN_DB && p->pWritable ){ if( p->flags&SQLITE_OPEN_MAIN_DB && p->pWritable ){
u32 pgno; u32 pgno;
u32 locking_page = (u32)(PENDING_BYTE/p->nPagesize+1); u32 locking_page = (u32)(PENDING_BYTE/p->nPagesize+1);
for(pgno=size/p->nPagesize+1; pgno<=p->nPage; pgno++){ for(pgno=(u32)(size/p->nPagesize+1); pgno<=p->nPage; pgno++){
assert( pgno==locking_page || sqlite3BitvecTest(p->pWritable, pgno) ); assert( pgno==locking_page || sqlite3BitvecTest(p->pWritable, pgno) );
} }
} }
@ -723,7 +723,7 @@ static int jtOpen(
** returning. ** returning.
*/ */
static int jtDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){ static int jtDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){
int nPath = strlen(zPath); int nPath = (int)strlen(zPath);
if( nPath>8 && 0==strcmp("-journal", &zPath[nPath-8]) ){ if( nPath>8 && 0==strcmp("-journal", &zPath[nPath-8]) ){
/* Deleting a journal file. The end of a transaction. */ /* Deleting a journal file. The end of a transaction. */
jt_file *pMain = locateDatabaseHandle(zPath); jt_file *pMain = locateDatabaseHandle(zPath);

View File

@ -713,14 +713,14 @@ static int test_memdebug_settitle(
int objc, int objc,
Tcl_Obj *CONST objv[] Tcl_Obj *CONST objv[]
){ ){
const char *zTitle;
if( objc!=2 ){ if( objc!=2 ){
Tcl_WrongNumArgs(interp, 1, objv, "TITLE"); Tcl_WrongNumArgs(interp, 1, objv, "TITLE");
return TCL_ERROR; return TCL_ERROR;
} }
zTitle = Tcl_GetString(objv[1]);
#ifdef SQLITE_MEMDEBUG #ifdef SQLITE_MEMDEBUG
{ {
const char *zTitle;
zTitle = Tcl_GetString(objv[1]);
extern int sqlite3MemdebugSettitle(const char*); extern int sqlite3MemdebugSettitle(const char*);
sqlite3MemdebugSettitle(zTitle); sqlite3MemdebugSettitle(zTitle);
} }
@ -1033,7 +1033,6 @@ static int test_config_lookaside(
int objc, int objc,
Tcl_Obj *CONST objv[] Tcl_Obj *CONST objv[]
){ ){
int rc;
int sz, cnt; int sz, cnt;
Tcl_Obj *pRet; Tcl_Obj *pRet;
if( objc!=3 ){ if( objc!=3 ){
@ -1049,7 +1048,7 @@ static int test_config_lookaside(
Tcl_ListObjAppendElement( Tcl_ListObjAppendElement(
interp, pRet, Tcl_NewIntObj(sqlite3GlobalConfig.nLookaside) interp, pRet, Tcl_NewIntObj(sqlite3GlobalConfig.nLookaside)
); );
rc = sqlite3_config(SQLITE_CONFIG_LOOKASIDE, sz, cnt); sqlite3_config(SQLITE_CONFIG_LOOKASIDE, sz, cnt);
Tcl_SetObjResult(interp, pRet); Tcl_SetObjResult(interp, pRet);
return TCL_OK; return TCL_OK;
} }
@ -1106,7 +1105,6 @@ static int test_config_heap(
Tcl_Obj *CONST objv[] Tcl_Obj *CONST objv[]
){ ){
static char *zBuf; /* Use this memory */ static char *zBuf; /* Use this memory */
static int szBuf; /* Bytes allocated for zBuf */
int nByte; /* Size of buffer to pass to sqlite3_config() */ int nByte; /* Size of buffer to pass to sqlite3_config() */
int nMinAlloc; /* Size of minimum allocation */ int nMinAlloc; /* Size of minimum allocation */
int rc; /* Return code of sqlite3_config() */ int rc; /* Return code of sqlite3_config() */
@ -1124,11 +1122,9 @@ static int test_config_heap(
if( nByte==0 ){ if( nByte==0 ){
free( zBuf ); free( zBuf );
zBuf = 0; zBuf = 0;
szBuf = 0;
rc = sqlite3_config(SQLITE_CONFIG_HEAP, (void*)0, 0, 0); rc = sqlite3_config(SQLITE_CONFIG_HEAP, (void*)0, 0, 0);
}else{ }else{
zBuf = realloc(zBuf, nByte); zBuf = realloc(zBuf, nByte);
szBuf = nByte;
rc = sqlite3_config(SQLITE_CONFIG_HEAP, zBuf, nByte, nMinAlloc); rc = sqlite3_config(SQLITE_CONFIG_HEAP, zBuf, nByte, nMinAlloc);
} }
@ -1327,7 +1323,8 @@ static int test_db_status(
{ "LOOKASIDE_MISS_SIZE", SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE }, { "LOOKASIDE_MISS_SIZE", SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE },
{ "LOOKASIDE_MISS_FULL", SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL }, { "LOOKASIDE_MISS_FULL", SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL },
{ "CACHE_HIT", SQLITE_DBSTATUS_CACHE_HIT }, { "CACHE_HIT", SQLITE_DBSTATUS_CACHE_HIT },
{ "CACHE_MISS", SQLITE_DBSTATUS_CACHE_MISS } { "CACHE_MISS", SQLITE_DBSTATUS_CACHE_MISS },
{ "CACHE_WRITE", SQLITE_DBSTATUS_CACHE_WRITE }
}; };
Tcl_Obj *pResult; Tcl_Obj *pResult;
if( objc!=4 ){ if( objc!=4 ){

View File

@ -81,8 +81,12 @@
#define sqlite3_mutex_notheld(X) ((void)(X),1) #define sqlite3_mutex_notheld(X) ((void)(X),1)
#endif /* SQLITE_THREADSAFE==0 */ #endif /* SQLITE_THREADSAFE==0 */
/* Maximum chunk number */
#define MX_CHUNK_NUMBER 299
/* First chunk for rollback journal files */ /* First chunk for rollback journal files */
#define SQLITE_MULTIPLEX_JOURNAL_8_3_OFFSET 400 #define SQLITE_MULTIPLEX_JOURNAL_8_3_OFFSET 400
#define SQLITE_MULTIPLEX_WAL_8_3_OFFSET 700
/************************ Shim Definitions ******************************/ /************************ Shim Definitions ******************************/
@ -251,17 +255,22 @@ static void multiplexFilename(
){ ){
int n = nBase; int n = nBase;
memcpy(zOut, zBase, n+1); memcpy(zOut, zBase, n+1);
if( iChunk!=0 && iChunk!=SQLITE_MULTIPLEX_JOURNAL_8_3_OFFSET ){ if( iChunk!=0 && iChunk<=MX_CHUNK_NUMBER ){
#ifdef SQLITE_ENABLE_8_3_NAMES #ifdef SQLITE_ENABLE_8_3_NAMES
int i; int i;
for(i=n-1; i>0 && i>=n-4 && zOut[i]!='.'; i--){} for(i=n-1; i>0 && i>=n-4 && zOut[i]!='.'; i--){}
if( i>=n-4 ) n = i+1; if( i>=n-4 ) n = i+1;
if( flags & SQLITE_OPEN_MAIN_JOURNAL ){ if( flags & SQLITE_OPEN_MAIN_JOURNAL ){
/* The extensions on overflow files for main databases are 001, 002, /* The extensions on overflow files for main databases are 001, 002,
** 003 and so forth. To avoid name collisions, add 400 to the ** 003 and so forth. To avoid name collisions, add 400 to the
** extensions of journal files so that they are 401, 402, 403, .... ** extensions of journal files so that they are 401, 402, 403, ....
*/ */
iChunk += SQLITE_MULTIPLEX_JOURNAL_8_3_OFFSET; iChunk += SQLITE_MULTIPLEX_JOURNAL_8_3_OFFSET;
}else if( flags & SQLITE_OPEN_WAL ){
/* To avoid name collisions, add 700 to the
** extensions of WAL files so that they are 701, 702, 703, ....
*/
iChunk += SQLITE_MULTIPLEX_WAL_8_3_OFFSET;
} }
#endif #endif
sqlite3_snprintf(4,&zOut[n],"%03d",iChunk); sqlite3_snprintf(4,&zOut[n],"%03d",iChunk);
@ -320,6 +329,7 @@ static sqlite3_file *multiplexSubOpen(
** database may therefore not grow to larger than 400 chunks. Attempting ** database may therefore not grow to larger than 400 chunks. Attempting
** to open chunk 401 indicates the database is full. */ ** to open chunk 401 indicates the database is full. */
if( iChunk>=SQLITE_MULTIPLEX_JOURNAL_8_3_OFFSET ){ if( iChunk>=SQLITE_MULTIPLEX_JOURNAL_8_3_OFFSET ){
sqlite3_log(SQLITE_FULL, "multiplexed chunk overflow: %s", pGroup->zName);
*rc = SQLITE_FULL; *rc = SQLITE_FULL;
return 0; return 0;
} }
@ -338,7 +348,13 @@ static sqlite3_file *multiplexSubOpen(
}else{ }else{
*rc = pOrigVfs->xAccess(pOrigVfs, pGroup->aReal[iChunk].z, *rc = pOrigVfs->xAccess(pOrigVfs, pGroup->aReal[iChunk].z,
SQLITE_ACCESS_EXISTS, &bExists); SQLITE_ACCESS_EXISTS, &bExists);
if( *rc || !bExists ) return 0; if( *rc || !bExists ){
if( *rc ){
sqlite3_log(*rc, "multiplexor.xAccess failure on %s",
pGroup->aReal[iChunk].z);
}
return 0;
}
flags &= ~SQLITE_OPEN_CREATE; flags &= ~SQLITE_OPEN_CREATE;
} }
pSubOpen = sqlite3_malloc( pOrigVfs->szOsFile ); pSubOpen = sqlite3_malloc( pOrigVfs->szOsFile );
@ -350,6 +366,8 @@ static sqlite3_file *multiplexSubOpen(
*rc = pOrigVfs->xOpen(pOrigVfs, pGroup->aReal[iChunk].z, pSubOpen, *rc = pOrigVfs->xOpen(pOrigVfs, pGroup->aReal[iChunk].z, pSubOpen,
flags, pOutFlags); flags, pOutFlags);
if( (*rc)!=SQLITE_OK ){ if( (*rc)!=SQLITE_OK ){
sqlite3_log(*rc, "multiplexor.xOpen failure on %s",
pGroup->aReal[iChunk].z);
sqlite3_free(pSubOpen); sqlite3_free(pSubOpen);
pGroup->aReal[iChunk].p = 0; pGroup->aReal[iChunk].p = 0;
return 0; return 0;
@ -520,7 +538,7 @@ static int multiplexOpen(
pGroup->bEnabled = -1; pGroup->bEnabled = -1;
pGroup->bTruncate = sqlite3_uri_boolean(zUri, "truncate", pGroup->bTruncate = sqlite3_uri_boolean(zUri, "truncate",
(flags & SQLITE_OPEN_MAIN_DB)==0); (flags & SQLITE_OPEN_MAIN_DB)==0);
pGroup->szChunk = sqlite3_uri_int64(zUri, "chunksize", pGroup->szChunk = (int)sqlite3_uri_int64(zUri, "chunksize",
SQLITE_MULTIPLEX_CHUNK_SIZE); SQLITE_MULTIPLEX_CHUNK_SIZE);
pGroup->szChunk = (pGroup->szChunk+0xffff)&~0xffff; pGroup->szChunk = (pGroup->szChunk+0xffff)&~0xffff;
if( zName ){ if( zName ){
@ -588,7 +606,7 @@ static int multiplexOpen(
bExists = multiplexSubSize(pGroup, 1, &rc)>0; bExists = multiplexSubSize(pGroup, 1, &rc)>0;
if( rc==SQLITE_OK && bExists && sz==(sz&0xffff0000) && sz>0 if( rc==SQLITE_OK && bExists && sz==(sz&0xffff0000) && sz>0
&& sz!=pGroup->szChunk ){ && sz!=pGroup->szChunk ){
pGroup->szChunk = sz; pGroup->szChunk = (int)sz;
}else if( rc==SQLITE_OK && !bExists && sz>pGroup->szChunk ){ }else if( rc==SQLITE_OK && !bExists && sz>pGroup->szChunk ){
pGroup->bEnabled = 0; pGroup->bEnabled = 0;
} }
@ -632,7 +650,7 @@ static int multiplexDelete(
/* If the main chunk was deleted successfully, also delete any subsequent /* If the main chunk was deleted successfully, also delete any subsequent
** chunks - starting with the last (highest numbered). ** chunks - starting with the last (highest numbered).
*/ */
int nName = strlen(zName); int nName = (int)strlen(zName);
char *z; char *z;
z = sqlite3_malloc(nName + 5); z = sqlite3_malloc(nName + 5);
if( z==0 ){ if( z==0 ){
@ -648,6 +666,17 @@ static int multiplexDelete(
multiplexFilename(zName, nName, SQLITE_OPEN_MAIN_JOURNAL, --iChunk, z); multiplexFilename(zName, nName, SQLITE_OPEN_MAIN_JOURNAL, --iChunk, z);
rc = pOrigVfs->xDelete(pOrigVfs, z, syncDir); rc = pOrigVfs->xDelete(pOrigVfs, z, syncDir);
} }
if( rc==SQLITE_OK ){
iChunk = 0;
do{
multiplexFilename(zName, nName, SQLITE_OPEN_WAL, ++iChunk, z);
rc = pOrigVfs->xAccess(pOrigVfs, z, SQLITE_ACCESS_EXISTS, &bExists);
}while( rc==SQLITE_OK && bExists );
while( rc==SQLITE_OK && iChunk>1 ){
multiplexFilename(zName, nName, SQLITE_OPEN_WAL, --iChunk, z);
rc = pOrigVfs->xDelete(pOrigVfs, z, syncDir);
}
}
} }
sqlite3_free(z); sqlite3_free(z);
} }

View File

@ -288,7 +288,7 @@ static int tmpWrite(
){ ){
tmp_file *pTmp = (tmp_file *)pFile; tmp_file *pTmp = (tmp_file *)pFile;
if( (iAmt+iOfst)>pTmp->nAlloc ){ if( (iAmt+iOfst)>pTmp->nAlloc ){
int nNew = 2*(iAmt+iOfst+pTmp->nAlloc); int nNew = (int)(2*(iAmt+iOfst+pTmp->nAlloc));
char *zNew = sqlite3_realloc(pTmp->zAlloc, nNew); char *zNew = sqlite3_realloc(pTmp->zAlloc, nNew);
if( !zNew ){ if( !zNew ){
return SQLITE_NOMEM; return SQLITE_NOMEM;
@ -297,7 +297,7 @@ static int tmpWrite(
pTmp->nAlloc = nNew; pTmp->nAlloc = nNew;
} }
memcpy(&pTmp->zAlloc[iOfst], zBuf, iAmt); memcpy(&pTmp->zAlloc[iOfst], zBuf, iAmt);
pTmp->nSize = MAX(pTmp->nSize, iOfst+iAmt); pTmp->nSize = (int)MAX(pTmp->nSize, iOfst+iAmt);
return SQLITE_OK; return SQLITE_OK;
} }
@ -306,7 +306,7 @@ static int tmpWrite(
*/ */
static int tmpTruncate(sqlite3_file *pFile, sqlite_int64 size){ static int tmpTruncate(sqlite3_file *pFile, sqlite_int64 size){
tmp_file *pTmp = (tmp_file *)pFile; tmp_file *pTmp = (tmp_file *)pFile;
pTmp->nSize = MIN(pTmp->nSize, size); pTmp->nSize = (int)MIN(pTmp->nSize, size);
return SQLITE_OK; return SQLITE_OK;
} }
@ -418,7 +418,7 @@ static int fsRead(
/* Journal file. */ /* Journal file. */
int iRem = iAmt; int iRem = iAmt;
int iBuf = 0; int iBuf = 0;
int ii = iOfst; int ii = (int)iOfst;
while( iRem>0 && rc==SQLITE_OK ){ while( iRem>0 && rc==SQLITE_OK ){
int iRealOff = pReal->nBlob - BLOCKSIZE*((ii/BLOCKSIZE)+1) + ii%BLOCKSIZE; int iRealOff = pReal->nBlob - BLOCKSIZE*((ii/BLOCKSIZE)+1) + ii%BLOCKSIZE;
int iRealAmt = MIN(iRem, BLOCKSIZE - (iRealOff%BLOCKSIZE)); int iRealAmt = MIN(iRem, BLOCKSIZE - (iRealOff%BLOCKSIZE));
@ -453,14 +453,14 @@ static int fsWrite(
}else{ }else{
rc = pF->pMethods->xWrite(pF, zBuf, iAmt, iOfst+BLOCKSIZE); rc = pF->pMethods->xWrite(pF, zBuf, iAmt, iOfst+BLOCKSIZE);
if( rc==SQLITE_OK ){ if( rc==SQLITE_OK ){
pReal->nDatabase = MAX(pReal->nDatabase, iAmt+iOfst); pReal->nDatabase = (int)MAX(pReal->nDatabase, iAmt+iOfst);
} }
} }
}else{ }else{
/* Journal file. */ /* Journal file. */
int iRem = iAmt; int iRem = iAmt;
int iBuf = 0; int iBuf = 0;
int ii = iOfst; int ii = (int)iOfst;
while( iRem>0 && rc==SQLITE_OK ){ while( iRem>0 && rc==SQLITE_OK ){
int iRealOff = pReal->nBlob - BLOCKSIZE*((ii/BLOCKSIZE)+1) + ii%BLOCKSIZE; int iRealOff = pReal->nBlob - BLOCKSIZE*((ii/BLOCKSIZE)+1) + ii%BLOCKSIZE;
int iRealAmt = MIN(iRem, BLOCKSIZE - (iRealOff%BLOCKSIZE)); int iRealAmt = MIN(iRem, BLOCKSIZE - (iRealOff%BLOCKSIZE));
@ -475,7 +475,7 @@ static int fsWrite(
} }
} }
if( rc==SQLITE_OK ){ if( rc==SQLITE_OK ){
pReal->nJournal = MAX(pReal->nJournal, iAmt+iOfst); pReal->nJournal = (int)MAX(pReal->nJournal, iAmt+iOfst);
} }
} }
@ -489,9 +489,9 @@ static int fsTruncate(sqlite3_file *pFile, sqlite_int64 size){
fs_file *p = (fs_file *)pFile; fs_file *p = (fs_file *)pFile;
fs_real_file *pReal = p->pReal; fs_real_file *pReal = p->pReal;
if( p->eType==DATABASE_FILE ){ if( p->eType==DATABASE_FILE ){
pReal->nDatabase = MIN(pReal->nDatabase, size); pReal->nDatabase = (int)MIN(pReal->nDatabase, size);
}else{ }else{
pReal->nJournal = MIN(pReal->nJournal, size); pReal->nJournal = (int)MIN(pReal->nJournal, size);
} }
return SQLITE_OK; return SQLITE_OK;
} }
@ -606,7 +606,7 @@ static int fsOpen(
p->eType = eType; p->eType = eType;
assert(strlen("-journal")==8); assert(strlen("-journal")==8);
nName = strlen(zName)-((eType==JOURNAL_FILE)?8:0); nName = (int)strlen(zName)-((eType==JOURNAL_FILE)?8:0);
pReal=pFsVfs->pFileList; pReal=pFsVfs->pFileList;
for(; pReal && strncmp(pReal->zName, zName, nName); pReal=pReal->pNext); for(; pReal && strncmp(pReal->zName, zName, nName); pReal=pReal->pNext);
@ -641,7 +641,7 @@ static int fsOpen(
pReal->nBlob = BLOBSIZE; pReal->nBlob = BLOBSIZE;
}else{ }else{
unsigned char zS[4]; unsigned char zS[4];
pReal->nBlob = size; pReal->nBlob = (int)size;
rc = pRealFile->pMethods->xRead(pRealFile, zS, 4, 0); rc = pRealFile->pMethods->xRead(pRealFile, zS, 4, 0);
pReal->nDatabase = (zS[0]<<24)+(zS[1]<<16)+(zS[2]<<8)+zS[3]; pReal->nDatabase = (zS[0]<<24)+(zS[1]<<16)+(zS[2]<<8)+zS[3];
if( rc==SQLITE_OK ){ if( rc==SQLITE_OK ){
@ -687,7 +687,7 @@ static int fsDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){
fs_vfs_t *pFsVfs = (fs_vfs_t *)pVfs; fs_vfs_t *pFsVfs = (fs_vfs_t *)pVfs;
fs_real_file *pReal; fs_real_file *pReal;
sqlite3_file *pF; sqlite3_file *pF;
int nName = strlen(zPath) - 8; int nName = (int)strlen(zPath) - 8;
assert(strlen("-journal")==8); assert(strlen("-journal")==8);
assert(strcmp("-journal", &zPath[nName])==0); assert(strcmp("-journal", &zPath[nName])==0);
@ -717,7 +717,7 @@ static int fsAccess(
fs_vfs_t *pFsVfs = (fs_vfs_t *)pVfs; fs_vfs_t *pFsVfs = (fs_vfs_t *)pVfs;
fs_real_file *pReal; fs_real_file *pReal;
int isJournal = 0; int isJournal = 0;
int nName = strlen(zPath); int nName = (int)strlen(zPath);
if( flags!=SQLITE_ACCESS_EXISTS ){ if( flags!=SQLITE_ACCESS_EXISTS ){
sqlite3_vfs *pParent = ((fs_vfs_t *)pVfs)->pParent; sqlite3_vfs *pParent = ((fs_vfs_t *)pVfs)->pParent;

View File

@ -242,7 +242,7 @@ static sqlite3_uint64 vfslog_time(){
} }
#endif #endif
static void vfslog_call(sqlite3_vfs *, int, int, int, int, int, int); static void vfslog_call(sqlite3_vfs *, int, int, sqlite3_int64, int, int, int);
static void vfslog_string(sqlite3_vfs *, const char *); static void vfslog_string(sqlite3_vfs *, const char *);
/* /*
@ -648,7 +648,7 @@ static void vfslog_call(
sqlite3_vfs *pVfs, sqlite3_vfs *pVfs,
int eEvent, int eEvent,
int iFileid, int iFileid,
int nClick, sqlite3_int64 nClick,
int return_code, int return_code,
int size, int size,
int offset int offset
@ -661,7 +661,7 @@ static void vfslog_call(
zRec = (unsigned char *)&p->aBuf[p->nBuf]; zRec = (unsigned char *)&p->aBuf[p->nBuf];
put32bits(&zRec[0], eEvent); put32bits(&zRec[0], eEvent);
put32bits(&zRec[4], iFileid); put32bits(&zRec[4], iFileid);
put32bits(&zRec[8], nClick); put32bits(&zRec[8], (unsigned int)(nClick&0xffff));
put32bits(&zRec[12], return_code); put32bits(&zRec[12], return_code);
put32bits(&zRec[16], size); put32bits(&zRec[16], size);
put32bits(&zRec[20], offset); put32bits(&zRec[20], offset);
@ -671,7 +671,7 @@ static void vfslog_call(
static void vfslog_string(sqlite3_vfs *pVfs, const char *zStr){ static void vfslog_string(sqlite3_vfs *pVfs, const char *zStr){
VfslogVfs *p = (VfslogVfs *)pVfs; VfslogVfs *p = (VfslogVfs *)pVfs;
unsigned char *zRec; unsigned char *zRec;
int nStr = zStr ? strlen(zStr) : 0; int nStr = zStr ? (int)strlen(zStr) : 0;
if( (4+nStr+p->nBuf)>sizeof(p->aBuf) ){ if( (4+nStr+p->nBuf)>sizeof(p->aBuf) ){
vfslog_flush(p); vfslog_flush(p);
} }
@ -720,7 +720,7 @@ int sqlite3_vfslog_new(
return SQLITE_ERROR; return SQLITE_ERROR;
} }
nVfs = strlen(zVfs); nVfs = (int)strlen(zVfs);
nByte = sizeof(VfslogVfs) + pParent->szOsFile + nVfs+1+pParent->mxPathname+1; nByte = sizeof(VfslogVfs) + pParent->szOsFile + nVfs+1+pParent->mxPathname+1;
p = (VfslogVfs *)sqlite3_malloc(nByte); p = (VfslogVfs *)sqlite3_malloc(nByte);
memset(p, 0, nByte); memset(p, 0, nByte);
@ -1043,7 +1043,7 @@ static int vlogColumn(
} }
case 1: { case 1: {
char *zStr = pCsr->zTransient; char *zStr = pCsr->zTransient;
if( val!=0 && val<pCsr->nFile ){ if( val!=0 && val<(unsigned)pCsr->nFile ){
zStr = pCsr->azFile[val]; zStr = pCsr->azFile[val];
} }
sqlite3_result_text(ctx, zStr, -1, SQLITE_TRANSIENT); sqlite3_result_text(ctx, zStr, -1, SQLITE_TRANSIENT);

View File

@ -120,6 +120,9 @@ struct quota_FILE {
FILE *f; /* Open stdio file pointer */ FILE *f; /* Open stdio file pointer */
sqlite3_int64 iOfst; /* Current offset into the file */ sqlite3_int64 iOfst; /* Current offset into the file */
quotaFile *pFile; /* The file record in the quota system */ quotaFile *pFile; /* The file record in the quota system */
#if SQLITE_OS_WIN
char *zMbcsName; /* Full MBCS pathname of the file */
#endif
}; };
@ -341,7 +344,7 @@ static quotaFile *quotaFindFile(
pFile = pFile->pNext; pFile = pFile->pNext;
} }
if( pFile==0 && createFlag ){ if( pFile==0 && createFlag ){
int nName = strlen(zName); int nName = (int)(strlen(zName) & 0x3fffffff);
pFile = (quotaFile *)sqlite3_malloc( sizeof(*pFile) + nName + 1 ); pFile = (quotaFile *)sqlite3_malloc( sizeof(*pFile) + nName + 1 );
if( pFile ){ if( pFile ){
memset(pFile, 0, sizeof(*pFile)); memset(pFile, 0, sizeof(*pFile));
@ -419,7 +422,7 @@ static quotaFile *quotaFindFile(
*/ */
static char *quota_utf8_to_mbcs(const char *zUtf8){ static char *quota_utf8_to_mbcs(const char *zUtf8){
#if SQLITE_OS_WIN #if SQLITE_OS_WIN
int n; /* Bytes in zUtf8 */ size_t n; /* Bytes in zUtf8 */
int nWide; /* number of UTF-16 characters */ int nWide; /* number of UTF-16 characters */
int nMbcs; /* Bytes of MBCS */ int nMbcs; /* Bytes of MBCS */
LPWSTR zTmpWide; /* The UTF16 text */ LPWSTR zTmpWide; /* The UTF16 text */
@ -897,7 +900,7 @@ int sqlite3_quota_set(
pGroup = pGroup->pNext; pGroup = pGroup->pNext;
} }
if( pGroup==0 ){ if( pGroup==0 ){
int nPattern = strlen(zPattern); int nPattern = (int)(strlen(zPattern) & 0x3fffffff);
if( iLimit<=0 ){ if( iLimit<=0 ){
quotaLeave(); quotaLeave();
return SQLITE_OK; return SQLITE_OK;
@ -979,7 +982,7 @@ int sqlite3_quota_file(const char *zFilename){
quota_FILE *sqlite3_quota_fopen(const char *zFilename, const char *zMode){ quota_FILE *sqlite3_quota_fopen(const char *zFilename, const char *zMode){
quota_FILE *p = 0; quota_FILE *p = 0;
char *zFull = 0; char *zFull = 0;
char *zFullTranslated; char *zFullTranslated = 0;
int rc; int rc;
quotaGroup *pGroup; quotaGroup *pGroup;
quotaFile *pFile; quotaFile *pFile;
@ -995,7 +998,6 @@ quota_FILE *sqlite3_quota_fopen(const char *zFilename, const char *zMode){
zFullTranslated = quota_utf8_to_mbcs(zFull); zFullTranslated = quota_utf8_to_mbcs(zFull);
if( zFullTranslated==0 ) goto quota_fopen_error; if( zFullTranslated==0 ) goto quota_fopen_error;
p->f = fopen(zFullTranslated, zMode); p->f = fopen(zFullTranslated, zMode);
quota_mbcs_free(zFullTranslated);
if( p->f==0 ) goto quota_fopen_error; if( p->f==0 ) goto quota_fopen_error;
quotaEnter(); quotaEnter();
pGroup = quotaGroupFind(zFull); pGroup = quotaGroupFind(zFull);
@ -1010,9 +1012,13 @@ quota_FILE *sqlite3_quota_fopen(const char *zFilename, const char *zMode){
} }
quotaLeave(); quotaLeave();
sqlite3_free(zFull); sqlite3_free(zFull);
#if SQLITE_OS_WIN
p->zMbcsName = zFullTranslated;
#endif
return p; return p;
quota_fopen_error: quota_fopen_error:
quota_mbcs_free(zFullTranslated);
sqlite3_free(zFull); sqlite3_free(zFull);
if( p && p->f ) fclose(p->f); if( p && p->f ) fclose(p->f);
sqlite3_free(p); sqlite3_free(p);
@ -1045,6 +1051,7 @@ size_t sqlite3_quota_fwrite(
sqlite3_int64 iEnd; sqlite3_int64 iEnd;
sqlite3_int64 szNew; sqlite3_int64 szNew;
quotaFile *pFile; quotaFile *pFile;
size_t rc;
iOfst = ftell(p->f); iOfst = ftell(p->f);
iEnd = iOfst + size*nmemb; iEnd = iOfst + size*nmemb;
@ -1060,7 +1067,7 @@ size_t sqlite3_quota_fwrite(
} }
if( szNew>pGroup->iLimit && pGroup->iLimit>0 ){ if( szNew>pGroup->iLimit && pGroup->iLimit>0 ){
iEnd = pGroup->iLimit - pGroup->iSize + pFile->iSize; iEnd = pGroup->iLimit - pGroup->iSize + pFile->iSize;
nmemb = (iEnd - iOfst)/size; nmemb = (size_t)((iEnd - iOfst)/size);
iEnd = iOfst + size*nmemb; iEnd = iOfst + size*nmemb;
szNew = pGroup->iSize - pFile->iSize + iEnd; szNew = pGroup->iSize - pFile->iSize + iEnd;
} }
@ -1068,8 +1075,23 @@ size_t sqlite3_quota_fwrite(
pGroup->iSize = szNew; pGroup->iSize = szNew;
pFile->iSize = iEnd; pFile->iSize = iEnd;
quotaLeave(); quotaLeave();
}else{
pFile = 0;
} }
return fwrite(pBuf, size, nmemb, p->f); rc = fwrite(pBuf, size, nmemb, p->f);
/* If the write was incomplete, adjust the file size and group size
** downward */
if( rc<nmemb && pFile ){
size_t nWritten = rc>=0 ? rc : 0;
sqlite3_int64 iNewEnd = iOfst + size*nWritten;
if( iNewEnd<iEnd ) iNewEnd = iEnd;
quotaEnter();
pFile->pGroup->iSize += iNewEnd - pFile->iSize;
pFile->iSize = iNewEnd;
quotaLeave();
}
return rc;
} }
/* /*
@ -1093,6 +1115,9 @@ int sqlite3_quota_fclose(quota_FILE *p){
} }
quotaLeave(); quotaLeave();
} }
#if SQLITE_OS_WIN
quota_mbcs_free(p->zMbcsName);
#endif
sqlite3_free(p); sqlite3_free(p);
return rc; return rc;
} }
@ -1135,12 +1160,89 @@ long sqlite3_quota_ftell(quota_FILE *p){
return ftell(p->f); return ftell(p->f);
} }
/*
** Truncate a file to szNew bytes.
*/
int sqlite3_quota_ftruncate(quota_FILE *p, sqlite3_int64 szNew){
quotaFile *pFile = p->pFile;
int rc;
if( (pFile = p->pFile)!=0 && pFile->iSize<szNew ){
quotaGroup *pGroup;
if( pFile->iSize<szNew ){
/* This routine cannot be used to extend a file that is under
** quota management. Only true truncation is allowed. */
return -1;
}
pGroup = pFile->pGroup;
quotaEnter();
pGroup->iSize += szNew - pFile->iSize;
quotaLeave();
}
#if SQLITE_OS_UNIX
rc = ftruncate(fileno(p->f), szNew);
#endif
#if SQLITE_OS_WIN
rc = _chsize_s(_fileno(p->f), szNew);
#endif
if( pFile && rc==0 ){
quotaGroup *pGroup = pFile->pGroup;
quotaEnter();
pGroup->iSize += szNew - pFile->iSize;
pFile->iSize = szNew;
quotaLeave();
}
return rc;
}
/*
** Determine the time that the given file was last modified, in
** seconds size 1970. Write the result into *pTime. Return 0 on
** success and non-zero on any kind of error.
*/
int sqlite3_quota_file_mtime(quota_FILE *p, time_t *pTime){
int rc;
#if SQLITE_OS_UNIX
struct stat buf;
rc = fstat(fileno(p->f), &buf);
#endif
#if SQLITE_OS_WIN
struct _stati64 buf;
rc = _stati64(p->zMbcsName, &buf);
#endif
if( rc==0 ) *pTime = buf.st_mtime;
return rc;
}
/*
** Return the true size of the file, as reported by the operating
** system.
*/
sqlite3_int64 sqlite3_quota_file_truesize(quota_FILE *p){
int rc;
#if SQLITE_OS_UNIX
struct stat buf;
rc = fstat(fileno(p->f), &buf);
#endif
#if SQLITE_OS_WIN
struct _stati64 buf;
rc = _stati64(p->zMbcsName, &buf);
#endif
return rc==0 ? buf.st_size : -1;
}
/*
** Return the size of the file, as it is known to the quota subsystem.
*/
sqlite3_int64 sqlite3_quota_file_size(quota_FILE *p){
return p->pFile ? p->pFile->iSize : -1;
}
/* /*
** Remove a managed file. Update quotas accordingly. ** Remove a managed file. Update quotas accordingly.
*/ */
int sqlite3_quota_remove(const char *zFilename){ int sqlite3_quota_remove(const char *zFilename){
char *zFull; /* Full pathname for zFilename */ char *zFull; /* Full pathname for zFilename */
int nFull; /* Number of bytes in zFilename */ size_t nFull; /* Number of bytes in zFilename */
int rc; /* Result code */ int rc; /* Result code */
quotaGroup *pGroup; /* Group containing zFilename */ quotaGroup *pGroup; /* Group containing zFilename */
quotaFile *pFile; /* A file in the group */ quotaFile *pFile; /* A file in the group */
@ -1480,7 +1582,7 @@ static int test_quota_fread(
char *zBuf; char *zBuf;
int sz; int sz;
int nElem; int nElem;
int got; size_t got;
if( objc!=4 ){ if( objc!=4 ){
Tcl_WrongNumArgs(interp, 1, objv, "HANDLE SIZE NELEM"); Tcl_WrongNumArgs(interp, 1, objv, "HANDLE SIZE NELEM");
@ -1515,7 +1617,7 @@ static int test_quota_fwrite(
char *zBuf; char *zBuf;
int sz; int sz;
int nElem; int nElem;
int got; size_t got;
if( objc!=5 ){ if( objc!=5 ){
Tcl_WrongNumArgs(interp, 1, objv, "HANDLE SIZE NELEM CONTENT"); Tcl_WrongNumArgs(interp, 1, objv, "HANDLE SIZE NELEM CONTENT");
@ -1526,7 +1628,7 @@ static int test_quota_fwrite(
if( Tcl_GetIntFromObj(interp, objv[3], &nElem) ) return TCL_ERROR; if( Tcl_GetIntFromObj(interp, objv[3], &nElem) ) return TCL_ERROR;
zBuf = Tcl_GetString(objv[4]); zBuf = Tcl_GetString(objv[4]);
got = sqlite3_quota_fwrite(zBuf, sz, nElem, p); got = sqlite3_quota_fwrite(zBuf, sz, nElem, p);
Tcl_SetObjResult(interp, Tcl_NewIntObj(got)); Tcl_SetObjResult(interp, Tcl_NewWideIntObj(got));
return TCL_OK; return TCL_OK;
} }
@ -1656,6 +1758,96 @@ static int test_quota_ftell(
return TCL_OK; return TCL_OK;
} }
/*
** tclcmd: sqlite3_quota_ftruncate HANDLE SIZE
*/
static int test_quota_ftruncate(
void * clientData,
Tcl_Interp *interp,
int objc,
Tcl_Obj *CONST objv[]
){
quota_FILE *p;
sqlite3_int64 x;
Tcl_WideInt w;
int rc;
if( objc!=3 ){
Tcl_WrongNumArgs(interp, 1, objv, "HANDLE SIZE");
return TCL_ERROR;
}
p = sqlite3TestTextToPtr(Tcl_GetString(objv[1]));
if( Tcl_GetWideIntFromObj(interp, objv[2], &w) ) return TCL_ERROR;
x = (sqlite3_int64)w;
rc = sqlite3_quota_ftruncate(p, x);
Tcl_SetObjResult(interp, Tcl_NewIntObj(rc));
return TCL_OK;
}
/*
** tclcmd: sqlite3_quota_file_size HANDLE
*/
static int test_quota_file_size(
void * clientData,
Tcl_Interp *interp,
int objc,
Tcl_Obj *CONST objv[]
){
quota_FILE *p;
sqlite3_int64 x;
if( objc!=2 ){
Tcl_WrongNumArgs(interp, 1, objv, "HANDLE");
return TCL_ERROR;
}
p = sqlite3TestTextToPtr(Tcl_GetString(objv[1]));
x = sqlite3_quota_file_size(p);
Tcl_SetObjResult(interp, Tcl_NewWideIntObj(x));
return TCL_OK;
}
/*
** tclcmd: sqlite3_quota_file_truesize HANDLE
*/
static int test_quota_file_truesize(
void * clientData,
Tcl_Interp *interp,
int objc,
Tcl_Obj *CONST objv[]
){
quota_FILE *p;
sqlite3_int64 x;
if( objc!=2 ){
Tcl_WrongNumArgs(interp, 1, objv, "HANDLE");
return TCL_ERROR;
}
p = sqlite3TestTextToPtr(Tcl_GetString(objv[1]));
x = sqlite3_quota_file_truesize(p);
Tcl_SetObjResult(interp, Tcl_NewWideIntObj(x));
return TCL_OK;
}
/*
** tclcmd: sqlite3_quota_file_mtime HANDLE
*/
static int test_quota_file_mtime(
void * clientData,
Tcl_Interp *interp,
int objc,
Tcl_Obj *CONST objv[]
){
quota_FILE *p;
time_t t;
if( objc!=2 ){
Tcl_WrongNumArgs(interp, 1, objv, "HANDLE");
return TCL_ERROR;
}
p = sqlite3TestTextToPtr(Tcl_GetString(objv[1]));
t = 0;
sqlite3_quota_file_mtime(p, &t);
Tcl_SetObjResult(interp, Tcl_NewWideIntObj(t));
return TCL_OK;
}
/* /*
** tclcmd: sqlite3_quota_remove FILENAME ** tclcmd: sqlite3_quota_remove FILENAME
*/ */
@ -1713,21 +1905,25 @@ int Sqlitequota_Init(Tcl_Interp *interp){
char *zName; char *zName;
Tcl_ObjCmdProc *xProc; Tcl_ObjCmdProc *xProc;
} aCmd[] = { } aCmd[] = {
{ "sqlite3_quota_initialize", test_quota_initialize }, { "sqlite3_quota_initialize", test_quota_initialize },
{ "sqlite3_quota_shutdown", test_quota_shutdown }, { "sqlite3_quota_shutdown", test_quota_shutdown },
{ "sqlite3_quota_set", test_quota_set }, { "sqlite3_quota_set", test_quota_set },
{ "sqlite3_quota_file", test_quota_file }, { "sqlite3_quota_file", test_quota_file },
{ "sqlite3_quota_dump", test_quota_dump }, { "sqlite3_quota_dump", test_quota_dump },
{ "sqlite3_quota_fopen", test_quota_fopen }, { "sqlite3_quota_fopen", test_quota_fopen },
{ "sqlite3_quota_fread", test_quota_fread }, { "sqlite3_quota_fread", test_quota_fread },
{ "sqlite3_quota_fwrite", test_quota_fwrite }, { "sqlite3_quota_fwrite", test_quota_fwrite },
{ "sqlite3_quota_fclose", test_quota_fclose }, { "sqlite3_quota_fclose", test_quota_fclose },
{ "sqlite3_quota_fflush", test_quota_fflush }, { "sqlite3_quota_fflush", test_quota_fflush },
{ "sqlite3_quota_fseek", test_quota_fseek }, { "sqlite3_quota_fseek", test_quota_fseek },
{ "sqlite3_quota_rewind", test_quota_rewind }, { "sqlite3_quota_rewind", test_quota_rewind },
{ "sqlite3_quota_ftell", test_quota_ftell }, { "sqlite3_quota_ftell", test_quota_ftell },
{ "sqlite3_quota_remove", test_quota_remove }, { "sqlite3_quota_ftruncate", test_quota_ftruncate },
{ "sqlite3_quota_glob", test_quota_glob }, { "sqlite3_quota_file_size", test_quota_file_size },
{ "sqlite3_quota_file_truesize", test_quota_file_truesize },
{ "sqlite3_quota_file_mtime", test_quota_file_mtime },
{ "sqlite3_quota_remove", test_quota_remove },
{ "sqlite3_quota_glob", test_quota_glob },
}; };
int i; int i;

View File

@ -29,6 +29,14 @@
#ifndef _QUOTA_H_ #ifndef _QUOTA_H_
#include "sqlite3.h" #include "sqlite3.h"
#include <stdio.h> #include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#if SQLITE_OS_UNIX
# include <unistd.h>
#endif
#if SQLITE_OS_WIN
# include <windows.h>
#endif
/* Make this callable from C++ */ /* Make this callable from C++ */
#ifdef __cplusplus #ifdef __cplusplus
@ -182,6 +190,48 @@ int sqlite3_quota_fseek(quota_FILE*, long, int);
void sqlite3_quota_rewind(quota_FILE*); void sqlite3_quota_rewind(quota_FILE*);
long sqlite3_quota_ftell(quota_FILE*); long sqlite3_quota_ftell(quota_FILE*);
/*
** Truncate a file previously opened by sqlite3_quota_fopen(). Return
** zero on success and non-zero on any kind of failure.
**
** The newSize argument must be less than or equal to the current file size.
** Any attempt to "truncate" a file to a larger size results in
** undefined behavior.
*/
int sqlite3_quota_ftrunate(quota_FILE*, sqlite3_int64 newSize);
/*
** Return the last modification time of the opened file, in seconds
** since 1970.
*/
int sqlite3_quota_file_mtime(quota_FILE*, time_t *pTime);
/*
** Return the size of the file as it is known to the quota system.
**
** This size might be different from the true size of the file on
** disk if some outside process has modified the file without using the
** quota mechanism, or if calls to sqlite3_quota_fwrite() have occurred
** which have increased the file size, but those writes have not yet been
** forced to disk using sqlite3_quota_fflush().
**
** Return -1 if the file is not participating in quota management.
*/
sqlite3_int64 sqlite3_quota_file_size(quota_FILE*);
/*
** Return the true size of the file.
**
** The true size should be the same as the size of the file as known
** to the quota system, however the sizes might be different if the
** file has been extended or truncated via some outside process or if
** pending writes have not yet been flushed to disk.
**
** Return -1 if the file does not exist or if the size of the file
** cannot be determined for some reason.
*/
sqlite3_int64 sqlite3_quota_file_truesize(quota_FILE*);
/* /*
** Delete a file from the disk, if that file is under quota management. ** Delete a file from the disk, if that file is under quota management.
** Adjust quotas accordingly. ** Adjust quotas accordingly.

View File

@ -49,7 +49,11 @@ static void circle_del(void *p){
static int circle_geom( static int circle_geom(
sqlite3_rtree_geometry *p, sqlite3_rtree_geometry *p,
int nCoord, int nCoord,
#ifdef SQLITE_RTREE_INT_ONLY
sqlite3_int64 *aCoord,
#else
double *aCoord, double *aCoord,
#endif
int *pRes int *pRes
){ ){
int i; /* Iterator variable */ int i; /* Iterator variable */
@ -188,8 +192,12 @@ static int gHere = 42;
*/ */
static int cube_geom( static int cube_geom(
sqlite3_rtree_geometry *p, sqlite3_rtree_geometry *p,
int nCoord, int nCoord,
#ifdef SQLITE_RTREE_INT_ONLY
sqlite3_int64 *aCoord,
#else
double *aCoord, double *aCoord,
#endif
int *piRes int *piRes
){ ){
Cube *pCube = (Cube *)p->pUser; Cube *pCube = (Cube *)p->pUser;

1951
src/test_spellfix.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -324,12 +324,13 @@ static int statDecodePage(Btree *pBt, StatPage *p){
u64 dummy; u64 dummy;
iOff += sqlite3GetVarint(&aData[iOff], &dummy); iOff += sqlite3GetVarint(&aData[iOff], &dummy);
} }
if( nPayload>p->nMxPayload ) p->nMxPayload = nPayload; if( nPayload>(u32)p->nMxPayload ) p->nMxPayload = nPayload;
getLocalPayload(nUsable, p->flags, nPayload, &nLocal); getLocalPayload(nUsable, p->flags, nPayload, &nLocal);
pCell->nLocal = nLocal; pCell->nLocal = nLocal;
assert( nPayload>=nLocal ); assert( nLocal>=0 );
assert( nPayload>=(u32)nLocal );
assert( nLocal<=(nUsable-35) ); assert( nLocal<=(nUsable-35) );
if( nPayload>nLocal ){ if( nPayload>(u32)nLocal ){
int j; int j;
int nOvfl = ((nPayload - nLocal) + nUsable-4 - 1) / (nUsable - 4); int nOvfl = ((nPayload - nLocal) + nUsable-4 - 1) / (nUsable - 4);
pCell->nLastOvfl = (nPayload-nLocal) - (nOvfl-1) * (nUsable-4); pCell->nLastOvfl = (nPayload-nLocal) - (nOvfl-1) * (nUsable-4);
@ -378,7 +379,7 @@ static void statSizeAndOffset(StatCursor *pCsr){
x[0] = pCsr->iPageno; x[0] = pCsr->iPageno;
if( sqlite3OsFileControl(fd, 230440, &x)==SQLITE_OK ){ if( sqlite3OsFileControl(fd, 230440, &x)==SQLITE_OK ){
pCsr->iOffset = x[0]; pCsr->iOffset = x[0];
pCsr->szPage = x[1]; pCsr->szPage = (int)x[1];
} }
} }
@ -400,7 +401,7 @@ static int statNext(sqlite3_vtab_cursor *pCursor){
rc = sqlite3_step(pCsr->pStmt); rc = sqlite3_step(pCsr->pStmt);
if( rc==SQLITE_ROW ){ if( rc==SQLITE_ROW ){
int nPage; int nPage;
u32 iRoot = sqlite3_column_int64(pCsr->pStmt, 1); u32 iRoot = (u32)sqlite3_column_int64(pCsr->pStmt, 1);
sqlite3PagerPagecount(pPager, &nPage); sqlite3PagerPagecount(pPager, &nPage);
if( nPage==0 ){ if( nPage==0 ){
pCsr->isEof = 1; pCsr->isEof = 1;

View File

@ -273,7 +273,6 @@ static int sqlthread_open(
const char *zFilename; const char *zFilename;
sqlite3 *db; sqlite3 *db;
int rc;
char zBuf[100]; char zBuf[100];
extern void Md5_Register(sqlite3*); extern void Md5_Register(sqlite3*);
@ -281,11 +280,12 @@ static int sqlthread_open(
UNUSED_PARAMETER(objc); UNUSED_PARAMETER(objc);
zFilename = Tcl_GetString(objv[2]); zFilename = Tcl_GetString(objv[2]);
rc = sqlite3_open(zFilename, &db); sqlite3_open(zFilename, &db);
#ifdef SQLITE_HAS_CODEC #ifdef SQLITE_HAS_CODEC
if( db && objc>=4 ){ if( db && objc>=4 ){
const char *zKey; const char *zKey;
int nKey; int nKey;
int rc;
zKey = Tcl_GetStringFromObj(objv[3], &nKey); zKey = Tcl_GetStringFromObj(objv[3], &nKey);
rc = sqlite3_key(db, zKey, nKey); rc = sqlite3_key(db, zKey, nKey);
if( rc!=SQLITE_OK ){ if( rc!=SQLITE_OK ){

View File

@ -480,6 +480,27 @@ static int tvfsCheckReservedLock(sqlite3_file *pFile, int *pResOut){
*/ */
static int tvfsFileControl(sqlite3_file *pFile, int op, void *pArg){ static int tvfsFileControl(sqlite3_file *pFile, int op, void *pArg){
TestvfsFd *p = tvfsGetFd(pFile); TestvfsFd *p = tvfsGetFd(pFile);
if( op==SQLITE_FCNTL_PRAGMA ){
char **argv = (char**)pArg;
if( sqlite3_stricmp(argv[1],"error")==0 ){
int rc = SQLITE_ERROR;
if( argv[2] ){
const char *z = argv[2];
int x = atoi(z);
if( x ){
rc = x;
while( sqlite3Isdigit(z[0]) ){ z++; }
while( sqlite3Isspace(z[0]) ){ z++; }
}
if( z[0] ) argv[0] = sqlite3_mprintf("%s", z);
}
return rc;
}
if( sqlite3_stricmp(argv[1], "filename")==0 ){
argv[0] = sqlite3_mprintf("%s", p->zFilename);
return SQLITE_OK;
}
}
return sqlite3OsFileControl(p->pReal, op, pArg); return sqlite3OsFileControl(p->pReal, op, pArg);
} }
@ -763,7 +784,7 @@ static int tvfsShmOpen(sqlite3_file *pFile){
if( 0==strcmp(pFd->zFilename, pBuffer->zFile) ) break; if( 0==strcmp(pFd->zFilename, pBuffer->zFile) ) break;
} }
if( !pBuffer ){ if( !pBuffer ){
int nByte = sizeof(TestvfsBuffer) + strlen(pFd->zFilename) + 1; int nByte = sizeof(TestvfsBuffer) + (int)strlen(pFd->zFilename) + 1;
pBuffer = (TestvfsBuffer *)ckalloc(nByte); pBuffer = (TestvfsBuffer *)ckalloc(nByte);
memset(pBuffer, 0, nByte); memset(pBuffer, 0, nByte);
pBuffer->zFile = (char *)&pBuffer[1]; pBuffer->zFile = (char *)&pBuffer[1];
@ -845,13 +866,13 @@ static int tvfsShmLock(
if( p->pScript && p->mask&TESTVFS_SHMLOCK_MASK ){ if( p->pScript && p->mask&TESTVFS_SHMLOCK_MASK ){
sqlite3_snprintf(sizeof(zLock), zLock, "%d %d", ofst, n); sqlite3_snprintf(sizeof(zLock), zLock, "%d %d", ofst, n);
nLock = strlen(zLock); nLock = (int)strlen(zLock);
if( flags & SQLITE_SHM_LOCK ){ if( flags & SQLITE_SHM_LOCK ){
strcpy(&zLock[nLock], " lock"); strcpy(&zLock[nLock], " lock");
}else{ }else{
strcpy(&zLock[nLock], " unlock"); strcpy(&zLock[nLock], " unlock");
} }
nLock += strlen(&zLock[nLock]); nLock += (int)strlen(&zLock[nLock]);
if( flags & SQLITE_SHM_SHARED ){ if( flags & SQLITE_SHM_SHARED ){
strcpy(&zLock[nLock], " shared"); strcpy(&zLock[nLock], " shared");
}else{ }else{
@ -1375,7 +1396,7 @@ static int testvfs_cmd(
} }
zVfs = Tcl_GetString(objv[1]); zVfs = Tcl_GetString(objv[1]);
nByte = sizeof(Testvfs) + strlen(zVfs)+1; nByte = sizeof(Testvfs) + (int)strlen(zVfs)+1;
p = (Testvfs *)ckalloc(nByte); p = (Testvfs *)ckalloc(nByte);
memset(p, 0, nByte); memset(p, 0, nByte);
p->iDevchar = -1; p->iDevchar = -1;

View File

@ -476,6 +476,12 @@ static int vfstraceFileControl(sqlite3_file *pFile, int op, void *pArg){
case SQLITE_FCNTL_OVERWRITE: zOp = "OVERWRITE"; break; case SQLITE_FCNTL_OVERWRITE: zOp = "OVERWRITE"; break;
case SQLITE_FCNTL_VFSNAME: zOp = "VFSNAME"; break; case SQLITE_FCNTL_VFSNAME: zOp = "VFSNAME"; break;
case 0xca093fa0: zOp = "DB_UNCHANGED"; break; case 0xca093fa0: zOp = "DB_UNCHANGED"; break;
case SQLITE_FCNTL_PRAGMA: {
const char *const* a = (const char*const*)pArg;
sqlite3_snprintf(sizeof(zBuf), zBuf, "PRAGMA,[%s,%s]",a[1],a[2]);
zOp = zBuf;
break;
}
default: { default: {
sqlite3_snprintf(sizeof zBuf, zBuf, "%d", op); sqlite3_snprintf(sizeof zBuf, zBuf, "%d", op);
zOp = zBuf; zOp = zBuf;
@ -490,6 +496,10 @@ static int vfstraceFileControl(sqlite3_file *pFile, int op, void *pArg){
*(char**)pArg = sqlite3_mprintf("vfstrace.%s/%z", *(char**)pArg = sqlite3_mprintf("vfstrace.%s/%z",
pInfo->zVfsName, *(char**)pArg); pInfo->zVfsName, *(char**)pArg);
} }
if( op==SQLITE_FCNTL_PRAGMA && rc==SQLITE_OK && *(char**)pArg ){
vfstrace_printf(pInfo, "%s.xFileControl(%s,%s) returns %s",
pInfo->zVfsName, p->zFName, zOp, *(char**)pArg);
}
return rc; return rc;
} }

View File

@ -33,8 +33,8 @@
typedef struct wholenumber_cursor wholenumber_cursor; typedef struct wholenumber_cursor wholenumber_cursor;
struct wholenumber_cursor { struct wholenumber_cursor {
sqlite3_vtab_cursor base; /* Base class - must be first */ sqlite3_vtab_cursor base; /* Base class - must be first */
unsigned iValue; /* Current value */ sqlite3_int64 iValue; /* Current value */
unsigned mxValue; /* Maximum value */ sqlite3_int64 mxValue; /* Maximum value */
}; };
/* Methods for the wholenumber module */ /* Methods for the wholenumber module */

View File

@ -216,13 +216,13 @@ int sqlite3Dequote(char *z){
** Some systems have stricmp(). Others have strcasecmp(). Because ** Some systems have stricmp(). Others have strcasecmp(). Because
** there is no consistency, we will define our own. ** there is no consistency, we will define our own.
** **
** IMPLEMENTATION-OF: R-20522-24639 The sqlite3_strnicmp() API allows ** IMPLEMENTATION-OF: R-30243-02494 The sqlite3_stricmp() and
** applications and extensions to compare the contents of two buffers ** sqlite3_strnicmp() APIs allow applications and extensions to compare
** containing UTF-8 strings in a case-independent fashion, using the same ** the contents of two buffers containing UTF-8 strings in a
** definition of case independence that SQLite uses internally when ** case-independent fashion, using the same definition of "case
** comparing identifiers. ** independence" that SQLite uses internally when comparing identifiers.
*/ */
int sqlite3StrICmp(const char *zLeft, const char *zRight){ int sqlite3_stricmp(const char *zLeft, const char *zRight){
register unsigned char *a, *b; register unsigned char *a, *b;
a = (unsigned char *)zLeft; a = (unsigned char *)zLeft;
b = (unsigned char *)zRight; b = (unsigned char *)zRight;

View File

@ -176,6 +176,18 @@ int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db){
} }
#endif #endif
rc = execSql(db, pzErrMsg, "PRAGMA vacuum_db.synchronous=OFF");
if( rc!=SQLITE_OK ) goto end_of_vacuum;
/* Begin a transaction and take an exclusive lock on the main database
** file. This is done before the sqlite3BtreeGetPageSize(pMain) call below,
** to ensure that we do not try to change the page-size on a WAL database.
*/
rc = execSql(db, pzErrMsg, "BEGIN;");
if( rc!=SQLITE_OK ) goto end_of_vacuum;
rc = sqlite3BtreeBeginTrans(pMain, 2);
if( rc!=SQLITE_OK ) goto end_of_vacuum;
/* Do not attempt to change the page size for a WAL database */ /* Do not attempt to change the page size for a WAL database */
if( sqlite3PagerGetJournalMode(sqlite3BtreePager(pMain)) if( sqlite3PagerGetJournalMode(sqlite3BtreePager(pMain))
==PAGER_JOURNALMODE_WAL ){ ==PAGER_JOURNALMODE_WAL ){
@ -189,20 +201,12 @@ int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db){
rc = SQLITE_NOMEM; rc = SQLITE_NOMEM;
goto end_of_vacuum; goto end_of_vacuum;
} }
rc = execSql(db, pzErrMsg, "PRAGMA vacuum_db.synchronous=OFF");
if( rc!=SQLITE_OK ){
goto end_of_vacuum;
}
#ifndef SQLITE_OMIT_AUTOVACUUM #ifndef SQLITE_OMIT_AUTOVACUUM
sqlite3BtreeSetAutoVacuum(pTemp, db->nextAutovac>=0 ? db->nextAutovac : sqlite3BtreeSetAutoVacuum(pTemp, db->nextAutovac>=0 ? db->nextAutovac :
sqlite3BtreeGetAutoVacuum(pMain)); sqlite3BtreeGetAutoVacuum(pMain));
#endif #endif
/* Begin a transaction */
rc = execSql(db, pzErrMsg, "BEGIN EXCLUSIVE;");
if( rc!=SQLITE_OK ) goto end_of_vacuum;
/* Query the schema of the main database. Create a mirror schema /* Query the schema of the main database. Create a mirror schema
** in the temporary database. ** in the temporary database.
*/ */

View File

@ -1331,19 +1331,26 @@ arithmetic_result_is_null:
break; break;
} }
/* Opcode: CollSeq * * P4 /* Opcode: CollSeq P1 * * P4
** **
** P4 is a pointer to a CollSeq struct. If the next call to a user function ** P4 is a pointer to a CollSeq struct. If the next call to a user function
** or aggregate calls sqlite3GetFuncCollSeq(), this collation sequence will ** or aggregate calls sqlite3GetFuncCollSeq(), this collation sequence will
** be returned. This is used by the built-in min(), max() and nullif() ** be returned. This is used by the built-in min(), max() and nullif()
** functions. ** functions.
** **
** If P1 is not zero, then it is a register that a subsequent min() or
** max() aggregate will set to 1 if the current row is not the minimum or
** maximum. The P1 register is initialized to 0 by this instruction.
**
** The interface used by the implementation of the aforementioned functions ** The interface used by the implementation of the aforementioned functions
** to retrieve the collation sequence set by this opcode is not available ** to retrieve the collation sequence set by this opcode is not available
** publicly, only to user functions defined in func.c. ** publicly, only to user functions defined in func.c.
*/ */
case OP_CollSeq: { case OP_CollSeq: {
assert( pOp->p4type==P4_COLLSEQ ); assert( pOp->p4type==P4_COLLSEQ );
if( pOp->p1 ){
sqlite3VdbeMemSetInt64(&aMem[pOp->p1], 0);
}
break; break;
} }
@ -2120,6 +2127,11 @@ case OP_NotNull: { /* same as TK_NOTNULL, jump, in1 */
** then the cache of the cursor is reset prior to extracting the column. ** then the cache of the cursor is reset prior to extracting the column.
** The first OP_Column against a pseudo-table after the value of the content ** The first OP_Column against a pseudo-table after the value of the content
** register has changed should have this bit set. ** register has changed should have this bit set.
**
** If the OPFLAG_LENGTHARG and OPFLAG_TYPEOFARG bits are set on P5 when
** the result is guaranteed to only be used as the argument of a length()
** or typeof() function, respectively. The loading of large blobs can be
** skipped for length() and all content loading can be skipped for typeof().
*/ */
case OP_Column: { case OP_Column: {
u32 payloadSize; /* Number of bytes in the record */ u32 payloadSize; /* Number of bytes in the record */
@ -2260,7 +2272,7 @@ case OP_Column: {
pC->aRow = 0; pC->aRow = 0;
} }
} }
/* The following assert is true in all cases accept when /* The following assert is true in all cases except when
** the database file has been corrupted externally. ** the database file has been corrupted externally.
** assert( zRec!=0 || avail>=payloadSize || avail>=9 ); */ ** assert( zRec!=0 || avail>=payloadSize || avail>=9 ); */
szHdr = getVarint32((u8*)zData, offset); szHdr = getVarint32((u8*)zData, offset);
@ -2335,11 +2347,11 @@ case OP_Column: {
break; break;
} }
}else{ }else{
/* If i is less that nField, then there are less fields in this /* If i is less that nField, then there are fewer fields in this
** record than SetNumColumns indicated there are columns in the ** record than SetNumColumns indicated there are columns in the
** table. Set the offset for any extra columns not present in ** table. Set the offset for any extra columns not present in
** the record to 0. This tells code below to store a NULL ** the record to 0. This tells code below to store the default value
** instead of deserializing a value from the record. ** for the column instead of deserializing a value from the record.
*/ */
aOffset[i] = 0; aOffset[i] = 0;
} }
@ -2369,17 +2381,32 @@ case OP_Column: {
if( aOffset[p2] ){ if( aOffset[p2] ){
assert( rc==SQLITE_OK ); assert( rc==SQLITE_OK );
if( zRec ){ if( zRec ){
/* This is the common case where the whole row fits on a single page */
VdbeMemRelease(pDest); VdbeMemRelease(pDest);
sqlite3VdbeSerialGet((u8 *)&zRec[aOffset[p2]], aType[p2], pDest); sqlite3VdbeSerialGet((u8 *)&zRec[aOffset[p2]], aType[p2], pDest);
}else{ }else{
len = sqlite3VdbeSerialTypeLen(aType[p2]); /* This branch happens only when the row overflows onto multiple pages */
sqlite3VdbeMemMove(&sMem, pDest); t = aType[p2];
rc = sqlite3VdbeMemFromBtree(pCrsr, aOffset[p2], len, pC->isIndex, &sMem); if( (pOp->p5 & (OPFLAG_LENGTHARG|OPFLAG_TYPEOFARG))!=0
if( rc!=SQLITE_OK ){ && ((t>=12 && (t&1)==0) || (pOp->p5 & OPFLAG_TYPEOFARG)!=0)
goto op_column_out; ){
/* Content is irrelevant for the typeof() function and for
** the length(X) function if X is a blob. So we might as well use
** bogus content rather than reading content from disk. NULL works
** for text and blob and whatever is in the payloadSize64 variable
** will work for everything else. */
zData = t<12 ? (char*)&payloadSize64 : 0;
}else{
len = sqlite3VdbeSerialTypeLen(t);
sqlite3VdbeMemMove(&sMem, pDest);
rc = sqlite3VdbeMemFromBtree(pCrsr, aOffset[p2], len, pC->isIndex,
&sMem);
if( rc!=SQLITE_OK ){
goto op_column_out;
}
zData = sMem.z;
} }
zData = sMem.z; sqlite3VdbeSerialGet((u8*)zData, t, pDest);
sqlite3VdbeSerialGet((u8*)zData, aType[p2], pDest);
} }
pDest->enc = encoding; pDest->enc = encoding;
}else{ }else{
@ -2677,16 +2704,12 @@ case OP_Savepoint: {
if( !pSavepoint ){ if( !pSavepoint ){
sqlite3SetString(&p->zErrMsg, db, "no such savepoint: %s", zName); sqlite3SetString(&p->zErrMsg, db, "no such savepoint: %s", zName);
rc = SQLITE_ERROR; rc = SQLITE_ERROR;
}else if( }else if( db->writeVdbeCnt>0 && p1==SAVEPOINT_RELEASE ){
db->writeVdbeCnt>0 || (p1==SAVEPOINT_ROLLBACK && db->activeVdbeCnt>1)
){
/* It is not possible to release (commit) a savepoint if there are /* It is not possible to release (commit) a savepoint if there are
** active write statements. It is not possible to rollback a savepoint ** active write statements.
** if there are any active statements at all.
*/ */
sqlite3SetString(&p->zErrMsg, db, sqlite3SetString(&p->zErrMsg, db,
"cannot %s savepoint - SQL statements in progress", "cannot release savepoint - SQL statements in progress"
(p1==SAVEPOINT_ROLLBACK ? "rollback": "release")
); );
rc = SQLITE_BUSY; rc = SQLITE_BUSY;
}else{ }else{
@ -2711,6 +2734,11 @@ case OP_Savepoint: {
rc = p->rc; rc = p->rc;
}else{ }else{
iSavepoint = db->nSavepoint - iSavepoint - 1; iSavepoint = db->nSavepoint - iSavepoint - 1;
if( p1==SAVEPOINT_ROLLBACK ){
for(ii=0; ii<db->nDb; ii++){
sqlite3BtreeTripAllCursors(db->aDb[ii].pBt, SQLITE_ABORT);
}
}
for(ii=0; ii<db->nDb; ii++){ for(ii=0; ii<db->nDb; ii++){
rc = sqlite3BtreeSavepoint(db->aDb[ii].pBt, p1, iSavepoint); rc = sqlite3BtreeSavepoint(db->aDb[ii].pBt, p1, iSavepoint);
if( rc!=SQLITE_OK ){ if( rc!=SQLITE_OK ){
@ -2779,6 +2807,7 @@ case OP_AutoCommit: {
assert( desiredAutoCommit==1 || iRollback==0 ); assert( desiredAutoCommit==1 || iRollback==0 );
assert( db->activeVdbeCnt>0 ); /* At least this one VM is active */ assert( db->activeVdbeCnt>0 ); /* At least this one VM is active */
#if 0
if( turnOnAC && iRollback && db->activeVdbeCnt>1 ){ if( turnOnAC && iRollback && db->activeVdbeCnt>1 ){
/* If this instruction implements a ROLLBACK and other VMs are /* If this instruction implements a ROLLBACK and other VMs are
** still running, and a transaction is active, return an error indicating ** still running, and a transaction is active, return an error indicating
@ -2787,7 +2816,9 @@ case OP_AutoCommit: {
sqlite3SetString(&p->zErrMsg, db, "cannot rollback transaction - " sqlite3SetString(&p->zErrMsg, db, "cannot rollback transaction - "
"SQL statements in progress"); "SQL statements in progress");
rc = SQLITE_BUSY; rc = SQLITE_BUSY;
}else if( turnOnAC && !iRollback && db->writeVdbeCnt>0 ){ }else
#endif
if( turnOnAC && !iRollback && db->writeVdbeCnt>0 ){
/* If this instruction implements a COMMIT and other VMs are writing /* If this instruction implements a COMMIT and other VMs are writing
** return an error indicating that the other VMs must complete first. ** return an error indicating that the other VMs must complete first.
*/ */
@ -2797,7 +2828,7 @@ case OP_AutoCommit: {
}else if( desiredAutoCommit!=db->autoCommit ){ }else if( desiredAutoCommit!=db->autoCommit ){
if( iRollback ){ if( iRollback ){
assert( desiredAutoCommit==1 ); assert( desiredAutoCommit==1 );
sqlite3RollbackAll(db); sqlite3RollbackAll(db, SQLITE_ABORT_ROLLBACK);
db->autoCommit = 1; db->autoCommit = 1;
}else if( (rc = sqlite3VdbeCheckFk(p, 1))!=SQLITE_OK ){ }else if( (rc = sqlite3VdbeCheckFk(p, 1))!=SQLITE_OK ){
goto vdbe_return; goto vdbe_return;
@ -2853,7 +2884,7 @@ case OP_AutoCommit: {
** throw an ABORT exception), a statement transaction may also be opened. ** throw an ABORT exception), a statement transaction may also be opened.
** More specifically, a statement transaction is opened iff the database ** More specifically, a statement transaction is opened iff the database
** connection is currently not in autocommit mode, or if there are other ** connection is currently not in autocommit mode, or if there are other
** active statements. A statement transaction allows the affects of this ** active statements. A statement transaction allows the changes made by this
** VDBE to be rolled back after an error without having to roll back the ** VDBE to be rolled back after an error without having to roll back the
** entire transaction. If no error is encountered, the statement transaction ** entire transaction. If no error is encountered, the statement transaction
** will automatically commit when the VDBE halts. ** will automatically commit when the VDBE halts.
@ -3835,7 +3866,7 @@ case OP_NewRowid: { /* out2-prerelease */
assert( sqlite3BtreeCursorIsValid(pC->pCursor) ); assert( sqlite3BtreeCursorIsValid(pC->pCursor) );
rc = sqlite3BtreeKeySize(pC->pCursor, &v); rc = sqlite3BtreeKeySize(pC->pCursor, &v);
assert( rc==SQLITE_OK ); /* Cannot fail following BtreeLast() */ assert( rc==SQLITE_OK ); /* Cannot fail following BtreeLast() */
if( v==MAX_ROWID ){ if( v>=MAX_ROWID ){
pC->useRandomRowid = 1; pC->useRandomRowid = 1;
}else{ }else{
v++; /* IMP: R-29538-34987 */ v++; /* IMP: R-29538-34987 */
@ -4833,6 +4864,7 @@ case OP_ParseSchema: {
db->init.busy = 0; db->init.busy = 0;
} }
} }
if( rc ) sqlite3ResetInternalSchema(db, -1);
if( rc==SQLITE_NOMEM ){ if( rc==SQLITE_NOMEM ){
goto no_mem; goto no_mem;
} }
@ -5167,7 +5199,6 @@ case OP_Program: { /* jump */
p->nOp = pProgram->nOp; p->nOp = pProgram->nOp;
p->aOnceFlag = (u8 *)&p->apCsr[p->nCursor]; p->aOnceFlag = (u8 *)&p->apCsr[p->nCursor];
p->nOnceFlag = pProgram->nOnce; p->nOnceFlag = pProgram->nOnce;
p->nOp = pProgram->nOp;
pc = -1; pc = -1;
memset(p->aOnceFlag, 0, p->nOnceFlag); memset(p->aOnceFlag, 0, p->nOnceFlag);
@ -5356,6 +5387,7 @@ case OP_AggStep: {
ctx.s.db = db; ctx.s.db = db;
ctx.isError = 0; ctx.isError = 0;
ctx.pColl = 0; ctx.pColl = 0;
ctx.skipFlag = 0;
if( ctx.pFunc->flags & SQLITE_FUNC_NEEDCOLL ){ if( ctx.pFunc->flags & SQLITE_FUNC_NEEDCOLL ){
assert( pOp>p->aOp ); assert( pOp>p->aOp );
assert( pOp[-1].p4type==P4_COLLSEQ ); assert( pOp[-1].p4type==P4_COLLSEQ );
@ -5367,6 +5399,11 @@ case OP_AggStep: {
sqlite3SetString(&p->zErrMsg, db, "%s", sqlite3_value_text(&ctx.s)); sqlite3SetString(&p->zErrMsg, db, "%s", sqlite3_value_text(&ctx.s));
rc = ctx.isError; rc = ctx.isError;
} }
if( ctx.skipFlag ){
assert( pOp[-1].opcode==OP_CollSeq );
i = pOp[-1].p1;
if( i ) sqlite3VdbeMemSetInt64(&aMem[i], 1);
}
sqlite3VdbeMemRelease(&ctx.s); sqlite3VdbeMemRelease(&ctx.s);

Some files were not shown because too many files have changed in this diff Show More