mirror of
https://github.com/status-im/sqlcipher.git
synced 2025-02-23 09:18:11 +00:00
Merge branch 'sqlite-release' into release-integration
This commit is contained in:
commit
53c6fc9f5f
@ -954,6 +954,7 @@ clean:
|
||||
rm -f mkkeywordhash$(BEXE) keywordhash.h
|
||||
rm -f $(PUBLISH)
|
||||
rm -f *.da *.bb *.bbg gmon.out
|
||||
rm -rf quota2a quota2b quota2c
|
||||
rm -rf tsrc .target_source
|
||||
rm -f tclsqlite3$(TEXE)
|
||||
rm -f testfixture$(TEXE) test.db
|
||||
|
@ -979,9 +979,15 @@ sqlite3_analyzer.exe: sqlite3_analyzer.c
|
||||
|
||||
clean:
|
||||
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 lemon.exe lempar.c parse.*
|
||||
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
|
||||
del /Q .target_source
|
||||
del /Q tclsqlite3.exe
|
||||
|
@ -657,6 +657,7 @@ clean:
|
||||
rm -f lemon lempar.c parse.* sqlite*.tar.gz mkkeywordhash keywordhash.h
|
||||
rm -f $(PUBLISH)
|
||||
rm -f *.da *.bb *.bbg gmon.out
|
||||
rm -rf quota2a quota2b quota2c
|
||||
rm -rf tsrc target_source
|
||||
rm -f testloadext.dll libtestloadext.so
|
||||
rm -f sqlite3.c fts?amal.c tclsqlite3.c
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 3.7 KiB |
BIN
art/SQLite.eps
BIN
art/SQLite.eps
Binary file not shown.
BIN
art/SQLite.gif
BIN
art/SQLite.gif
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 |
BIN
art/nocopy.gif
BIN
art/nocopy.gif
Binary file not shown.
Before Width: | Height: | Size: 3.4 KiB |
Binary file not shown.
Before Width: | Height: | Size: 3.3 KiB |
BIN
art/src_logo.gif
BIN
art/src_logo.gif
Binary file not shown.
Before Width: | Height: | Size: 3.3 KiB |
@ -33,6 +33,9 @@
|
||||
/* Define to 1 if you have the `localtime_s' function. */
|
||||
#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. */
|
||||
#undef HAVE_MALLOC_USABLE_SIZE
|
||||
|
||||
|
21
configure
vendored
21
configure
vendored
@ -1,6 +1,6 @@
|
||||
#! /bin/sh
|
||||
# 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,
|
||||
# 2002, 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
|
||||
@ -743,8 +743,8 @@ SHELL=${CONFIG_SHELL-/bin/sh}
|
||||
# Identity of this package.
|
||||
PACKAGE_NAME='sqlite'
|
||||
PACKAGE_TARNAME='sqlite'
|
||||
PACKAGE_VERSION='3.7.10'
|
||||
PACKAGE_STRING='sqlite 3.7.10'
|
||||
PACKAGE_VERSION='3.7.12'
|
||||
PACKAGE_STRING='sqlite 3.7.12'
|
||||
PACKAGE_BUGREPORT=''
|
||||
|
||||
# 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.
|
||||
# This message is too long to be a string in the A/UX 3.1 sh.
|
||||
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]...
|
||||
|
||||
@ -1550,7 +1550,7 @@ fi
|
||||
|
||||
if test -n "$ac_init_help"; then
|
||||
case $ac_init_help in
|
||||
short | recursive ) echo "Configuration of sqlite 3.7.10:";;
|
||||
short | recursive ) echo "Configuration of sqlite 3.7.12:";;
|
||||
esac
|
||||
cat <<\_ACEOF
|
||||
|
||||
@ -1666,7 +1666,7 @@ fi
|
||||
test -n "$ac_init_help" && exit $ac_status
|
||||
if $ac_init_version; then
|
||||
cat <<\_ACEOF
|
||||
sqlite configure 3.7.10
|
||||
sqlite configure 3.7.12
|
||||
generated by GNU Autoconf 2.62
|
||||
|
||||
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
|
||||
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
|
||||
|
||||
$ $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
|
||||
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
|
||||
@ -14031,7 +14032,7 @@ exec 6>&1
|
||||
# report actual input values of CONFIG_FILES etc. instead of their
|
||||
# values after options handling.
|
||||
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
|
||||
|
||||
CONFIG_FILES = $CONFIG_FILES
|
||||
@ -14084,7 +14085,7 @@ Report bugs to <bug-autoconf@gnu.org>."
|
||||
_ACEOF
|
||||
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
|
||||
ac_cs_version="\\
|
||||
sqlite config.status 3.7.10
|
||||
sqlite config.status 3.7.12
|
||||
configured by $0, generated by GNU Autoconf 2.62,
|
||||
with options \\"`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`\\"
|
||||
|
||||
|
@ -122,7 +122,7 @@ AC_CHECK_TYPES([int8_t, int16_t, int32_t, int64_t, intptr_t, uint8_t,
|
||||
|
||||
#########
|
||||
# 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
|
||||
|
@ -944,7 +944,7 @@ static int asyncFileControl(sqlite3_file *id, int op, void *pArg){
|
||||
return SQLITE_OK;
|
||||
}
|
||||
}
|
||||
return SQLITE_ERROR;
|
||||
return SQLITE_NOTFOUND;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1044,15 +1044,18 @@ static int asyncOpen(
|
||||
char *z;
|
||||
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 ){
|
||||
nName = (int)strlen(zName)+1;
|
||||
nName = (int)strlen(zName);
|
||||
}
|
||||
|
||||
nByte = (
|
||||
sizeof(AsyncFileData) + /* AsyncFileData structure */
|
||||
2 * pVfs->szOsFile + /* AsyncFileData.pBaseRead and pBaseWrite */
|
||||
nName /* AsyncFileData.zName */
|
||||
nName + 2 /* AsyncFileData.zName */
|
||||
);
|
||||
z = sqlite3_malloc(nByte);
|
||||
if( !z ){
|
||||
|
388
ext/fts3/fts3.c
388
ext/fts3/fts3.c
@ -70,7 +70,7 @@
|
||||
** A doclist is stored like this:
|
||||
**
|
||||
** array {
|
||||
** varint docid;
|
||||
** varint docid; (delta from previous doclist)
|
||||
** array { (position list for column 0)
|
||||
** 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
|
||||
** 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
|
||||
** 234 at I is the next docid. It has one position 72 (72-2) and then
|
||||
** terminates with the 0 at K.
|
||||
** 234 at I is the delta to next docid (357). It has one position 70
|
||||
** (72-2) and then terminates with the 0 at K.
|
||||
**
|
||||
** 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
|
||||
@ -286,10 +286,6 @@
|
||||
** will eventually overtake the earlier data and knock it out. The
|
||||
** query logic likewise merges doclists so that newer data knocks out
|
||||
** 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"
|
||||
@ -469,6 +465,7 @@ static int fts3DisconnectMethod(sqlite3_vtab *pVtab){
|
||||
sqlite3_free(p->zReadExprlist);
|
||||
sqlite3_free(p->zWriteExprlist);
|
||||
sqlite3_free(p->zContentTbl);
|
||||
sqlite3_free(p->zLanguageid);
|
||||
|
||||
/* Invoke the tokenizer destructor to free the tokenizer. */
|
||||
p->pTokenizer->pModule->xDestroy(p->pTokenizer);
|
||||
@ -545,7 +542,9 @@ static void fts3DeclareVtab(int *pRc, Fts3Table *p){
|
||||
int rc; /* Return code */
|
||||
char *zSql; /* SQL statement passed to declare_vtab() */
|
||||
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);
|
||||
|
||||
/* 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 */
|
||||
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 ){
|
||||
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)
|
||||
** 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 */
|
||||
|
||||
if( p->zContentTbl==0 ){
|
||||
const char *zLanguageid = p->zLanguageid;
|
||||
char *zContentCols; /* Columns of %_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];
|
||||
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;
|
||||
|
||||
/* Create the content table */
|
||||
@ -626,11 +642,9 @@ static int fts3CreateTables(Fts3Table *p){
|
||||
p->zDb, p->zName
|
||||
);
|
||||
}
|
||||
assert( p->bHasStat==p->bFts4 );
|
||||
if( p->bHasStat ){
|
||||
fts3DbExec(&rc, db,
|
||||
"CREATE TABLE %Q.'%q_stat'(id INTEGER PRIMARY KEY, value BLOB);",
|
||||
p->zDb, p->zName
|
||||
);
|
||||
sqlite3Fts3CreateStatTable(&rc, p);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
@ -737,7 +751,7 @@ static void fts3Appendf(
|
||||
static char *fts3QuoteId(char const *zInput){
|
||||
int nRet;
|
||||
char *zRet;
|
||||
nRet = 2 + strlen(zInput)*2 + 1;
|
||||
nRet = 2 + (int)strlen(zInput)*2 + 1;
|
||||
zRet = sqlite3_malloc(nRet);
|
||||
if( zRet ){
|
||||
int i;
|
||||
@ -792,14 +806,20 @@ static char *fts3ReadExprList(Fts3Table *p, const char *zFunc, int *pRc){
|
||||
for(i=0; i<p->nColumn; 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);
|
||||
}else{
|
||||
fts3Appendf(pRc, &zRet, "rowid");
|
||||
for(i=0; i<p->nColumn; 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->zContentTbl ? p->zContentTbl : p->zName),
|
||||
(p->zContentTbl ? "" : "_content")
|
||||
@ -842,6 +862,9 @@ static char *fts3WriteExprList(Fts3Table *p, const char *zFunc, int *pRc){
|
||||
for(i=0; i<p->nColumn; i++){
|
||||
fts3Appendf(pRc, &zRet, ",%s(?)", zFunction);
|
||||
}
|
||||
if( p->zLanguageid ){
|
||||
fts3Appendf(pRc, &zRet, ", ?");
|
||||
}
|
||||
sqlite3_free(zFree);
|
||||
return zRet;
|
||||
}
|
||||
@ -984,7 +1007,7 @@ static int fts3ContentColumns(
|
||||
nCol = sqlite3_column_count(pStmt);
|
||||
for(i=0; i<nCol; 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. */
|
||||
@ -995,7 +1018,7 @@ static int fts3ContentColumns(
|
||||
char *p = (char *)&azCol[nCol];
|
||||
for(i=0; i<nCol; i++){
|
||||
const char *zCol = sqlite3_column_name(pStmt, i);
|
||||
int n = strlen(zCol)+1;
|
||||
int n = (int)strlen(zCol)+1;
|
||||
memcpy(p, zCol, n);
|
||||
azCol[i] = p;
|
||||
p += n;
|
||||
@ -1057,6 +1080,7 @@ static int fts3InitVtab(
|
||||
char *zCompress = 0; /* compress=? parameter (or NULL) */
|
||||
char *zUncompress = 0; /* uncompress=? parameter (or NULL) */
|
||||
char *zContent = 0; /* content=? parameter (or NULL) */
|
||||
char *zLanguageid = 0; /* languageid=? parameter (or NULL) */
|
||||
|
||||
assert( strlen(argv[0])==4 );
|
||||
assert( (sqlite3_strnicmp(argv[0], "fts4", 4)==0 && isFts4)
|
||||
@ -1106,7 +1130,8 @@ static int fts3InitVtab(
|
||||
{ "compress", 8 }, /* 2 -> COMPRESS */
|
||||
{ "uncompress", 10 }, /* 3 -> UNCOMPRESS */
|
||||
{ "order", 5 }, /* 4 -> ORDER */
|
||||
{ "content", 7 } /* 5 -> CONTENT */
|
||||
{ "content", 7 }, /* 5 -> CONTENT */
|
||||
{ "languageid", 10 } /* 6 -> LANGUAGEID */
|
||||
};
|
||||
|
||||
int iOpt;
|
||||
@ -1160,12 +1185,18 @@ static int fts3InitVtab(
|
||||
bDescIdx = (zVal[0]=='d' || zVal[0]=='D');
|
||||
break;
|
||||
|
||||
default: /* CONTENT */
|
||||
assert( iOpt==5 );
|
||||
sqlite3_free(zUncompress);
|
||||
case 5: /* CONTENT */
|
||||
sqlite3_free(zContent);
|
||||
zContent = zVal;
|
||||
zVal = 0;
|
||||
break;
|
||||
|
||||
case 6: /* LANGUAGEID */
|
||||
assert( iOpt==6 );
|
||||
sqlite3_free(zLanguageid);
|
||||
zLanguageid = zVal;
|
||||
zVal = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
sqlite3_free(zVal);
|
||||
@ -1195,8 +1226,21 @@ static int fts3InitVtab(
|
||||
sqlite3_free((void*)aCol);
|
||||
aCol = 0;
|
||||
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;
|
||||
|
||||
@ -1241,9 +1285,13 @@ static int fts3InitVtab(
|
||||
p->nMaxPendingData = FTS3_MAX_PENDING_DATA;
|
||||
p->bHasDocsize = (isFts4 && bNoDocsize==0);
|
||||
p->bHasStat = isFts4;
|
||||
p->bFts4 = isFts4;
|
||||
p->bDescIdx = bDescIdx;
|
||||
p->bAutoincrmerge = 0xff; /* 0xff means setting unknown */
|
||||
p->zContentTbl = zContent;
|
||||
p->zLanguageid = zLanguageid;
|
||||
zContent = 0;
|
||||
zLanguageid = 0;
|
||||
TESTONLY( p->inTransaction = -1 );
|
||||
TESTONLY( p->mxSavepoint = -1 );
|
||||
|
||||
@ -1292,6 +1340,16 @@ static int fts3InitVtab(
|
||||
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
|
||||
** estimate the cost of loading large doclists from the database. */
|
||||
fts3DatabasePageSize(&rc, p);
|
||||
@ -1306,6 +1364,7 @@ fts3_init_out:
|
||||
sqlite3_free(zCompress);
|
||||
sqlite3_free(zUncompress);
|
||||
sqlite3_free(zContent);
|
||||
sqlite3_free(zLanguageid);
|
||||
sqlite3_free((void *)aCol);
|
||||
if( rc!=SQLITE_OK ){
|
||||
if( p ){
|
||||
@ -1357,6 +1416,7 @@ static int fts3BestIndexMethod(sqlite3_vtab *pVTab, sqlite3_index_info *pInfo){
|
||||
Fts3Table *p = (Fts3Table *)pVTab;
|
||||
int i; /* Iterator variable */
|
||||
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,
|
||||
** 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;
|
||||
|
||||
/* 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 )
|
||||
){
|
||||
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->estimatedCost = 2.0;
|
||||
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].omit = 1;
|
||||
}
|
||||
if( iLangidCons>=0 ){
|
||||
pInfo->aConstraintUsage[iLangidCons].argvIndex = 2;
|
||||
}
|
||||
|
||||
/* Regardless of the strategy selected, FTS can deliver rows in rowid (or
|
||||
** docid) order. Both ascending and descending are possible.
|
||||
@ -2283,7 +2353,7 @@ static int fts3DoclistOrMerge(
|
||||
}
|
||||
|
||||
*paOut = aOut;
|
||||
*pnOut = (p-aOut);
|
||||
*pnOut = (int)(p-aOut);
|
||||
assert( *pnOut<=n1+n2+FTS3_VARINT_MAX-1 );
|
||||
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(
|
||||
Fts3Table *p, /* FTS3 table handle */
|
||||
int iLangid, /* Language id */
|
||||
int iIndex, /* Index to search (from 0 to p->nIndex-1) */
|
||||
int iLevel, /* Level of segments to scan */
|
||||
const char *zTerm, /* Term to query for */
|
||||
@ -2577,7 +2648,7 @@ static int fts3SegReaderCursor(
|
||||
|
||||
if( iLevel!=FTS3_SEGCURSOR_PENDING ){
|
||||
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)) ){
|
||||
@ -2600,7 +2671,9 @@ static int fts3SegReaderCursor(
|
||||
}
|
||||
|
||||
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;
|
||||
rc = fts3SegReaderCursorAppend(pCsr, pSeg);
|
||||
@ -2620,6 +2693,7 @@ static int fts3SegReaderCursor(
|
||||
*/
|
||||
int sqlite3Fts3SegReaderCursor(
|
||||
Fts3Table *p, /* FTS3 table handle */
|
||||
int iLangid, /* Language-id to search */
|
||||
int iIndex, /* Index to search (from 0 to p->nIndex-1) */
|
||||
int iLevel, /* Level of segments to scan */
|
||||
const char *zTerm, /* Term to query for */
|
||||
@ -2637,14 +2711,9 @@ int sqlite3Fts3SegReaderCursor(
|
||||
assert( FTS3_SEGCURSOR_ALL<0 && FTS3_SEGCURSOR_PENDING<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));
|
||||
|
||||
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(
|
||||
Fts3Table *p, /* FTS virtual table handle */
|
||||
int iLangid,
|
||||
const char *zTerm, /* Term to scan doclist of */
|
||||
int nTerm, /* Number of bytes in zTerm */
|
||||
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++){
|
||||
if( p->aIndex[i].nPrefix==nTerm ){
|
||||
bFound = 1;
|
||||
rc = sqlite3Fts3SegReaderCursor(
|
||||
p, i, FTS3_SEGCURSOR_ALL, zTerm, nTerm, 0, 0, pSegcsr);
|
||||
rc = sqlite3Fts3SegReaderCursor(p, pCsr->iLangid,
|
||||
i, FTS3_SEGCURSOR_ALL, zTerm, nTerm, 0, 0, pSegcsr
|
||||
);
|
||||
pSegcsr->bLookup = 1;
|
||||
}
|
||||
}
|
||||
@ -2705,19 +2778,21 @@ static int fts3TermSegReaderCursor(
|
||||
for(i=1; bFound==0 && i<p->nIndex; i++){
|
||||
if( p->aIndex[i].nPrefix==nTerm+1 ){
|
||||
bFound = 1;
|
||||
rc = sqlite3Fts3SegReaderCursor(
|
||||
p, i, FTS3_SEGCURSOR_ALL, zTerm, nTerm, 1, 0, pSegcsr
|
||||
rc = sqlite3Fts3SegReaderCursor(p, pCsr->iLangid,
|
||||
i, FTS3_SEGCURSOR_ALL, zTerm, nTerm, 1, 0, pSegcsr
|
||||
);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = fts3SegReaderCursorAddZero(p, zTerm, nTerm, pSegcsr);
|
||||
rc = fts3SegReaderCursorAddZero(
|
||||
p, pCsr->iLangid, zTerm, nTerm, pSegcsr
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( bFound==0 ){
|
||||
rc = sqlite3Fts3SegReaderCursor(
|
||||
p, 0, FTS3_SEGCURSOR_ALL, zTerm, nTerm, isPrefix, 0, pSegcsr
|
||||
rc = sqlite3Fts3SegReaderCursor(p, pCsr->iLangid,
|
||||
0, FTS3_SEGCURSOR_ALL, zTerm, nTerm, isPrefix, 0, pSegcsr
|
||||
);
|
||||
pSegcsr->bLookup = !isPrefix;
|
||||
}
|
||||
@ -2872,7 +2947,7 @@ static int fts3FilterMethod(
|
||||
UNUSED_PARAMETER(nVal);
|
||||
|
||||
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( p->pSegments==0 );
|
||||
|
||||
@ -2897,8 +2972,11 @@ static int fts3FilterMethod(
|
||||
return SQLITE_NOMEM;
|
||||
}
|
||||
|
||||
rc = sqlite3Fts3ExprParse(p->pTokenizer, p->azColumn, p->bHasStat,
|
||||
p->nColumn, iCol, zQuery, -1, &pCsr->pExpr
|
||||
pCsr->iLangid = 0;
|
||||
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_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
|
||||
** 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(
|
||||
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 rc = SQLITE_OK; /* Return Code */
|
||||
@ -2980,22 +3065,34 @@ static int fts3ColumnMethod(
|
||||
Fts3Table *p = (Fts3Table *)pCursor->pVtab;
|
||||
|
||||
/* 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 ){
|
||||
/* This call is a request for the "docid" column. Since "docid" is an
|
||||
** 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 ){
|
||||
/* The extra column whose name is the same as the table.
|
||||
** Return a blob which is a pointer to the cursor.
|
||||
*/
|
||||
sqlite3_result_blob(pContext, &pCsr, sizeof(pCsr), SQLITE_TRANSIENT);
|
||||
** Return a blob which is a pointer to the cursor. */
|
||||
sqlite3_result_blob(pCtx, &pCsr, sizeof(pCsr), SQLITE_TRANSIENT);
|
||||
}else if( iCol==p->nColumn+2 && pCsr->pExpr ){
|
||||
sqlite3_result_int64(pCtx, pCsr->iLangid);
|
||||
}else{
|
||||
/* The requested column is either a user column (one that contains
|
||||
** indexed data), or the language-id column. */
|
||||
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.
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
@ -3031,13 +3162,14 @@ static int fts3SyncMethod(sqlite3_vtab *pVtab){
|
||||
** Implementation of xBegin() method. This is a no-op.
|
||||
*/
|
||||
static int fts3BeginMethod(sqlite3_vtab *pVtab){
|
||||
TESTONLY( Fts3Table *p = (Fts3Table*)pVtab );
|
||||
Fts3Table *p = (Fts3Table*)pVtab;
|
||||
UNUSED_PARAMETER(pVtab);
|
||||
assert( p->pSegments==0 );
|
||||
assert( p->nPendingData==0 );
|
||||
assert( p->inTransaction!=1 );
|
||||
TESTONLY( p->inTransaction = 1 );
|
||||
TESTONLY( p->mxSavepoint = -1; );
|
||||
p->nLeafAdd = 0;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
@ -3332,11 +3464,15 @@ static int fts3RenameMethod(
|
||||
** Flush the contents of the pending-terms table to disk.
|
||||
*/
|
||||
static int fts3SavepointMethod(sqlite3_vtab *pVtab, int iSavepoint){
|
||||
int rc = SQLITE_OK;
|
||||
UNUSED_PARAMETER(iSavepoint);
|
||||
assert( ((Fts3Table *)pVtab)->inTransaction );
|
||||
assert( ((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);
|
||||
sqlite3_free(aPoslist);
|
||||
aPoslist = pList;
|
||||
nPoslist = aOut - aPoslist;
|
||||
nPoslist = (int)(aOut - aPoslist);
|
||||
if( nPoslist==0 ){
|
||||
sqlite3_free(aPoslist);
|
||||
pPhrase->doclist.pList = 0;
|
||||
@ -3740,7 +3876,7 @@ static int fts3EvalDeferredPhrase(Fts3Cursor *pCsr, Fts3Phrase *pPhrase){
|
||||
pPhrase->doclist.pList = aOut;
|
||||
if( fts3PoslistPhraseMerge(&aOut, nDistance, 0, 1, &p1, &p2) ){
|
||||
pPhrase->doclist.bFreeList = 1;
|
||||
pPhrase->doclist.nList = (aOut - pPhrase->doclist.pList);
|
||||
pPhrase->doclist.nList = (int)(aOut - pPhrase->doclist.pList);
|
||||
}else{
|
||||
sqlite3_free(aOut);
|
||||
pPhrase->doclist.pList = 0;
|
||||
@ -3809,7 +3945,7 @@ void sqlite3Fts3DoclistPrev(
|
||||
int nDoclist, /* Length of aDoclist in bytes */
|
||||
char **ppIter, /* IN/OUT: Iterator 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 */
|
||||
){
|
||||
char *p = *ppIter;
|
||||
@ -3836,7 +3972,7 @@ void sqlite3Fts3DoclistPrev(
|
||||
iMul = (bDescIdx ? -1 : 1);
|
||||
}
|
||||
|
||||
*pnList = pEnd - pNext;
|
||||
*pnList = (int)(pEnd - pNext);
|
||||
*ppIter = pNext;
|
||||
*piDocid = iDocid;
|
||||
}else{
|
||||
@ -3850,12 +3986,47 @@ void sqlite3Fts3DoclistPrev(
|
||||
}else{
|
||||
char *pSave = p;
|
||||
fts3ReversePoslist(aDoclist, &p);
|
||||
*pnList = (pSave - p);
|
||||
*pnList = (int)(pSave - 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.
|
||||
** If an error occurs, return an SQLite error code. Otherwise, return
|
||||
@ -3910,7 +4081,7 @@ static int fts3EvalPhraseNext(
|
||||
}
|
||||
pDL->pList = 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-
|
||||
** 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);
|
||||
|
||||
/* 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;
|
||||
Fts3Expr **apOr;
|
||||
aTC = (Fts3TokenAndCost *)sqlite3_malloc(
|
||||
@ -4268,8 +4439,8 @@ static int fts3EvalStart(Fts3Cursor *pCsr){
|
||||
Fts3Expr **ppOr = apOr;
|
||||
|
||||
fts3EvalTokenCosts(pCsr, 0, pCsr->pExpr, &pTC, &ppOr, &rc);
|
||||
nToken = pTC-aTC;
|
||||
nOr = ppOr-apOr;
|
||||
nToken = (int)(pTC-aTC);
|
||||
nOr = (int)(ppOr-apOr);
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = fts3EvalSelectDeferred(pCsr, 0, aTC, nToken);
|
||||
@ -4341,7 +4512,7 @@ static int fts3EvalNearTrim(
|
||||
&pOut, aTmp, nParam1, nParam2, paPoslist, &p2
|
||||
);
|
||||
if( res ){
|
||||
nNew = (pOut - pPhrase->doclist.pList) - 1;
|
||||
nNew = (int)(pOut - pPhrase->doclist.pList) - 1;
|
||||
assert( pPhrase->doclist.pList[nNew]=='\0' );
|
||||
assert( nNew<=pPhrase->doclist.nList && nNew>0 );
|
||||
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,
|
||||
** incremental, or neither.
|
||||
*/
|
||||
char *sqlite3Fts3EvalPhrasePoslist(
|
||||
int sqlite3Fts3EvalPhrasePoslist(
|
||||
Fts3Cursor *pCsr, /* FTS3 cursor object */
|
||||
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;
|
||||
Fts3Table *pTab = (Fts3Table *)pCsr->base.pVtab;
|
||||
char *pIter = pPhrase->doclist.pList;
|
||||
char *pIter;
|
||||
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 );
|
||||
if( !pIter
|
||||
|| pExpr->bEof
|
||||
|| pExpr->iDocid!=pCsr->iPrevId
|
||||
|| (pPhrase->iColumn<pTab->nColumn && pPhrase->iColumn!=iCol)
|
||||
){
|
||||
return 0;
|
||||
if( (pPhrase->iColumn<pTab->nColumn && pPhrase->iColumn!=iCol) ){
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
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 ){
|
||||
pIter++;
|
||||
pIter += sqlite3Fts3GetVarint32(pIter, &iThis);
|
||||
@ -5044,7 +5276,8 @@ char *sqlite3Fts3EvalPhrasePoslist(
|
||||
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.
|
||||
*/
|
||||
|
@ -67,6 +67,9 @@ extern const sqlite3_api_routines *sqlite3_api;
|
||||
#ifndef MIN
|
||||
# define MIN(x,y) ((x)<(y)?(x):(y))
|
||||
#endif
|
||||
#ifndef MAX
|
||||
# define MAX(x,y) ((x)>(y)?(x):(y))
|
||||
#endif
|
||||
|
||||
/*
|
||||
** 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)
|
||||
#else
|
||||
# define ALWAYS(x) (x)
|
||||
# define NEVER(X) (x)
|
||||
# define NEVER(x) (x)
|
||||
#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 unsigned int u32; /* 4-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.
|
||||
@ -192,36 +196,44 @@ struct Fts3Table {
|
||||
char **azColumn; /* column names. malloced */
|
||||
sqlite3_tokenizer *pTokenizer; /* tokenizer for inserts and queries */
|
||||
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
|
||||
** statements is run and reset within a single virtual table API call.
|
||||
*/
|
||||
sqlite3_stmt *aStmt[27];
|
||||
sqlite3_stmt *aStmt[37];
|
||||
|
||||
char *zReadExprlist;
|
||||
char *zWriteExprlist;
|
||||
|
||||
int nNodeSize; /* Soft limit for node size */
|
||||
u8 bFts4; /* True for FTS4, false for FTS3 */
|
||||
u8 bHasStat; /* True if %_stat table exists */
|
||||
u8 bHasDocsize; /* True if %_docsize table exists */
|
||||
u8 bDescIdx; /* True if doclists are in reverse order */
|
||||
u8 bIgnoreSavepoint; /* True to ignore xSavepoint invocations */
|
||||
int nPgsz; /* Page size for host database */
|
||||
char *zSegmentsTbl; /* Name of %_segments table */
|
||||
sqlite3_blob *pSegments; /* Blob handle open on %_segments table */
|
||||
|
||||
/* TODO: Fix the first paragraph of this comment.
|
||||
**
|
||||
** The following hash table is used to buffer pending index updates during
|
||||
** transactions. Variable nPendingData estimates the memory size of the
|
||||
** pending data, including hash table overhead, but not malloc overhead.
|
||||
** When nPendingData exceeds nMaxPendingData, the buffer is flushed
|
||||
** automatically. Variable iPrevDocid is the docid of the most recently
|
||||
** inserted record.
|
||||
/*
|
||||
** The following array of hash tables is used to buffer pending index
|
||||
** updates during transactions. All pending updates buffered at any one
|
||||
** time must share a common language-id (see the FTS4 langid= feature).
|
||||
** The current language id is stored in variable iPrevLangid.
|
||||
**
|
||||
** 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
|
||||
** terms that appear in the document set. Each subsequent index in aIndex[]
|
||||
** 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[] */
|
||||
struct Fts3Index {
|
||||
@ -231,12 +243,13 @@ struct Fts3Table {
|
||||
int nMaxPendingData; /* Max pending data before flush to disk */
|
||||
int nPendingData; /* Current bytes of pending data */
|
||||
sqlite_int64 iPrevDocid; /* Docid of most recently inserted document */
|
||||
int iPrevLangid; /* Langid of recently inserted document */
|
||||
|
||||
#if defined(SQLITE_DEBUG) || defined(SQLITE_COVERAGE_TEST)
|
||||
/* State variables used for validating that the transaction control
|
||||
** methods of the virtual table are called at appropriate times. These
|
||||
** values do not contribution to the FTS computation; they are used for
|
||||
** verifying the SQLite core.
|
||||
** values do not contribute to FTS functionality; they are used for
|
||||
** verifying the operation of the SQLite core.
|
||||
*/
|
||||
int inTransaction; /* True after xBegin but before xCommit/xRollback */
|
||||
int mxSavepoint; /* Largest valid xSavepoint integer */
|
||||
@ -255,6 +268,7 @@ struct Fts3Cursor {
|
||||
u8 isRequireSeek; /* True if must seek pStmt to %_content row */
|
||||
sqlite3_stmt *pStmt; /* Prepared statement in use by the cursor */
|
||||
Fts3Expr *pExpr; /* Parsed MATCH query string */
|
||||
int iLangid; /* Language being queried for */
|
||||
int nPhrase; /* Number of matchable phrases in query */
|
||||
Fts3DeferredToken *pDeferred; /* Deferred search tokens, if any */
|
||||
sqlite3_int64 iPrevId; /* Previous id read from aDoclist */
|
||||
@ -401,12 +415,12 @@ int sqlite3Fts3UpdateMethod(sqlite3_vtab*,int,sqlite3_value**,sqlite3_int64*);
|
||||
int sqlite3Fts3PendingTermsFlush(Fts3Table *);
|
||||
void sqlite3Fts3PendingTermsClear(Fts3Table *);
|
||||
int sqlite3Fts3Optimize(Fts3Table *);
|
||||
int sqlite3Fts3SegReaderNew(int, sqlite3_int64,
|
||||
int sqlite3Fts3SegReaderNew(int, int, sqlite3_int64,
|
||||
sqlite3_int64, sqlite3_int64, const char *, int, Fts3SegReader**);
|
||||
int sqlite3Fts3SegReaderPending(
|
||||
Fts3Table*,int,const char*,int,int,Fts3SegReader**);
|
||||
void sqlite3Fts3SegReaderFree(Fts3SegReader *);
|
||||
int sqlite3Fts3AllSegdirs(Fts3Table*, int, int, sqlite3_stmt **);
|
||||
int sqlite3Fts3AllSegdirs(Fts3Table*, int, int, int, sqlite3_stmt **);
|
||||
int sqlite3Fts3ReadLock(Fts3Table *);
|
||||
int sqlite3Fts3ReadBlock(Fts3Table*, sqlite3_int64, char **, int*, int*);
|
||||
|
||||
@ -418,6 +432,7 @@ int sqlite3Fts3DeferToken(Fts3Cursor *, Fts3PhraseToken *, int);
|
||||
int sqlite3Fts3CacheDeferredDoclists(Fts3Cursor *);
|
||||
void sqlite3Fts3FreeDeferredDoclists(Fts3Cursor *);
|
||||
void sqlite3Fts3SegmentsClose(Fts3Table *);
|
||||
int sqlite3Fts3MaxLevel(Fts3Table *, int *);
|
||||
|
||||
/* Special values interpreted by sqlite3SegReaderCursor() */
|
||||
#define FTS3_SEGCURSOR_PENDING -1
|
||||
@ -427,8 +442,8 @@ int sqlite3Fts3SegReaderStart(Fts3Table*, Fts3MultiSegReader*, Fts3SegFilter*);
|
||||
int sqlite3Fts3SegReaderStep(Fts3Table *, Fts3MultiSegReader *);
|
||||
void sqlite3Fts3SegReaderFinish(Fts3MultiSegReader *);
|
||||
|
||||
int sqlite3Fts3SegReaderCursor(
|
||||
Fts3Table *, int, int, const char *, int, int, int, Fts3MultiSegReader *);
|
||||
int sqlite3Fts3SegReaderCursor(Fts3Table *,
|
||||
int, int, int, const char *, int, int, int, Fts3MultiSegReader *);
|
||||
|
||||
/* Flags allowed as part of the 4th argument to SegmentReaderIterate() */
|
||||
#define FTS3_SEGMENT_REQUIRE_POS 0x00000001
|
||||
@ -469,6 +484,8 @@ struct Fts3MultiSegReader {
|
||||
int nDoclist; /* Size of aDoclist[] in bytes */
|
||||
};
|
||||
|
||||
int sqlite3Fts3Incrmerge(Fts3Table*,int,int);
|
||||
|
||||
/* fts3.c */
|
||||
int sqlite3Fts3PutVarint(char *, sqlite3_int64);
|
||||
int sqlite3Fts3GetVarint(const char *, sqlite_int64 *);
|
||||
@ -478,6 +495,7 @@ void sqlite3Fts3Dequote(char *);
|
||||
void sqlite3Fts3DoclistPrev(int,char*,int,char**,sqlite3_int64*,int*,u8*);
|
||||
int sqlite3Fts3EvalPhraseStats(Fts3Cursor *, Fts3Expr *, u32 *);
|
||||
int sqlite3Fts3FirstFilter(sqlite3_int64, char *, int, char *);
|
||||
void sqlite3Fts3CreateStatTable(int*, Fts3Table*);
|
||||
|
||||
/* fts3_tokenizer.c */
|
||||
const char *sqlite3Fts3NextToken(const char *, int *);
|
||||
@ -495,7 +513,7 @@ void sqlite3Fts3Snippet(sqlite3_context *, Fts3Cursor *, const char *,
|
||||
void sqlite3Fts3Matchinfo(sqlite3_context *, Fts3Cursor *, const char *);
|
||||
|
||||
/* fts3_expr.c */
|
||||
int sqlite3Fts3ExprParse(sqlite3_tokenizer *,
|
||||
int sqlite3Fts3ExprParse(sqlite3_tokenizer *, int,
|
||||
char **, int, int, int, const char *, int, Fts3Expr **
|
||||
);
|
||||
void sqlite3Fts3ExprFree(Fts3Expr *);
|
||||
@ -504,6 +522,10 @@ int sqlite3Fts3ExprInitTestInterface(sqlite3 *db);
|
||||
int sqlite3Fts3InitTerm(sqlite3 *db);
|
||||
#endif
|
||||
|
||||
int sqlite3Fts3OpenTokenizer(sqlite3_tokenizer *, int, const char *, int,
|
||||
sqlite3_tokenizer_cursor **
|
||||
);
|
||||
|
||||
/* fts3_aux.c */
|
||||
int sqlite3Fts3InitAux(sqlite3 *db);
|
||||
|
||||
@ -513,7 +535,7 @@ int sqlite3Fts3MsrIncrStart(
|
||||
Fts3Table*, Fts3MultiSegReader*, int, const char*, int);
|
||||
int sqlite3Fts3MsrIncrNext(
|
||||
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 sqlite3Fts3MsrIncrRestart(Fts3MultiSegReader *pCsr);
|
||||
|
||||
|
@ -79,9 +79,9 @@ static int fts3auxConnectMethod(
|
||||
}
|
||||
|
||||
zDb = argv[1];
|
||||
nDb = strlen(zDb);
|
||||
nDb = (int)strlen(zDb);
|
||||
zFts3 = argv[3];
|
||||
nFts3 = strlen(zFts3);
|
||||
nFts3 = (int)strlen(zFts3);
|
||||
|
||||
rc = sqlite3_declare_vtab(db, FTS3_TERMS_SCHEMA);
|
||||
if( rc!=SQLITE_OK ) return rc;
|
||||
@ -376,7 +376,7 @@ static int fts3auxFilterMethod(
|
||||
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
|
||||
);
|
||||
if( rc==SQLITE_OK ){
|
||||
|
@ -92,6 +92,7 @@ int sqlite3_fts3_enable_parentheses = 0;
|
||||
typedef struct ParseContext ParseContext;
|
||||
struct ParseContext {
|
||||
sqlite3_tokenizer *pTokenizer; /* Tokenizer module */
|
||||
int iLangid; /* Language id used with tokenizer */
|
||||
const char **azCol; /* Array of column names for fts3 table */
|
||||
int bFts4; /* True to allow FTS4-only syntax */
|
||||
int nCol; /* Number of entries in azCol[] */
|
||||
@ -127,6 +128,33 @@ static void *fts3MallocZero(int nByte){
|
||||
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
|
||||
@ -154,15 +182,13 @@ static int getNextToken(
|
||||
Fts3Expr *pRet = 0;
|
||||
int nConsumed = 0;
|
||||
|
||||
rc = pModule->xOpen(pTokenizer, z, n, &pCursor);
|
||||
rc = sqlite3Fts3OpenTokenizer(pTokenizer, pParse->iLangid, z, n, &pCursor);
|
||||
if( rc==SQLITE_OK ){
|
||||
const char *zToken;
|
||||
int nToken, iStart, iEnd, iPosition;
|
||||
int nByte; /* total space to allocate */
|
||||
|
||||
pCursor->pTokenizer = pTokenizer;
|
||||
rc = pModule->xNext(pCursor, &zToken, &nToken, &iStart, &iEnd, &iPosition);
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
nByte = sizeof(Fts3Expr) + sizeof(Fts3Phrase) + nToken;
|
||||
pRet = (Fts3Expr *)fts3MallocZero(nByte);
|
||||
@ -268,10 +294,10 @@ static int getNextString(
|
||||
** appends buffer zTemp to buffer p, and fills in the Fts3Expr and Fts3Phrase
|
||||
** structures.
|
||||
*/
|
||||
rc = pModule->xOpen(pTokenizer, zInput, nInput, &pCursor);
|
||||
rc = sqlite3Fts3OpenTokenizer(
|
||||
pTokenizer, pParse->iLangid, zInput, nInput, &pCursor);
|
||||
if( rc==SQLITE_OK ){
|
||||
int ii;
|
||||
pCursor->pTokenizer = pTokenizer;
|
||||
for(ii=0; rc==SQLITE_OK; ii++){
|
||||
const char *zByte;
|
||||
int nByte, iBegin, iEnd, iPos;
|
||||
@ -745,6 +771,7 @@ exprparse_out:
|
||||
*/
|
||||
int sqlite3Fts3ExprParse(
|
||||
sqlite3_tokenizer *pTokenizer, /* Tokenizer module */
|
||||
int iLangid, /* Language id for tokenizer */
|
||||
char **azCol, /* Array of column names for fts3 table */
|
||||
int bFts4, /* True to allow FTS4-only syntax */
|
||||
int nCol, /* Number of entries in azCol[] */
|
||||
@ -755,11 +782,13 @@ int sqlite3Fts3ExprParse(
|
||||
int nParsed;
|
||||
int rc;
|
||||
ParseContext sParse;
|
||||
|
||||
memset(&sParse, 0, sizeof(ParseContext));
|
||||
sParse.pTokenizer = pTokenizer;
|
||||
sParse.iLangid = iLangid;
|
||||
sParse.azCol = (const char **)azCol;
|
||||
sParse.nCol = nCol;
|
||||
sParse.iDefaultCol = iDefaultCol;
|
||||
sParse.nNest = 0;
|
||||
sParse.bFts4 = bFts4;
|
||||
if( z==0 ){
|
||||
*ppExpr = 0;
|
||||
@ -950,7 +979,7 @@ static void fts3ExprTest(
|
||||
}
|
||||
|
||||
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 ){
|
||||
sqlite3_result_error(context, "Error parsing expression", -1);
|
||||
|
@ -110,7 +110,10 @@ static int icuOpen(
|
||||
|
||||
*ppCursor = 0;
|
||||
|
||||
if( nInput<0 ){
|
||||
if( zInput==0 ){
|
||||
nInput = 0;
|
||||
zInput = "";
|
||||
}else if( nInput<0 ){
|
||||
nInput = strlen(zInput);
|
||||
}
|
||||
nChar = nInput+1;
|
||||
|
@ -630,6 +630,7 @@ static const sqlite3_tokenizer_module porterTokenizerModule = {
|
||||
porterOpen,
|
||||
porterClose,
|
||||
porterNext,
|
||||
0
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -360,10 +360,11 @@ static int fts3SnippetFindPositions(Fts3Expr *pExpr, int iPhrase, void *ctx){
|
||||
SnippetIter *p = (SnippetIter *)ctx;
|
||||
SnippetPhrase *pPhrase = &p->aPhrase[iPhrase];
|
||||
char *pCsr;
|
||||
int rc;
|
||||
|
||||
pPhrase->nToken = pExpr->pPhrase->nToken;
|
||||
|
||||
pCsr = sqlite3Fts3EvalPhrasePoslist(p->pCsr, pExpr, p->iCol);
|
||||
rc = sqlite3Fts3EvalPhrasePoslist(p->pCsr, pExpr, p->iCol, &pCsr);
|
||||
assert( rc==SQLITE_OK || pCsr==0 );
|
||||
if( pCsr ){
|
||||
int iFirst = 0;
|
||||
pPhrase->pList = pCsr;
|
||||
@ -374,10 +375,12 @@ static int fts3SnippetFindPositions(Fts3Expr *pExpr, int iPhrase, void *ctx){
|
||||
pPhrase->iHead = iFirst;
|
||||
pPhrase->iTail = iFirst;
|
||||
}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(
|
||||
Fts3Table *pTab, /* FTS3 table snippet comes from */
|
||||
int iLangid, /* Language id to use in tokenizing */
|
||||
int nSnippet, /* Number of tokens desired for snippet */
|
||||
const char *zDoc, /* Document text to extract snippet from */
|
||||
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)
|
||||
** 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 ){
|
||||
return rc;
|
||||
}
|
||||
pC->pTokenizer = pTab->pTokenizer;
|
||||
while( rc==SQLITE_OK && iCurrent<(nSnippet+nDesired) ){
|
||||
const char *ZDUMMY; int DUMMY1, DUMMY2, DUMMY3;
|
||||
rc = pMod->xNext(pC, &ZDUMMY, &DUMMY1, &DUMMY2, &DUMMY3, &iCurrent);
|
||||
@ -631,11 +634,10 @@ static int fts3SnippetText(
|
||||
|
||||
/* Open a token cursor on the document. */
|
||||
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 ){
|
||||
return rc;
|
||||
}
|
||||
pC->pTokenizer = pTab->pTokenizer;
|
||||
|
||||
while( rc==SQLITE_OK ){
|
||||
int iBegin; /* Offset in zDoc of start of token */
|
||||
@ -657,7 +659,9 @@ static int fts3SnippetText(
|
||||
|
||||
if( !isShiftDone ){
|
||||
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;
|
||||
|
||||
/* Now that the shift has been done, check if the initial "..." are
|
||||
@ -769,13 +773,14 @@ static int fts3ExprLocalHitsCb(
|
||||
int iPhrase, /* Phrase number */
|
||||
void *pCtx /* Pointer to MatchInfo structure */
|
||||
){
|
||||
int rc = SQLITE_OK;
|
||||
MatchInfo *p = (MatchInfo *)pCtx;
|
||||
int iStart = iPhrase * p->nCol * 3;
|
||||
int i;
|
||||
|
||||
for(i=0; i<p->nCol; i++){
|
||||
for(i=0; i<p->nCol && rc==SQLITE_OK; i++){
|
||||
char *pCsr;
|
||||
pCsr = sqlite3Fts3EvalPhrasePoslist(p->pCursor, pExpr, i);
|
||||
rc = sqlite3Fts3EvalPhrasePoslist(p->pCursor, pExpr, i, &pCsr);
|
||||
if( pCsr ){
|
||||
p->aMatchinfo[iStart+i*3] = fts3ColumnlistCount(&pCsr);
|
||||
}else{
|
||||
@ -783,7 +788,7 @@ static int fts3ExprLocalHitsCb(
|
||||
}
|
||||
}
|
||||
|
||||
return SQLITE_OK;
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int fts3MatchinfoCheck(
|
||||
@ -793,8 +798,8 @@ static int fts3MatchinfoCheck(
|
||||
){
|
||||
if( (cArg==FTS3_MATCHINFO_NPHRASE)
|
||||
|| (cArg==FTS3_MATCHINFO_NCOL)
|
||||
|| (cArg==FTS3_MATCHINFO_NDOC && pTab->bHasStat)
|
||||
|| (cArg==FTS3_MATCHINFO_AVGLENGTH && pTab->bHasStat)
|
||||
|| (cArg==FTS3_MATCHINFO_NDOC && pTab->bFts4)
|
||||
|| (cArg==FTS3_MATCHINFO_AVGLENGTH && pTab->bFts4)
|
||||
|| (cArg==FTS3_MATCHINFO_LENGTH && pTab->bHasDocsize)
|
||||
|| (cArg==FTS3_MATCHINFO_LCS)
|
||||
|| (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 */
|
||||
|
||||
for(i=0; i<pInfo->nPhrase; i++){
|
||||
int rc;
|
||||
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 ){
|
||||
pIt->iPos = pIt->iPosOffset;
|
||||
fts3LcsIteratorAdvance(&aIter[i]);
|
||||
@ -1297,9 +1304,10 @@ static int fts3ExprTermOffsetInit(Fts3Expr *pExpr, int iPhrase, void *ctx){
|
||||
int iTerm; /* For looping through nTerm phrase terms */
|
||||
char *pList; /* Pointer to position list for phrase */
|
||||
int iPos = 0; /* First position in position-list */
|
||||
int rc;
|
||||
|
||||
UNUSED_PARAMETER(iPhrase);
|
||||
pList = sqlite3Fts3EvalPhrasePoslist(p->pCsr, pExpr, p->iCol);
|
||||
rc = sqlite3Fts3EvalPhrasePoslist(p->pCsr, pExpr, p->iCol, &pList);
|
||||
nTerm = pExpr->pPhrase->nToken;
|
||||
if( pList ){
|
||||
fts3GetDeltaPosition(&pList, &iPos);
|
||||
@ -1313,7 +1321,7 @@ static int fts3ExprTermOffsetInit(Fts3Expr *pExpr, int iPhrase, void *ctx){
|
||||
pT->iPos = iPos;
|
||||
}
|
||||
|
||||
return SQLITE_OK;
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1390,9 +1398,10 @@ void sqlite3Fts3Offsets(
|
||||
}
|
||||
|
||||
/* 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;
|
||||
pC->pTokenizer = pTab->pTokenizer;
|
||||
|
||||
rc = pMod->xNext(pC, &ZDUMMY, &NDUMMY, &iStart, &iEnd, &iCurrent);
|
||||
while( rc==SQLITE_OK ){
|
||||
|
@ -73,6 +73,7 @@ static int fts3termConnectMethod(
|
||||
Fts3termTable *p; /* Virtual table object to return */
|
||||
int iIndex = 0;
|
||||
|
||||
UNUSED_PARAMETER(pCtx);
|
||||
if( argc==5 ){
|
||||
iIndex = atoi(argv[4]);
|
||||
argc--;
|
||||
@ -87,9 +88,9 @@ static int fts3termConnectMethod(
|
||||
}
|
||||
|
||||
zDb = argv[1];
|
||||
nDb = strlen(zDb);
|
||||
nDb = (int)strlen(zDb);
|
||||
zFts3 = argv[3];
|
||||
nFts3 = strlen(zFts3);
|
||||
nFts3 = (int)strlen(zFts3);
|
||||
|
||||
rc = sqlite3_declare_vtab(db, FTS3_TERMS_SCHEMA);
|
||||
if( rc!=SQLITE_OK ) return rc;
|
||||
@ -231,12 +232,12 @@ static int fts3termNextMethod(sqlite3_vtab_cursor *pCursor){
|
||||
|
||||
if( v==1 ){
|
||||
pCsr->pNext += sqlite3Fts3GetVarint(pCsr->pNext, &v);
|
||||
pCsr->iCol += v;
|
||||
pCsr->iCol += (int)v;
|
||||
pCsr->iPos = 0;
|
||||
pCsr->pNext += sqlite3Fts3GetVarint(pCsr->pNext, &v);
|
||||
}
|
||||
|
||||
pCsr->iPos += (v - 2);
|
||||
pCsr->iPos += (int)(v - 2);
|
||||
|
||||
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_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
|
||||
);
|
||||
if( rc==SQLITE_OK ){
|
||||
@ -357,7 +358,10 @@ int sqlite3Fts3InitTerm(sqlite3 *db){
|
||||
0, /* xCommit */
|
||||
0, /* xRollback */
|
||||
0, /* xFindFunction */
|
||||
0 /* xRename */
|
||||
0, /* xRename */
|
||||
0, /* xSavepoint */
|
||||
0, /* xRelease */
|
||||
0 /* xRollbackTo */
|
||||
};
|
||||
int rc; /* Return code */
|
||||
|
||||
|
@ -13,13 +13,17 @@
|
||||
** 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
|
||||
** 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 <string.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 */
|
||||
#include "fts3Int.h"
|
||||
@ -158,6 +162,8 @@ static int fts3_near_match_cmd(
|
||||
Tcl_Obj **apExprToken;
|
||||
int nExprToken;
|
||||
|
||||
UNUSED_PARAMETER(clientData);
|
||||
|
||||
/* Must have 3 or more arguments. */
|
||||
if( objc<3 || (objc%2)==0 ){
|
||||
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_DecrRefCount(pRet);
|
||||
#endif
|
||||
UNUSED_PARAMETER(clientData);
|
||||
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){
|
||||
Tcl_CreateObjCommand(interp, "fts3_near_match", fts3_near_match_cmd, 0, 0);
|
||||
Tcl_CreateObjCommand(interp,
|
||||
"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;
|
||||
}
|
||||
#endif /* SQLITE_ENABLE_FTS3 || SQLITE_ENABLE_FTS4 */
|
||||
#endif /* ifdef SQLITE_TEST */
|
||||
|
@ -288,11 +288,10 @@ static void testFunc(
|
||||
goto finish;
|
||||
}
|
||||
pTokenizer->pModule = p;
|
||||
if( SQLITE_OK!=p->xOpen(pTokenizer, zInput, nInput, &pCsr) ){
|
||||
if( sqlite3Fts3OpenTokenizer(pTokenizer, 0, zInput, nInput, &pCsr) ){
|
||||
zErr = "error in xOpen()";
|
||||
goto finish;
|
||||
}
|
||||
pCsr->pTokenizer = pTokenizer;
|
||||
|
||||
while( SQLITE_OK==p->xNext(pCsr, &zToken, &nToken, &iStart, &iEnd, &iPos) ){
|
||||
Tcl_ListObjAppendElement(0, pRet, Tcl_NewIntObj(iPos));
|
||||
|
@ -52,7 +52,7 @@ typedef struct sqlite3_tokenizer_cursor sqlite3_tokenizer_cursor;
|
||||
struct sqlite3_tokenizer_module {
|
||||
|
||||
/*
|
||||
** Structure version. Should always be set to 0.
|
||||
** Structure version. Should always be set to 0 or 1.
|
||||
*/
|
||||
int iVersion;
|
||||
|
||||
@ -133,6 +133,15 @@ struct sqlite3_tokenizer_module {
|
||||
int *piEndOffset, /* OUT: Byte offset of end of token in input buffer */
|
||||
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 {
|
||||
|
@ -218,6 +218,7 @@ static const sqlite3_tokenizer_module simpleTokenizerModule = {
|
||||
simpleOpen,
|
||||
simpleClose,
|
||||
simpleNext,
|
||||
0,
|
||||
};
|
||||
|
||||
/*
|
||||
|
File diff suppressed because it is too large
Load Diff
874
ext/fts3/tool/fts3view.c
Normal file
874
ext/fts3/tool/fts3view.c
Normal 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;
|
||||
}
|
@ -182,6 +182,19 @@ struct Rtree {
|
||||
#define RTREE_COORD_REAL32 0
|
||||
#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
|
||||
** maximum. In Gutman's notation:
|
||||
@ -217,20 +230,25 @@ struct RtreeCursor {
|
||||
};
|
||||
|
||||
union RtreeCoord {
|
||||
float f;
|
||||
RtreeValue f;
|
||||
int i;
|
||||
};
|
||||
|
||||
/*
|
||||
** The argument is an RtreeCoord. Return the value stored within the RtreeCoord
|
||||
** formatted as a double. This macro assumes that local variable pRtree points
|
||||
** to the Rtree structure associated with the RtreeCoord.
|
||||
** formatted as a RtreeDValue (double or int64). This macro assumes that local
|
||||
** variable pRtree points to the Rtree structure associated with the
|
||||
** RtreeCoord.
|
||||
*/
|
||||
#define DCOORD(coord) ( \
|
||||
(pRtree->eCoordType==RTREE_COORD_REAL32) ? \
|
||||
((double)coord.f) : \
|
||||
((double)coord.i) \
|
||||
)
|
||||
#ifdef SQLITE_RTREE_INT_ONLY
|
||||
# define DCOORD(coord) ((RtreeDValue)coord.i)
|
||||
#else
|
||||
# define DCOORD(coord) ( \
|
||||
(pRtree->eCoordType==RTREE_COORD_REAL32) ? \
|
||||
((double)coord.f) : \
|
||||
((double)coord.i) \
|
||||
)
|
||||
#endif
|
||||
|
||||
/*
|
||||
** A search constraint.
|
||||
@ -238,8 +256,8 @@ union RtreeCoord {
|
||||
struct RtreeConstraint {
|
||||
int iCoord; /* Index of constrained coordinate */
|
||||
int op; /* Constraining operation */
|
||||
double rValue; /* Constraint value. */
|
||||
int (*xGeom)(sqlite3_rtree_geometry *, int, double *, int *);
|
||||
RtreeDValue rValue; /* Constraint value. */
|
||||
int (*xGeom)(sqlite3_rtree_geometry*, int, RtreeDValue*, int*);
|
||||
sqlite3_rtree_geometry *pGeom; /* Constraint callback argument for a MATCH */
|
||||
};
|
||||
|
||||
@ -287,10 +305,10 @@ struct RtreeCell {
|
||||
*/
|
||||
struct RtreeMatchArg {
|
||||
u32 magic; /* Always RTREE_GEOMETRY_MAGIC */
|
||||
int (*xGeom)(sqlite3_rtree_geometry *, int, double *, int *);
|
||||
int (*xGeom)(sqlite3_rtree_geometry *, int, RtreeDValue*, int *);
|
||||
void *pContext;
|
||||
int nParam;
|
||||
double aParam[1];
|
||||
RtreeDValue aParam[1];
|
||||
};
|
||||
|
||||
/*
|
||||
@ -302,7 +320,7 @@ struct RtreeMatchArg {
|
||||
** the geometry callback function).
|
||||
*/
|
||||
struct RtreeGeomCallback {
|
||||
int (*xGeom)(sqlite3_rtree_geometry *, int, double *, int *);
|
||||
int (*xGeom)(sqlite3_rtree_geometry*, int, RtreeDValue*, int*);
|
||||
void *pContext;
|
||||
};
|
||||
|
||||
@ -868,7 +886,7 @@ static int testRtreeGeom(
|
||||
int *pbRes /* OUT: Test result */
|
||||
){
|
||||
int i;
|
||||
double aCoord[RTREE_MAX_DIMENSIONS*2];
|
||||
RtreeDValue aCoord[RTREE_MAX_DIMENSIONS*2];
|
||||
int nCoord = pRtree->nDim*2;
|
||||
|
||||
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);
|
||||
for(ii=0; bRes==0 && ii<pCursor->nConstraint; ii++){
|
||||
RtreeConstraint *p = &pCursor->aConstraint[ii];
|
||||
double cell_min = DCOORD(cell.aCoord[(p->iCoord>>1)*2]);
|
||||
double cell_max = DCOORD(cell.aCoord[(p->iCoord>>1)*2+1]);
|
||||
RtreeDValue cell_min = DCOORD(cell.aCoord[(p->iCoord>>1)*2]);
|
||||
RtreeDValue cell_max = DCOORD(cell.aCoord[(p->iCoord>>1)*2+1]);
|
||||
|
||||
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
|
||||
@ -951,7 +969,7 @@ static int testRtreeEntry(Rtree *pRtree, RtreeCursor *pCursor, int *pbEof){
|
||||
nodeGetCell(pRtree, pCursor->pNode, pCursor->iCell, &cell);
|
||||
for(ii=0; ii<pCursor->nConstraint; ii++){
|
||||
RtreeConstraint *p = &pCursor->aConstraint[ii];
|
||||
double coord = DCOORD(cell.aCoord[p->iCoord]);
|
||||
RtreeDValue coord = DCOORD(cell.aCoord[p->iCoord]);
|
||||
int res;
|
||||
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
|
||||
@ -1149,9 +1167,12 @@ static int rtreeColumn(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int i){
|
||||
}else{
|
||||
RtreeCoord c;
|
||||
nodeGetCoord(pRtree, pCsr->pNode, pCsr->iCell, i-1, &c);
|
||||
#ifndef SQLITE_RTREE_INT_ONLY
|
||||
if( pRtree->eCoordType==RTREE_COORD_REAL32 ){
|
||||
sqlite3_result_double(ctx, c.f);
|
||||
}else{
|
||||
}else
|
||||
#endif
|
||||
{
|
||||
assert( pRtree->eCoordType==RTREE_COORD_INT32 );
|
||||
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. */
|
||||
nBlob = sqlite3_value_bytes(pValue);
|
||||
if( nBlob<(int)sizeof(RtreeMatchArg)
|
||||
|| ((nBlob-sizeof(RtreeMatchArg))%sizeof(double))!=0
|
||||
|| ((nBlob-sizeof(RtreeMatchArg))%sizeof(RtreeDValue))!=0
|
||||
){
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
@ -1212,7 +1233,7 @@ static int deserializeGeometry(sqlite3_value *pValue, RtreeConstraint *pCons){
|
||||
|
||||
memcpy(p, sqlite3_value_blob(pValue), nBlob);
|
||||
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);
|
||||
return SQLITE_ERROR;
|
||||
@ -1284,7 +1305,11 @@ static int rtreeFilter(
|
||||
break;
|
||||
}
|
||||
}else{
|
||||
#ifdef SQLITE_RTREE_INT_ONLY
|
||||
p->rValue = sqlite3_value_int64(argv[ii]);
|
||||
#else
|
||||
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.
|
||||
*/
|
||||
static float cellArea(Rtree *pRtree, RtreeCell *p){
|
||||
float area = 1.0;
|
||||
static RtreeDValue cellArea(Rtree *pRtree, RtreeCell *p){
|
||||
RtreeDValue area = (RtreeDValue)1;
|
||||
int ii;
|
||||
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;
|
||||
}
|
||||
@ -1431,11 +1456,11 @@ static float cellArea(Rtree *pRtree, RtreeCell *p){
|
||||
** Return the margin length of cell p. The margin length is the sum
|
||||
** of the objects size in each dimension.
|
||||
*/
|
||||
static float cellMargin(Rtree *pRtree, RtreeCell *p){
|
||||
float margin = 0.0;
|
||||
static RtreeDValue cellMargin(Rtree *pRtree, RtreeCell *p){
|
||||
RtreeDValue margin = (RtreeDValue)0;
|
||||
int ii;
|
||||
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;
|
||||
}
|
||||
@ -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.
|
||||
*/
|
||||
static float cellGrowth(Rtree *pRtree, RtreeCell *p, RtreeCell *pCell){
|
||||
float area;
|
||||
static RtreeDValue cellGrowth(Rtree *pRtree, RtreeCell *p, RtreeCell *pCell){
|
||||
RtreeDValue area;
|
||||
RtreeCell cell;
|
||||
memcpy(&cell, p, sizeof(RtreeCell));
|
||||
area = cellArea(pRtree, &cell);
|
||||
@ -1490,7 +1515,7 @@ static float cellGrowth(Rtree *pRtree, RtreeCell *p, RtreeCell *pCell){
|
||||
}
|
||||
|
||||
#if VARIANT_RSTARTREE_CHOOSESUBTREE || VARIANT_RSTARTREE_SPLIT
|
||||
static float cellOverlap(
|
||||
static RtreeDValue cellOverlap(
|
||||
Rtree *pRtree,
|
||||
RtreeCell *p,
|
||||
RtreeCell *aCell,
|
||||
@ -1498,7 +1523,7 @@ static float cellOverlap(
|
||||
int iExclude
|
||||
){
|
||||
int ii;
|
||||
float overlap = 0.0;
|
||||
RtreeDValue overlap = 0.0;
|
||||
for(ii=0; ii<nCell; ii++){
|
||||
#if VARIANT_RSTARTREE_CHOOSESUBTREE
|
||||
if( ii!=iExclude )
|
||||
@ -1508,10 +1533,9 @@ static float cellOverlap(
|
||||
#endif
|
||||
{
|
||||
int jj;
|
||||
float o = 1.0;
|
||||
RtreeDValue o = (RtreeDValue)1;
|
||||
for(jj=0; jj<(pRtree->nDim*2); jj+=2){
|
||||
double x1;
|
||||
double x2;
|
||||
RtreeDValue x1, x2;
|
||||
|
||||
x1 = MAX(DCOORD(p->aCoord[jj]), DCOORD(aCell[ii].aCoord[jj]));
|
||||
x2 = MIN(DCOORD(p->aCoord[jj+1]), DCOORD(aCell[ii].aCoord[jj+1]));
|
||||
@ -1520,7 +1544,7 @@ static float cellOverlap(
|
||||
o = 0.0;
|
||||
break;
|
||||
}else{
|
||||
o = o * (float)(x2-x1);
|
||||
o = o * (x2-x1);
|
||||
}
|
||||
}
|
||||
overlap += o;
|
||||
@ -1531,7 +1555,7 @@ static float cellOverlap(
|
||||
#endif
|
||||
|
||||
#if VARIANT_RSTARTREE_CHOOSESUBTREE
|
||||
static float cellOverlapEnlargement(
|
||||
static RtreeDValue cellOverlapEnlargement(
|
||||
Rtree *pRtree,
|
||||
RtreeCell *p,
|
||||
RtreeCell *pInsert,
|
||||
@ -1539,12 +1563,11 @@ static float cellOverlapEnlargement(
|
||||
int nCell,
|
||||
int iExclude
|
||||
){
|
||||
double before;
|
||||
double after;
|
||||
RtreeDValue before, after;
|
||||
before = cellOverlap(pRtree, p, aCell, nCell, iExclude);
|
||||
cellUnion(pRtree, p, pInsert);
|
||||
after = cellOverlap(pRtree, p, aCell, nCell, iExclude);
|
||||
return (float)(after-before);
|
||||
return (after-before);
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -1568,11 +1591,11 @@ static int ChooseLeaf(
|
||||
int iCell;
|
||||
sqlite3_int64 iBest = 0;
|
||||
|
||||
float fMinGrowth = 0.0;
|
||||
float fMinArea = 0.0;
|
||||
RtreeDValue fMinGrowth = 0.0;
|
||||
RtreeDValue fMinArea = 0.0;
|
||||
#if VARIANT_RSTARTREE_CHOOSESUBTREE
|
||||
float fMinOverlap = 0.0;
|
||||
float overlap;
|
||||
RtreeDValue fMinOverlap = 0.0;
|
||||
RtreeDValue overlap;
|
||||
#endif
|
||||
|
||||
int nCell = NCELL(pNode);
|
||||
@ -1603,8 +1626,8 @@ static int ChooseLeaf(
|
||||
*/
|
||||
for(iCell=0; iCell<nCell; iCell++){
|
||||
int bBest = 0;
|
||||
float growth;
|
||||
float area;
|
||||
RtreeDValue growth;
|
||||
RtreeDValue area;
|
||||
nodeGetCell(pRtree, pNode, iCell, &cell);
|
||||
growth = cellGrowth(pRtree, &cell, pCell);
|
||||
area = cellArea(pRtree, &cell);
|
||||
@ -1731,7 +1754,7 @@ static void LinearPickSeeds(
|
||||
int i;
|
||||
int iLeftSeed = 0;
|
||||
int iRightSeed = 1;
|
||||
float maxNormalInnerWidth = 0.0;
|
||||
RtreeDValue maxNormalInnerWidth = (RtreeDValue)0;
|
||||
|
||||
/* Pick two "seed" cells from the array of cells. The algorithm used
|
||||
** here is the LinearPickSeeds algorithm from Gutman[1984]. The
|
||||
@ -1739,18 +1762,18 @@ static void LinearPickSeeds(
|
||||
** variables iLeftSeek and iRightSeed.
|
||||
*/
|
||||
for(i=0; i<pRtree->nDim; i++){
|
||||
float x1 = DCOORD(aCell[0].aCoord[i*2]);
|
||||
float x2 = DCOORD(aCell[0].aCoord[i*2+1]);
|
||||
float x3 = x1;
|
||||
float x4 = x2;
|
||||
RtreeDValue x1 = DCOORD(aCell[0].aCoord[i*2]);
|
||||
RtreeDValue x2 = DCOORD(aCell[0].aCoord[i*2+1]);
|
||||
RtreeDValue x3 = x1;
|
||||
RtreeDValue x4 = x2;
|
||||
int jj;
|
||||
|
||||
int iCellLeft = 0;
|
||||
int iCellRight = 0;
|
||||
|
||||
for(jj=1; jj<nCell; jj++){
|
||||
float left = DCOORD(aCell[jj].aCoord[i*2]);
|
||||
float right = DCOORD(aCell[jj].aCoord[i*2+1]);
|
||||
RtreeDValue left = DCOORD(aCell[jj].aCoord[i*2]);
|
||||
RtreeDValue right = DCOORD(aCell[jj].aCoord[i*2+1]);
|
||||
|
||||
if( left<x1 ) x1 = left;
|
||||
if( right>x4 ) x4 = right;
|
||||
@ -1765,7 +1788,7 @@ static void LinearPickSeeds(
|
||||
}
|
||||
|
||||
if( x4!=x1 ){
|
||||
float normalwidth = (x3 - x2) / (x4 - x1);
|
||||
RtreeDValue normalwidth = (x3 - x2) / (x4 - x1);
|
||||
if( normalwidth>maxNormalInnerWidth ){
|
||||
iLeftSeed = iCellLeft;
|
||||
iRightSeed = iCellRight;
|
||||
@ -1794,13 +1817,13 @@ static RtreeCell *QuadraticPickNext(
|
||||
#define FABS(a) ((a)<0.0?-1.0*(a):(a))
|
||||
|
||||
int iSelect = -1;
|
||||
float fDiff;
|
||||
RtreeDValue fDiff;
|
||||
int ii;
|
||||
for(ii=0; ii<nCell; ii++){
|
||||
if( aiUsed[ii]==0 ){
|
||||
float left = cellGrowth(pRtree, pLeftBox, &aCell[ii]);
|
||||
float right = cellGrowth(pRtree, pLeftBox, &aCell[ii]);
|
||||
float diff = FABS(right-left);
|
||||
RtreeDValue left = cellGrowth(pRtree, pLeftBox, &aCell[ii]);
|
||||
RtreeDValue right = cellGrowth(pRtree, pLeftBox, &aCell[ii]);
|
||||
RtreeDValue diff = FABS(right-left);
|
||||
if( iSelect<0 || diff>fDiff ){
|
||||
fDiff = diff;
|
||||
iSelect = ii;
|
||||
@ -1827,13 +1850,13 @@ static void QuadraticPickSeeds(
|
||||
|
||||
int iLeftSeed = 0;
|
||||
int iRightSeed = 1;
|
||||
float fWaste = 0.0;
|
||||
RtreeDValue fWaste = 0.0;
|
||||
|
||||
for(ii=0; ii<nCell; ii++){
|
||||
for(jj=ii+1; jj<nCell; jj++){
|
||||
float right = cellArea(pRtree, &aCell[jj]);
|
||||
float growth = cellGrowth(pRtree, &aCell[ii], &aCell[jj]);
|
||||
float waste = growth - right;
|
||||
RtreeDValue right = cellArea(pRtree, &aCell[jj]);
|
||||
RtreeDValue growth = cellGrowth(pRtree, &aCell[ii], &aCell[jj]);
|
||||
RtreeDValue waste = growth - right;
|
||||
|
||||
if( waste>fWaste ){
|
||||
iLeftSeed = ii;
|
||||
@ -1868,7 +1891,7 @@ static void QuadraticPickSeeds(
|
||||
static void SortByDistance(
|
||||
int *aIdx,
|
||||
int nIdx,
|
||||
float *aDistance,
|
||||
RtreeDValue *aDistance,
|
||||
int *aSpare
|
||||
){
|
||||
if( nIdx>1 ){
|
||||
@ -1894,8 +1917,8 @@ static void SortByDistance(
|
||||
aIdx[iLeft+iRight] = aLeft[iLeft];
|
||||
iLeft++;
|
||||
}else{
|
||||
float fLeft = aDistance[aLeft[iLeft]];
|
||||
float fRight = aDistance[aRight[iRight]];
|
||||
RtreeDValue fLeft = aDistance[aLeft[iLeft]];
|
||||
RtreeDValue fRight = aDistance[aRight[iRight]];
|
||||
if( fLeft<fRight ){
|
||||
aIdx[iLeft+iRight] = aLeft[iLeft];
|
||||
iLeft++;
|
||||
@ -1911,8 +1934,8 @@ static void SortByDistance(
|
||||
{
|
||||
int jj;
|
||||
for(jj=1; jj<nIdx; jj++){
|
||||
float left = aDistance[aIdx[jj-1]];
|
||||
float right = aDistance[aIdx[jj]];
|
||||
RtreeDValue left = aDistance[aIdx[jj-1]];
|
||||
RtreeDValue right = aDistance[aIdx[jj]];
|
||||
assert( left<=right );
|
||||
}
|
||||
}
|
||||
@ -1955,10 +1978,10 @@ static void SortByDimension(
|
||||
memcpy(aSpare, aLeft, sizeof(int)*nLeft);
|
||||
aLeft = aSpare;
|
||||
while( iLeft<nLeft || iRight<nRight ){
|
||||
double xleft1 = DCOORD(aCell[aLeft[iLeft]].aCoord[iDim*2]);
|
||||
double xleft2 = DCOORD(aCell[aLeft[iLeft]].aCoord[iDim*2+1]);
|
||||
double xright1 = DCOORD(aCell[aRight[iRight]].aCoord[iDim*2]);
|
||||
double xright2 = DCOORD(aCell[aRight[iRight]].aCoord[iDim*2+1]);
|
||||
RtreeDValue xleft1 = DCOORD(aCell[aLeft[iLeft]].aCoord[iDim*2]);
|
||||
RtreeDValue xleft2 = DCOORD(aCell[aLeft[iLeft]].aCoord[iDim*2+1]);
|
||||
RtreeDValue xright1 = DCOORD(aCell[aRight[iRight]].aCoord[iDim*2]);
|
||||
RtreeDValue xright2 = DCOORD(aCell[aRight[iRight]].aCoord[iDim*2+1]);
|
||||
if( (iLeft!=nLeft) && ((iRight==nRight)
|
||||
|| (xleft1<xright1)
|
||||
|| (xleft1==xright1 && xleft2<xright2)
|
||||
@ -1976,10 +1999,10 @@ static void SortByDimension(
|
||||
{
|
||||
int jj;
|
||||
for(jj=1; jj<nIdx; jj++){
|
||||
float xleft1 = aCell[aIdx[jj-1]].aCoord[iDim*2];
|
||||
float xleft2 = aCell[aIdx[jj-1]].aCoord[iDim*2+1];
|
||||
float xright1 = aCell[aIdx[jj]].aCoord[iDim*2];
|
||||
float xright2 = aCell[aIdx[jj]].aCoord[iDim*2+1];
|
||||
RtreeDValue xleft1 = aCell[aIdx[jj-1]].aCoord[iDim*2];
|
||||
RtreeDValue xleft2 = aCell[aIdx[jj-1]].aCoord[iDim*2+1];
|
||||
RtreeDValue xright1 = aCell[aIdx[jj]].aCoord[iDim*2];
|
||||
RtreeDValue xright2 = aCell[aIdx[jj]].aCoord[iDim*2+1];
|
||||
assert( xleft1<=xright1 && (xleft1<xright1 || xleft2<=xright2) );
|
||||
}
|
||||
}
|
||||
@ -2006,7 +2029,7 @@ static int splitNodeStartree(
|
||||
|
||||
int iBestDim = 0;
|
||||
int iBestSplit = 0;
|
||||
float fBestMargin = 0.0;
|
||||
RtreeDValue fBestMargin = 0.0;
|
||||
|
||||
int nByte = (pRtree->nDim+1)*(sizeof(int*)+nCell*sizeof(int));
|
||||
|
||||
@ -2027,9 +2050,9 @@ static int splitNodeStartree(
|
||||
}
|
||||
|
||||
for(ii=0; ii<pRtree->nDim; ii++){
|
||||
float margin = 0.0;
|
||||
float fBestOverlap = 0.0;
|
||||
float fBestArea = 0.0;
|
||||
RtreeDValue margin = 0.0;
|
||||
RtreeDValue fBestOverlap = 0.0;
|
||||
RtreeDValue fBestArea = 0.0;
|
||||
int iBestLeft = 0;
|
||||
int nLeft;
|
||||
|
||||
@ -2041,8 +2064,8 @@ static int splitNodeStartree(
|
||||
RtreeCell left;
|
||||
RtreeCell right;
|
||||
int kk;
|
||||
float overlap;
|
||||
float area;
|
||||
RtreeDValue overlap;
|
||||
RtreeDValue area;
|
||||
|
||||
memcpy(&left, &aCell[aaSorted[ii][0]], 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--){
|
||||
RtreeCell *pNext;
|
||||
pNext = PickNext(pRtree, aCell, nCell, pBboxLeft, pBboxRight, aiUsed);
|
||||
float diff =
|
||||
RtreeDValue diff =
|
||||
cellGrowth(pRtree, pBboxLeft, pNext) -
|
||||
cellGrowth(pRtree, pBboxRight, pNext)
|
||||
;
|
||||
@ -2458,32 +2481,34 @@ static int Reinsert(
|
||||
int *aOrder;
|
||||
int *aSpare;
|
||||
RtreeCell *aCell;
|
||||
float *aDistance;
|
||||
RtreeDValue *aDistance;
|
||||
int nCell;
|
||||
float aCenterCoord[RTREE_MAX_DIMENSIONS];
|
||||
RtreeDValue aCenterCoord[RTREE_MAX_DIMENSIONS];
|
||||
int iDim;
|
||||
int ii;
|
||||
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;
|
||||
n = (nCell+1)&(~1);
|
||||
|
||||
/* Allocate the buffers used by this operation. The allocation is
|
||||
** relinquished before this function returns.
|
||||
*/
|
||||
aCell = (RtreeCell *)sqlite3_malloc(nCell * (
|
||||
sizeof(RtreeCell) + /* aCell array */
|
||||
sizeof(int) + /* aOrder array */
|
||||
sizeof(int) + /* aSpare array */
|
||||
sizeof(float) /* aDistance array */
|
||||
aCell = (RtreeCell *)sqlite3_malloc(n * (
|
||||
sizeof(RtreeCell) + /* aCell array */
|
||||
sizeof(int) + /* aOrder array */
|
||||
sizeof(int) + /* aSpare array */
|
||||
sizeof(RtreeDValue) /* aDistance array */
|
||||
));
|
||||
if( !aCell ){
|
||||
return SQLITE_NOMEM;
|
||||
}
|
||||
aOrder = (int *)&aCell[nCell];
|
||||
aSpare = (int *)&aOrder[nCell];
|
||||
aDistance = (float *)&aSpare[nCell];
|
||||
aOrder = (int *)&aCell[n];
|
||||
aSpare = (int *)&aOrder[n];
|
||||
aDistance = (RtreeDValue *)&aSpare[n];
|
||||
|
||||
for(ii=0; ii<nCell; ii++){
|
||||
if( ii==(nCell-1) ){
|
||||
@ -2493,19 +2518,19 @@ static int Reinsert(
|
||||
}
|
||||
aOrder[ii] = ii;
|
||||
for(iDim=0; iDim<pRtree->nDim; iDim++){
|
||||
aCenterCoord[iDim] += (float)DCOORD(aCell[ii].aCoord[iDim*2]);
|
||||
aCenterCoord[iDim] += (float)DCOORD(aCell[ii].aCoord[iDim*2+1]);
|
||||
aCenterCoord[iDim] += DCOORD(aCell[ii].aCoord[iDim*2]);
|
||||
aCenterCoord[iDim] += DCOORD(aCell[ii].aCoord[iDim*2+1]);
|
||||
}
|
||||
}
|
||||
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++){
|
||||
aDistance[ii] = 0.0;
|
||||
for(iDim=0; iDim<pRtree->nDim; iDim++){
|
||||
float coord = (float)(DCOORD(aCell[ii].aCoord[iDim*2+1]) -
|
||||
DCOORD(aCell[ii].aCoord[iDim*2]));
|
||||
RtreeDValue coord = (DCOORD(aCell[ii].aCoord[iDim*2+1]) -
|
||||
DCOORD(aCell[ii].aCoord[iDim*2]));
|
||||
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]. */
|
||||
assert( nData==(pRtree->nDim*2 + 3) );
|
||||
#ifndef SQLITE_RTREE_INT_ONLY
|
||||
if( pRtree->eCoordType==RTREE_COORD_REAL32 ){
|
||||
for(ii=0; ii<(pRtree->nDim*2); ii+=2){
|
||||
cell.aCoord[ii].f = (float)sqlite3_value_double(azData[ii+3]);
|
||||
cell.aCoord[ii+1].f = (float)sqlite3_value_double(azData[ii+4]);
|
||||
cell.aCoord[ii].f = (RtreeValue)sqlite3_value_double(azData[ii+3]);
|
||||
cell.aCoord[ii+1].f = (RtreeValue)sqlite3_value_double(azData[ii+4]);
|
||||
if( cell.aCoord[ii].f>cell.aCoord[ii+1].f ){
|
||||
rc = SQLITE_CONSTRAINT;
|
||||
goto constraint;
|
||||
}
|
||||
}
|
||||
}else{
|
||||
}else
|
||||
#endif
|
||||
{
|
||||
for(ii=0; ii<(pRtree->nDim*2); ii+=2){
|
||||
cell.aCoord[ii].i = sqlite3_value_int(azData[ii+3]);
|
||||
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);
|
||||
|
||||
/* Allocate the sqlite3_vtab structure */
|
||||
nDb = strlen(argv[1]);
|
||||
nName = strlen(argv[2]);
|
||||
nDb = (int)strlen(argv[1]);
|
||||
nName = (int)strlen(argv[2]);
|
||||
pRtree = (Rtree *)sqlite3_malloc(sizeof(Rtree)+nDb+nName+2);
|
||||
if( !pRtree ){
|
||||
return SQLITE_NOMEM;
|
||||
@ -3152,10 +3180,16 @@ static void rtreenode(sqlite3_context *ctx, int nArg, sqlite3_value **apArg){
|
||||
|
||||
nodeGetCell(&tree, &node, ii, &cell);
|
||||
sqlite3_snprintf(512-nCell,&zCell[nCell],"%lld", cell.iRowid);
|
||||
nCell = strlen(zCell);
|
||||
nCell = (int)strlen(zCell);
|
||||
for(jj=0; jj<tree.nDim*2; jj++){
|
||||
sqlite3_snprintf(512-nCell,&zCell[nCell]," %f",(double)cell.aCoord[jj].f);
|
||||
nCell = strlen(zCell);
|
||||
#ifndef SQLITE_RTREE_INT_ONLY
|
||||
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 ){
|
||||
@ -3196,7 +3230,11 @@ int sqlite3RtreeInit(sqlite3 *db){
|
||||
rc = sqlite3_create_function(db, "rtreedepth", 1, utf8, 0,rtreedepth, 0, 0);
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
#ifdef SQLITE_RTREE_INT_ONLY
|
||||
void *c = (void *)RTREE_COORD_INT32;
|
||||
#else
|
||||
void *c = (void *)RTREE_COORD_REAL32;
|
||||
#endif
|
||||
rc = sqlite3_create_module_v2(db, "rtree", &rtreeModule, c, 0);
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
@ -3230,7 +3268,7 @@ static void geomCallback(sqlite3_context *ctx, int nArg, sqlite3_value **aArg){
|
||||
RtreeMatchArg *pBlob;
|
||||
int nBlob;
|
||||
|
||||
nBlob = sizeof(RtreeMatchArg) + (nArg-1)*sizeof(double);
|
||||
nBlob = sizeof(RtreeMatchArg) + (nArg-1)*sizeof(RtreeDValue);
|
||||
pBlob = (RtreeMatchArg *)sqlite3_malloc(nBlob);
|
||||
if( !pBlob ){
|
||||
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->nParam = nArg;
|
||||
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]);
|
||||
#endif
|
||||
}
|
||||
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(
|
||||
sqlite3 *db,
|
||||
const char *zGeom,
|
||||
int (*xGeom)(sqlite3_rtree_geometry *, int, double *, int *),
|
||||
int (*xGeom)(sqlite3_rtree_geometry *, int, RtreeDValue *, int *),
|
||||
void *pContext
|
||||
){
|
||||
RtreeGeomCallback *pGeomCtx; /* Context object for new user-function */
|
||||
|
@ -104,6 +104,18 @@ for {set nCol 1} {$nCol<[llength $cols]} {incr nCol} {
|
||||
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
|
||||
# r-tree tables.
|
||||
#
|
||||
@ -117,8 +129,8 @@ do_test rtree-1.4.1 {
|
||||
do_test rtree-1.4.2 {
|
||||
db close
|
||||
sqlite3 db test.db
|
||||
execsql { SELECT * FROM t1 ORDER BY ii }
|
||||
} {1 5.0 10.0 2 15.0 20.0}
|
||||
execsql_intout { SELECT * FROM t1 ORDER BY ii }
|
||||
} {1 5 10 2 15 20}
|
||||
do_test rtree-1.4.3 {
|
||||
execsql { DROP TABLE t1 }
|
||||
} {}
|
||||
@ -127,12 +139,12 @@ do_test rtree-1.4.3 {
|
||||
# column names.
|
||||
#
|
||||
do_test rtree-1.5.1 {
|
||||
execsql {
|
||||
execsql_intout {
|
||||
CREATE VIRTUAL TABLE t1 USING rtree("the key", "x dim.", "x2'dim");
|
||||
INSERT INTO t1 VALUES(1, 2, 3);
|
||||
SELECT "the key", "x dim.", "x2'dim" FROM t1;
|
||||
}
|
||||
} {1 2.0 3.0}
|
||||
} {1 2 3}
|
||||
do_test rtree-1.5.1 {
|
||||
execsql { DROP TABLE t1 }
|
||||
} {}
|
||||
@ -161,8 +173,8 @@ do_test rtree-2.1.1 {
|
||||
|
||||
do_test rtree-2.1.2 {
|
||||
execsql { INSERT INTO t1 VALUES(NULL, 1, 3, 2, 4) }
|
||||
execsql { SELECT * FROM t1 }
|
||||
} {1 1.0 3.0 2.0 4.0}
|
||||
execsql_intout { SELECT * FROM t1 }
|
||||
} {1 1 3 2 4}
|
||||
do_test rtree-2.1.3 {
|
||||
execsql { INSERT INTO t1 VALUES(NULL, 1, 3, 2, 4) }
|
||||
execsql { SELECT rowid FROM t1 ORDER BY rowid }
|
||||
@ -201,17 +213,17 @@ do_test rtree-3.1.1 {
|
||||
}
|
||||
} {}
|
||||
do_test rtree-3.1.2 {
|
||||
execsql {
|
||||
execsql_intout {
|
||||
INSERT INTO t1 VALUES(5, 1, 3, 2, 4);
|
||||
SELECT * FROM t1;
|
||||
}
|
||||
} {5 1.0 3.0 2.0 4.0}
|
||||
} {5 1 3 2 4}
|
||||
do_test rtree-3.1.3 {
|
||||
execsql {
|
||||
execsql_intout {
|
||||
INSERT INTO t1 VALUES(6, 2, 6, 4, 8);
|
||||
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)):
|
||||
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) }
|
||||
} {}
|
||||
do_test rtree-5.1.2 {
|
||||
execsql {
|
||||
execsql_intout {
|
||||
INSERT INTO t2 VALUES(1, 10, 20);
|
||||
INSERT INTO t2 VALUES(2, 30, 40);
|
||||
INSERT INTO t2 VALUES(3, 50, 60);
|
||||
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 {
|
||||
execsql {
|
||||
execsql_intout {
|
||||
DELETE FROM t2 WHERE ii=2;
|
||||
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 {
|
||||
execsql {
|
||||
execsql_intout {
|
||||
DELETE FROM t2 WHERE ii=1;
|
||||
SELECT * FROM t2 ORDER BY ii;
|
||||
}
|
||||
} {3 50.0 60.0}
|
||||
} {3 50 60}
|
||||
do_test rtree-5.1.5 {
|
||||
execsql {
|
||||
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) }
|
||||
} {}
|
||||
do_test rtree-6.1.2 {
|
||||
execsql {
|
||||
execsql_intout {
|
||||
INSERT INTO t3 VALUES(1, 2, 3, 4, 5);
|
||||
UPDATE t3 SET x2=5;
|
||||
SELECT * FROM t3;
|
||||
}
|
||||
} {1 2.0 5.0 4.0 5.0}
|
||||
} {1 2 5 4 5}
|
||||
do_test rtree-6.1.3 {
|
||||
execsql { UPDATE t3 SET ii = 2 }
|
||||
execsql { SELECT * FROM t3 }
|
||||
} {2 2.0 5.0 4.0 5.0}
|
||||
execsql_intout { SELECT * FROM t3 }
|
||||
} {2 2 5 4 5}
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
# Test cases rtree-7.* test rename operations.
|
||||
@ -286,29 +298,29 @@ do_test rtree-7.1.1 {
|
||||
} {}
|
||||
do_test rtree-7.1.2 {
|
||||
execsql { ALTER TABLE t4 RENAME TO t5 }
|
||||
execsql { SELECT * FROM t5 }
|
||||
} {1 2.0 3.0 4.0 5.0 6.0 7.0}
|
||||
execsql_intout { SELECT * FROM t5 }
|
||||
} {1 2 3 4 5 6 7}
|
||||
do_test rtree-7.1.3 {
|
||||
db close
|
||||
sqlite3 db test.db
|
||||
execsql { SELECT * FROM t5 }
|
||||
} {1 2.0 3.0 4.0 5.0 6.0 7.0}
|
||||
execsql_intout { SELECT * FROM t5 }
|
||||
} {1 2 3 4 5 6 7}
|
||||
do_test rtree-7.1.4 {
|
||||
execsql { ALTER TABLE t5 RENAME TO 'raisara "one"'''}
|
||||
execsql { SELECT * FROM "raisara ""one""'" }
|
||||
} {1 2.0 3.0 4.0 5.0 6.0 7.0}
|
||||
execsql_intout { SELECT * FROM "raisara ""one""'" }
|
||||
} {1 2 3 4 5 6 7}
|
||||
do_test rtree-7.1.5 {
|
||||
execsql { SELECT * FROM 'raisara "one"''' }
|
||||
} {1 2.0 3.0 4.0 5.0 6.0 7.0}
|
||||
execsql_intout { SELECT * FROM 'raisara "one"''' }
|
||||
} {1 2 3 4 5 6 7}
|
||||
do_test rtree-7.1.6 {
|
||||
execsql { ALTER TABLE "raisara ""one""'" RENAME TO "abc 123" }
|
||||
execsql { SELECT * FROM "abc 123" }
|
||||
} {1 2.0 3.0 4.0 5.0 6.0 7.0}
|
||||
execsql_intout { SELECT * FROM "abc 123" }
|
||||
} {1 2 3 4 5 6 7}
|
||||
do_test rtree-7.1.7 {
|
||||
db close
|
||||
sqlite3 db test.db
|
||||
execsql { SELECT * FROM "abc 123" }
|
||||
} {1 2.0 3.0 4.0 5.0 6.0 7.0}
|
||||
execsql_intout { SELECT * FROM "abc 123" }
|
||||
} {1 2 3 4 5 6 7}
|
||||
|
||||
# An error midway through a rename operation.
|
||||
do_test rtree-7.2.1 {
|
||||
@ -318,8 +330,8 @@ do_test rtree-7.2.1 {
|
||||
catchsql { ALTER TABLE "abc 123" RENAME TO t4 }
|
||||
} {1 {SQL logic error or missing database}}
|
||||
do_test rtree-7.2.2 {
|
||||
execsql { SELECT * FROM "abc 123" }
|
||||
} {1 2.0 3.0 4.0 5.0 6.0 7.0}
|
||||
execsql_intout { SELECT * FROM "abc 123" }
|
||||
} {1 2 3 4 5 6 7}
|
||||
do_test rtree-7.2.3 {
|
||||
execsql {
|
||||
DROP TABLE t4_node;
|
||||
@ -330,13 +342,13 @@ do_test rtree-7.2.3 {
|
||||
do_test rtree-7.2.4 {
|
||||
db close
|
||||
sqlite3 db test.db
|
||||
execsql { SELECT * FROM "abc 123" }
|
||||
} {1 2.0 3.0 4.0 5.0 6.0 7.0}
|
||||
execsql_intout { SELECT * FROM "abc 123" }
|
||||
} {1 2 3 4 5 6 7}
|
||||
do_test rtree-7.2.5 {
|
||||
execsql { DROP TABLE t4_rowid }
|
||||
execsql { ALTER TABLE "abc 123" RENAME TO t4 }
|
||||
execsql { SELECT * FROM t4 }
|
||||
} {1 2.0 3.0 4.0 5.0 6.0 7.0}
|
||||
execsql_intout { SELECT * FROM t4 }
|
||||
} {1 2 3 4 5 6 7}
|
||||
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
|
@ -27,21 +27,38 @@ if {[info exists G(isquick)] && $G(isquick)} {
|
||||
set ::NROW 250
|
||||
}
|
||||
|
||||
# Return a floating point number between -X and X.
|
||||
#
|
||||
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
|
||||
#
|
||||
proc randincr {X} {
|
||||
while 1 {
|
||||
set r [expr {int(rand()*$X*32.0)/32.0}]
|
||||
if {$r>0.0} {return $r}
|
||||
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}]
|
||||
}
|
||||
|
||||
# Return a positive floating point number less than or equal to X
|
||||
#
|
||||
proc randincr {X} {
|
||||
while 1 {
|
||||
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.
|
||||
#
|
||||
proc scramble {inlist} {
|
||||
|
@ -49,9 +49,11 @@ do_test rtree5-1.6 {
|
||||
do_test rtree5-1.7 {
|
||||
execsql { SELECT count(*) FROM t1 WHERE x1==5 }
|
||||
} {1}
|
||||
do_test rtree5-1.8 {
|
||||
execsql { SELECT count(*) FROM t1 WHERE x1==5.2 }
|
||||
} {0}
|
||||
ifcapable !rtree_int_only {
|
||||
do_test rtree5-1.8 {
|
||||
execsql { SELECT count(*) FROM t1 WHERE x1==5.2 }
|
||||
} {0}
|
||||
}
|
||||
do_test rtree5-1.9 {
|
||||
execsql { SELECT count(*) FROM t1 WHERE x1==5.0 }
|
||||
} {1}
|
||||
|
@ -16,7 +16,7 @@ if {![info exists testdir]} {
|
||||
}
|
||||
source $testdir/tester.tcl
|
||||
|
||||
ifcapable !rtree {
|
||||
ifcapable {!rtree || rtree_int_only} {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
@ -24,6 +24,18 @@ ifcapable !rtree||!vacuum {
|
||||
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 {
|
||||
execsql {
|
||||
PRAGMA page_size = 1024;
|
||||
@ -32,27 +44,27 @@ do_test rtree7-1.1 {
|
||||
}
|
||||
} {}
|
||||
do_test rtree7-1.2 {
|
||||
execsql { SELECT * FROM rt }
|
||||
} {1 1.0 2.0 3.0 4.0}
|
||||
execsql_intout { SELECT * FROM rt }
|
||||
} {1 1 2 3 4}
|
||||
do_test rtree7-1.3 {
|
||||
execsql {
|
||||
execsql_intout {
|
||||
PRAGMA page_size = 2048;
|
||||
VACUUM;
|
||||
SELECT * FROM rt;
|
||||
}
|
||||
} {1 1.0 2.0 3.0 4.0}
|
||||
} {1 1 2 3 4}
|
||||
do_test rtree7-1.4 {
|
||||
for {set i 2} {$i <= 51} {incr i} {
|
||||
execsql { INSERT INTO rt VALUES($i, 1, 2, 3, 4) }
|
||||
}
|
||||
execsql { SELECT sum(x1), sum(x2), sum(y1), sum(y2) FROM rt }
|
||||
} {51.0 102.0 153.0 204.0}
|
||||
execsql_intout { SELECT sum(x1), sum(x2), sum(y1), sum(y2) FROM rt }
|
||||
} {51 102 153 204}
|
||||
do_test rtree7-1.5 {
|
||||
execsql {
|
||||
execsql_intout {
|
||||
PRAGMA page_size = 512;
|
||||
VACUUM;
|
||||
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
|
||||
|
@ -17,6 +17,7 @@ if {![info exists testdir]} {
|
||||
}
|
||||
source $testdir/tester.tcl
|
||||
ifcapable !rtree { finish_test ; return }
|
||||
ifcapable rtree_int_only { finish_test; return }
|
||||
|
||||
register_cube_geom db
|
||||
|
||||
|
@ -18,17 +18,30 @@ if {![info exists testdir]} {
|
||||
source $testdir/tester.tcl
|
||||
ifcapable !rtree { finish_test ; return }
|
||||
|
||||
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}}}
|
||||
|
||||
ifcapable rtree_int_only {
|
||||
do_test rtreeB-1.1-intonly {
|
||||
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 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
|
||||
|
@ -31,7 +31,11 @@ typedef struct sqlite3_rtree_geometry sqlite3_rtree_geometry;
|
||||
int sqlite3_rtree_geometry_callback(
|
||||
sqlite3 *db,
|
||||
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
|
||||
);
|
||||
|
||||
|
1
main.mk
1
main.mk
@ -601,6 +601,7 @@ clean:
|
||||
rm -f mkkeywordhash mkkeywordhash.exe keywordhash.h
|
||||
rm -f $(PUBLISH)
|
||||
rm -f *.da *.bb *.bbg gmon.out
|
||||
rm -rf quota2a quota2b quota2c
|
||||
rm -rf tsrc target_source
|
||||
rm -f testloadext.dll libtestloadext.so
|
||||
rm -f amalgamation-testfixture amalgamation-testfixture.exe
|
||||
|
412
manifest
412
manifest
@ -1,37 +1,29 @@
|
||||
C Version\s3.7.10
|
||||
D 2012-01-16T13:28:40.069
|
||||
C Version\s3.7.12
|
||||
D 2012-05-14T01:41:23.304
|
||||
F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
|
||||
F Makefile.in 3f79a373e57c3b92dabf76f40b065e719d31ac34
|
||||
F Makefile.in 2f37e468503dbe79d35c9f6dffcf3fae1ae9ec20
|
||||
F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
|
||||
F Makefile.msc dcad80fa69f17d46fe6778ba873fc108ca16298d
|
||||
F Makefile.vxworks 1deb39c8bb047296c30161ffa10c1b5423e632f9
|
||||
F Makefile.msc 7849a871b6cdb20fd51baee6bbe5965a03326be4
|
||||
F Makefile.vxworks 3b7fe7a0571fdadc61363ebc1b23732d2d6363ca
|
||||
F README cd04a36fbc7ea56932a4052d7d0b7f09f27c33d6
|
||||
F VERSION af03cd6400f9d71d38bdb7a9d66a1aefdc2f3e0d
|
||||
F VERSION f9313d88cb77df8617059a88eb382291321ef6bc
|
||||
F aclocal.m4 a5c22d164aff7ed549d53a90fa56d56955281f50
|
||||
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.ico af56c1d00fee7cd4753e8631ed60703ed0fc6e90
|
||||
F art/sqlite370.jpg d512473dae7e378a67e28ff96a34da7cb331def2
|
||||
F art/src_logo.gif 9341ef09f0e53cd44c0c9b6fc3c16f7f3d6c2ad9
|
||||
F config.guess 226d9a188c6196f3033ffc651cbc9dcee1a42977
|
||||
F config.h.in 31cc8c4943f56e60c4aa4fba929c9d4c70e418b4
|
||||
F config.h.in 0921066a13130082764ab4ab6456f7b5bebe56de
|
||||
F config.sub 9ebe4c3b3dab6431ece34f16828b594fb420da55
|
||||
F configure 5c1cf496e63a233d2abcccf7a3570ba84d5dce01 x
|
||||
F configure.ac 75323bdac56fb0e69f6a3fc5b23f24359550b9d9
|
||||
F configure eb9e5e7f4c1601b5acf674a724e1a778481d2835 x
|
||||
F configure.ac 9ee886c21c095b3272137b1553ae416c8b8c8557
|
||||
F contrib/sqlitecon.tcl 210a913ad63f9f991070821e599d600bd913e0ad
|
||||
F doc/lemon.html 3091574143dd3415669b6745843ff8d011d33549
|
||||
F doc/pager-invariants.txt 870107036470d7c419e93768676fae2f8749cf9e
|
||||
F doc/vfs-shm.txt e101f27ea02a8387ce46a05be2b1a902a021d37a
|
||||
F ext/README.txt 913a7bd3f4837ab14d7e063304181787658b14e1
|
||||
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/fts1/README.txt 20ac73b006a70bcfd80069bdaf59214b6cf1db5e
|
||||
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.tokenizers 998756696647400de63d5ba60e9655036cb966e9
|
||||
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/fts3Int.h def7a900f98c5ab5fa4772e922bfa219d5097f05
|
||||
F ext/fts3/fts3_aux.c 0ebfa7b86cf8ff6a0861605fcc63b83ec1b70691
|
||||
F ext/fts3/fts3_expr.c f5df26bddf46a5916b2a5f80c4027996e92b7b15
|
||||
F ext/fts3/fts3Int.h aca752b99c15ee738f5bcf0910eafb9e4aeb1b97
|
||||
F ext/fts3/fts3_aux.c 5205182bd8f372782597888156404766edf5781e
|
||||
F ext/fts3/fts3_expr.c dbc7ba4c3a6061adde0f38ed8e9b349568299551
|
||||
F ext/fts3/fts3_hash.c 8dd2d06b66c72c628c2732555a32bc0943114914
|
||||
F ext/fts3/fts3_hash.h 8331fb2206c609f9fc4c4735b9ab5ad6137c88ec
|
||||
F ext/fts3/fts3_icu.c 6c8f395cdf9e1e3afa7fadb7e523dbbf381c6dfa
|
||||
F ext/fts3/fts3_porter.c b7e5276f9f0a5fc7018b6fa55ce0f31f269ef881
|
||||
F ext/fts3/fts3_snippet.c 1f9ee6a8e0e242649645968dcec4deb253d86c2a
|
||||
F ext/fts3/fts3_term.c a5457992723455a58804cb75c8cbd8978db5c2ef
|
||||
F ext/fts3/fts3_test.c 24fa13f330db011500acb95590da9eee24951894
|
||||
F ext/fts3/fts3_tokenizer.c 9ff7ec66ae3c5c0340fa081958e64f395c71a106
|
||||
F ext/fts3/fts3_tokenizer.h 13ffd9fcb397fec32a05ef5cd9e0fa659bf3dbd3
|
||||
F ext/fts3/fts3_tokenizer1.c 0dde8f307b8045565cf63797ba9acfaff1c50c68
|
||||
F ext/fts3/fts3_write.c fdf0c99830360146ec7128150271c8c014a8fef7
|
||||
F ext/fts3/fts3_icu.c 62ec177c55f6a5c6e994dd3e5fd3194b4045c347
|
||||
F ext/fts3/fts3_porter.c a465b49fcb8249a755792f87516eff182efa42b3
|
||||
F ext/fts3/fts3_snippet.c bf67520ae9d2352a65368ed101729ff701c08808
|
||||
F ext/fts3/fts3_term.c a521f75132f9a495bdca1bdd45949b3191c52763
|
||||
F ext/fts3/fts3_test.c 348f7d08cae05285794e23dc4fe8b8fdf66e264a
|
||||
F ext/fts3/fts3_tokenizer.c 3da7254a9881f7e270ab28e2004e0d22b3212bce
|
||||
F ext/fts3/fts3_tokenizer.h 66dec98e365854b6cd2d54f1a96bb6d428fc5a68
|
||||
F ext/fts3/fts3_tokenizer1.c 5c98225a53705e5ee34824087478cf477bdb7004
|
||||
F ext/fts3/fts3_write.c cd4af00b3b0512b4d76177a267fcaafab44cbce4
|
||||
F ext/fts3/fts3speed.tcl b54caf6a18d38174f1a6e84219950d85e98bb1e9
|
||||
F ext/fts3/mkfts3amal.tcl 252ecb7fe6467854f2aa237bf2c390b74e71f100
|
||||
F ext/fts3/tool/fts3view.c 6cfc5b67a5f0e09c0d698f9fd012c784bfaa9197
|
||||
F ext/icu/README.txt bf8461d8cdc6b8f514c080e4e10dc3b2bbdfefa9
|
||||
F ext/icu/icu.c eb9ae1d79046bd7871aa97ee6da51eb770134b5a
|
||||
F ext/icu/sqliteicu.h 728867a802baa5a96de7495e9689a8e01715ef37
|
||||
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/rtree1.test 28e1b8da4da98093ce3210187434dd760a8d89d8
|
||||
F ext/rtree/rtree1.test e474a2b5eff231496dbd073fe67e5fbaf7f444c9
|
||||
F ext/rtree/rtree2.test acbb3a4ce0f4fbc2c304d2b4b784cfa161856bba
|
||||
F ext/rtree/rtree3.test a494da55c30ee0bc9b01a91c80c81b387b22d2dc
|
||||
F ext/rtree/rtree4.test 0061e6f464fd3dc6a79f82454c5a1c3dadbe42af
|
||||
F ext/rtree/rtree5.test ce3d7ccae2cfd9d2e1052b462424964c9bdcda12
|
||||
F ext/rtree/rtree6.test 0b380bd9af93f3bc496eef42502a336f58949c1b
|
||||
F ext/rtree/rtree7.test bcb647b42920b3b5d025846689147778485cc318
|
||||
F ext/rtree/rtree4.test c8fe384f60ebd49540a5fecc990041bf452eb6e0
|
||||
F ext/rtree/rtree5.test 9a229678a00f40e6aedb40cb3a07ec5444af892c
|
||||
F ext/rtree/rtree6.test 3ff9113b4a872fa935309e3511cd9b7cdb4d2472
|
||||
F ext/rtree/rtree7.test 1fa710b9e6bf997a0c1a537b81be7bb6fded1971
|
||||
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/rtreeB.test b1916a9cecb86b02529c4cc5a546e8d6e7ff10da
|
||||
F ext/rtree/rtreeB.test 983e567b49b5dca165940f66b87e161aa30e82b2
|
||||
F ext/rtree/rtree_perf.tcl 6c18c1f23cd48e0f948930c98dfdd37dfccb5195
|
||||
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/viewrtree.tcl eea6224b3553599ae665b239bd827e182b466024
|
||||
F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x
|
||||
F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8
|
||||
F main.mk ac48970ca7506c9034f5c7b2212111fbeb0a1aaa
|
||||
F main.mk a80771d44176a0c744d9d4e048497e7ed0b4040d
|
||||
F mkdll.sh 7d09b23c05d56532e9d44a50868eb4b12ff4f74a
|
||||
F mkextu.sh 416f9b7089d80e5590a29692c9d9280a10dbad9f
|
||||
F mkextw.sh 4123480947681d9b434a5e7b1ee08135abe409ac
|
||||
@ -118,39 +111,39 @@ F spec.template 86a4a43b99ebb3e75e6b9a735d5fd293a24e90ca
|
||||
F sqlite.pc.in 42b7bf0d02e08b9e77734a47798d1a55a9e0716b
|
||||
F sqlite3.1 6be1ad09113570e1fc8dcaff84c9b0b337db5ffc
|
||||
F sqlite3.pc.in ae6f59a76e862f5c561eb32a380228a02afc3cad
|
||||
F src/alter.c ac80a0f31189f8b4a524ebf661e47e84536ee7f5
|
||||
F src/analyze.c f32ff304da413851eefa562b04e61ff6cb88248b
|
||||
F src/alter.c 149cc80d9257971b0bff34e58fb2263e01998289
|
||||
F src/analyze.c 70c46504c0d2543ea5cdca01140b2cd3e1d886e7
|
||||
F src/attach.c 12c6957996908edc31c96d7c68d4942c2474405f
|
||||
F src/auth.c 523da7fb4979469955d822ff9298352d6b31de34
|
||||
F src/backup.c e9538bad2d4a4fcd4308f1aed7cb18a0fbc968f9
|
||||
F src/backup.c 6be23a344d3301ae38e92fddb3a33b91c309fce4
|
||||
F src/bitvec.c af50f1c8c0ff54d6bdb7a80e2fceca5a93670bef
|
||||
F src/btmutex.c 976f45a12e37293e32cae0281b15a21d48a8aaa7
|
||||
F src/btree.c a65816cc000bdd421772986e64c88c9035331332
|
||||
F src/btree.h f5d775cd6cfc7ac32a2535b70e8d2af48ef5f2ce
|
||||
F src/btreeInt.h 6c9960645c431c9456ca56498f43a2b3bf1fa8c2
|
||||
F src/build.c 8e2a4dedad860fed982270ef43968505f35ec57f
|
||||
F src/callback.c 0425c6320730e6d3981acfb9202c1bed9016ad1a
|
||||
F src/btree.c df800f10896bc2ddaa1125c532d6e7a7b9efc532
|
||||
F src/btree.h 48a013f8964f12d944d90e4700df47b72dd6d923
|
||||
F src/btreeInt.h 38a639c0542c29fe8331a221c4aed0cb8686249e
|
||||
F src/build.c 987c6933ea170e443dc6a79d52f8d2506206b12b
|
||||
F src/callback.c 0cb4228cdcd827dcc5def98fb099edcc9142dbcd
|
||||
F src/complete.c dc1d136c0feee03c2f7550bafc0d29075e36deac
|
||||
F src/ctime.c a9c26822515f81ec21588cbb482ca6724be02e33
|
||||
F src/date.c 067a81c9942c497aafd2c260e13add8a7d0c7dd4
|
||||
F src/delete.c 51d32f0a9c880663e54ce309f52e40c325d5e112
|
||||
F src/expr.c 537591e95eac74af783e4eb033954fb218cf398e
|
||||
F src/delete.c 4c20ea4f6213b3bc1c6a510586864b679946e05e
|
||||
F src/expr.c eefabaa4a3dc67309a754eb0eab1a163ff4c2bf3
|
||||
F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb
|
||||
F src/fkey.c 657212460bf5cfd3ae607d12ea62092844c227b5
|
||||
F src/func.c 6261ce00aad9c63cd5b4219249b05683979060e9
|
||||
F src/func.c c6b3c94320253a35bda43fb69cc292618e3285d6
|
||||
F src/global.c 4cfdca5cb0edd33c4d021baec4ede958cb2c793b
|
||||
F src/hash.c 458488dcc159c301b8e7686280ab209f1fb915af
|
||||
F src/hash.h 2894c932d84d9f892d4b4023a75e501f83050970
|
||||
F src/hwtime.h d32741c8f4df852c7d959236615444e2b1063b08
|
||||
F src/insert.c d7c69718acbb92e10e4b121da7bed13903342962
|
||||
F src/insert.c 0bbffe75c254c62a5686ab5e7f88e29235e16174
|
||||
F src/journal.c 552839e54d1bf76fb8f7abe51868b66acacf6a0e
|
||||
F src/legacy.c a199d7683d60cef73089e892409113e69c23a99f
|
||||
F src/lempar.c 0ee69fca0be54cd93939df98d2aca4ca46f44416
|
||||
F src/loadext.c f20382fbaeec832438a1ba7797bee3d3c8a6d51d
|
||||
F src/main.c e60abee4a7ca3da31b67745ccf02b8d29f138614
|
||||
F src/main.c 91458c713e9b7f8dbc98d79e78f1150f0ca9c2a1
|
||||
F src/malloc.c 15afac5e59b6584efe072e9933aefb4230e74f97
|
||||
F src/mem0.c 6a55ebe57c46ca1a7d98da93aaa07f99f1059645
|
||||
F src/mem1.c 7998e7003a3047e323c849a26dda004debc04d03
|
||||
F src/mem1.c b3677415e69603d6a0e7c5410a1b3731d55beda1
|
||||
F src/mem2.c e307323e86b5da1853d7111b68fd6b84ad6f09cf
|
||||
F src/mem3.c 61c9d47b792908c532ca3a62b999cf21795c6534
|
||||
F src/mem5.c c2c63b7067570b00bf33d751c39af24182316f7f
|
||||
@ -163,102 +156,103 @@ F src/mutex_unix.c c3a4e00f96ba068a8dbef34084465979aaf369cc
|
||||
F src/mutex_w32.c 5e54f3ba275bcb5d00248b8c23107df2e2f73e33
|
||||
F src/notify.c 976dd0f6171d4588e89e874fcc765e92914b6d30
|
||||
F src/os.c e1acdc09ff3ac2412945cca9766e2dcf4675f31c
|
||||
F src/os.h a2219c3b05ce31230bb000fdc4f1a542b33ee649
|
||||
F src/os.h 59beba555b65a450bd1d804220532971d4299f60
|
||||
F src/os_common.h 92815ed65f805560b66166e3583470ff94478f04
|
||||
F src/os_os2.c 4a75888ba3dfc820ad5e8177025972d74d7f2440
|
||||
F src/os_unix.c 657672fab2580a84116c140b36ee3d6b6fc75b4e
|
||||
F src/os_win.c 5ac061ae1326a71500cee578ed0fd9113b4f6a37
|
||||
F src/pager.c 4d58c983d9f4d34bc2d48e4280361ccaeecd03c5
|
||||
F src/pager.h 5cd760857707529b403837d813d86b68938d6183
|
||||
F src/parse.y fabb2e7047417d840e6fdb3ef0988a86849a08ba
|
||||
F src/os_unix.c 424d46e0edab969293c2223f09923b2178171f47
|
||||
F src/os_win.c 412d6434133c7c81dc48b7702f3ea5e61c309e5c
|
||||
F src/pager.c bb5635dde0b152797836d1c72275284724bb563c
|
||||
F src/pager.h ef1eaf8593e78f73885c1dfac27ad83bee23bdc5
|
||||
F src/parse.y f29df90bd3adc64b33114ab1de9fb7768fcf2099
|
||||
F src/pcache.c f8043b433a57aba85384a531e3937a804432a346
|
||||
F src/pcache.h b1d8775a9bddf44e65edb0d20bfc57a4982f840f
|
||||
F src/pcache1.c 281822d22265245b19f908cb3f5df725f7e11b06
|
||||
F src/pragma.c fb979b7b5103ad0db1b72bcf349c83f5dec62574
|
||||
F src/pcache.h 1b5dcc3dc8103d03e625b177023ee67764fa6b7c
|
||||
F src/pcache1.c b30b1c35908346ecc43d8d9d17f2ddf6817f8f60
|
||||
F src/pragma.c e708b3bb5704605816f617e0b1d63a5488060715
|
||||
F src/prepare.c ec4989f7f480544bdc4192fe663470d2a2d7d61e
|
||||
F src/printf.c 7ffb4ebb8b341f67e049695ba031da717b3d2699
|
||||
F src/random.c cd4a67b3953b88019f8cd4ccd81394a8ddfaba50
|
||||
F src/resolve.c 3d3e80a98f203ac6b9329e9621e29eda85ddfd40
|
||||
F src/rowset.c 69afa95a97c524ba6faf3805e717b5b7ae85a697
|
||||
F src/select.c a1d075db66a0ea42807353501b62997969e5be79
|
||||
F src/shell.c aa4183d4a5243d8110b1d3d77faa4aea7e9c9c2d
|
||||
F src/sqlite.h.in 53516617d2945a411d028674d7fa20dd394b9ec0
|
||||
F src/resolve.c 748e75299faff345f34f0e5bd02a2bac8aa69fcd
|
||||
F src/rowset.c f6a49f3e9579428024662f6e2931832511f831a1
|
||||
F src/select.c d7b9018b7dd2e821183d69477ab55c39b8272335
|
||||
F src/shell.c 04399b2f9942bd02ed5ffee3b84bcdb39c52a1e6
|
||||
F src/sqlite.h.in 4f4d4792f6fb00387c877af013cb09d955643f12
|
||||
F src/sqlite3ext.h 6904f4aadf976f95241311fbffb00823075d9477
|
||||
F src/sqliteInt.h b8fdd9c39c8d7f5c794f4ea917293d9c75b9aff2
|
||||
F src/sqliteInt.h c5e917c4f1453f3972b1fd0c81105dfe4f09cc32
|
||||
F src/sqliteLimit.h 164b0e6749d31e0daa1a4589a169d31c0dec7b3d
|
||||
F src/status.c 4568e72dfd36b6a5911f93457364deb072e0b03a
|
||||
F src/status.c 35939e7e03abf1b7577ce311f48f682c40de3208
|
||||
F src/table.c 2cd62736f845d82200acfa1287e33feb3c15d62e
|
||||
F src/tclsqlite.c 2aeb69958965dad0842d5ea1456f1a958ef296e6
|
||||
F src/test1.c 1b1e514e85ffe7152b02cba38bd0a1ce8cd56113
|
||||
F src/test2.c 711555927f1f7e8db9aab86b512bc6934a774abe
|
||||
F src/test3.c 91d3f1a09cfae3533ef17d8b484a160f3d1f1a21
|
||||
F src/tclsqlite.c fe5406573e1527957e00dcaf51edd9d8bd31b918
|
||||
F src/test1.c a808bfa548a501b513579bdbaf83901c98e059c9
|
||||
F src/test2.c 4178056dd1e7d70f954ad8a1e3edb71a2a784daf
|
||||
F src/test3.c 3c3c2407fa6ec7a19e24ae23f7cb439d0275a60d
|
||||
F src/test4.c d1e5a5e904d4b444cf572391fdcb017638e36ff7
|
||||
F src/test5.c e1a19845625144caf038031234a12185e40d315c
|
||||
F src/test6.c cf6ab27a59e1ab64b011bb251ba600131e803e59
|
||||
F src/test5.c a6d1ac55ac054d0b2b8f37b5e655b6c92645a013
|
||||
F src/test6.c 417e1e214734393c24a8ee80b41485a9c4169123
|
||||
F src/test7.c 2e0781754905c8adc3268d8f0967e7633af58843
|
||||
F src/test8.c 99f70341d6ec480313775127f4cd14b4a47db557
|
||||
F src/test8.c 5ecbffe6712da81d5d10454e9d77d6c5bac95fe8
|
||||
F src/test9.c bea1e8cf52aa93695487badedd6e1886c321ea60
|
||||
F src/test_async.c 0612a752896fad42d55c3999a5122af10dcf22ad
|
||||
F src/test_autoext.c 30e7bd98ab6d70a62bb9ba572e4c7df347fe645e
|
||||
F src/test_backup.c c129c91127e9b46e335715ae2e75756e25ba27de
|
||||
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_devsym.c e7498904e72ba7491d142d5c83b476c4e76993bc
|
||||
F src/test_func.c 6232d722a4ddb193035aa13a03796bf57d6c12fd
|
||||
F src/test_fuzzer.c f884f6f32e8513d34248d6e1ac8a32047fead254
|
||||
F src/test_hexio.c c4773049603151704a6ab25ac5e936b5109caf5a
|
||||
F src/test_func.c 090f2c3339e85c2c964435f99aed6f3da9d59525
|
||||
F src/test_fuzzer.c 1d26aa965120420bc14807da29d4d4541bfa6148
|
||||
F src/test_hexio.c abfdecb6fa58c354623978efceb088ca18e379cd
|
||||
F src/test_init.c 3cbad7ce525aec925f8fda2192d576d47f0d478a
|
||||
F src/test_intarray.c d879bbf8e4ce085ab966d1f3c896a7c8b4f5fc99
|
||||
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_malloc.c 8d416f29ad8573f32601f6056c9d2b17472e9ad5
|
||||
F src/test_multiplex.c afab2c9d292677ab31e0dd4b3dde2420fb655c5f
|
||||
F src/test_malloc.c 3f5903a1528fd32fe4c472a3bd0259128d8faaef
|
||||
F src/test_multiplex.c 66dcfca001ee22f04ef31ad353772ed05a017e53
|
||||
F src/test_multiplex.h e99c571bc4968b7a9363b661481f3934bfead61d
|
||||
F src/test_mutex.c a6bd7b9cf6e19d989e31392b06ac8d189f0d573e
|
||||
F src/test_onefile.c 40cf9e212a377a6511469384a64b01e6e34b2eec
|
||||
F src/test_osinst.c 6abf0a37ce831120c4ef1b913afdd813e7ac1a73
|
||||
F src/test_onefile.c 0396f220561f3b4eedc450cef26d40c593c69a25
|
||||
F src/test_osinst.c 90a845c8183013d80eccb1f29e8805608516edba
|
||||
F src/test_pcache.c a5cd24730cb43c5b18629043314548c9169abb00
|
||||
F src/test_quota.c b4a6519417d87870e7ef5838dbf3cae164dcc28d
|
||||
F src/test_quota.h 9ffa1d3ad6d0a6a24e8670ea64b909c717ec3358
|
||||
F src/test_rtree.c 6d06306e29946dc36f528a3a2cdc3add794656f1
|
||||
F src/test_quota.c 9d6be9cd3bb132df2b964193b6a4ec850f50a210
|
||||
F src/test_quota.h ee5da2ae7f84d1c8e0e0e2ab33f01d69f10259b5
|
||||
F src/test_rtree.c aba603c949766c4193f1068b91c787f57274e0d9
|
||||
F src/test_schema.c 8c06ef9ddb240c7a0fcd31bc221a6a2aade58bf0
|
||||
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_syscall.c a992d8c80ea91fbf21fb2dd570db40e77dd7e6ae
|
||||
F src/test_tclvar.c f4dc67d5f780707210d6bb0eb6016a431c04c7fa
|
||||
F src/test_thread.c 35022393dd54d147b998b6b7f7e945b01114d666
|
||||
F src/test_vfs.c 07157a0bbfe161cb5e32cad2079abd26cd611c4b
|
||||
F src/test_vfstrace.c 065c7270a614254b2c68fbc7ba8d1fb1d5cbc823
|
||||
F src/test_wholenumber.c 6129adfbe7c7444f2e60cc785927f3aa74e12290
|
||||
F src/test_thread.c e286f2173563f2a1747c24bcda6b9d030bf4f4e4
|
||||
F src/test_vfs.c 9d934e111021d56c629efc73a796648c9519ad12
|
||||
F src/test_vfstrace.c 6b28adb2a0e8ecd0f2e3581482e1f658b11b4067
|
||||
F src/test_wholenumber.c 3d2b9ed1505c40ad5c5ca2ad16ae7a289d6cc251
|
||||
F src/test_wsd.c 41cadfd9d97fe8e3e4e44f61a4a8ccd6f7ca8fe9
|
||||
F src/tokenize.c 1e86210d3976717a19238ea7b047fac481fe8c12
|
||||
F src/trigger.c ee7e178fb9188f44b532cebd449a7c1df90fb684
|
||||
F src/update.c d3076782c887c10e882996550345da9c4c9f9dea
|
||||
F src/utf.c 890c67dcfcc7a74623c95baac7535aadfe265e84
|
||||
F src/util.c 9e07bd67dfafe9c75b1da78c87ba030cebbb5388
|
||||
F src/vacuum.c 0c0ba2242355c6048d65e2b333abe0f7c06348fa
|
||||
F src/vdbe.c 64e3fc0bb56c54ceac3cc1d1e65257bfda21550e
|
||||
F src/util.c 4f6cfad661b2e3454b0cdd5b1b9d39a54942d0e3
|
||||
F src/vacuum.c bfd53f9bd20a8fdb70b0fa8e77182b866875c0d8
|
||||
F src/vdbe.c e1d26b98288889c22f00cf4851ec351ee67ad8b9
|
||||
F src/vdbe.h 18f581cac1f4339ec3299f3e0cc6e11aec654cdb
|
||||
F src/vdbeInt.h ef9b8584b23b033894a0804dc6b90196c6779fb9
|
||||
F src/vdbeInt.h 6ff4180a05683566a8835d12f7ec504b22932c82
|
||||
F src/vdbeapi.c 3662b6a468a2a4605a15dfab313baa6dff81ad91
|
||||
F src/vdbeaux.c eb13a6917ed7455b5b49236fe5cfb3d3c3e4c57b
|
||||
F src/vdbeaux.c d52c8a424fdd4b1d5cf1ac93cc7cd20da023ec5c
|
||||
F src/vdbeblob.c 32f2a4899d67f69634ea4dd93e3f651936d732cb
|
||||
F src/vdbemem.c 4f7d25d5ea2e2040254095b8f6de07f8dbbadf80
|
||||
F src/vdbesort.c 468d43c057063e54da4f1988b38b4f46d60e7790
|
||||
F src/vdbemem.c cb55e84b8e2c15704968ee05f0fae25883299b74
|
||||
F src/vdbesort.c b25814d385895544ebc8118245c8311ded7f81c9
|
||||
F src/vdbetrace.c d6e50e04e1ec498150e519058f617d91b8f5c843
|
||||
F src/vtab.c e9318d88feac85be8e27ee783ac8f5397933fc8a
|
||||
F src/wal.c 5f7bcc0610af759953defd769eacebfd98a22bc8
|
||||
F src/wal.h eaa00b9a403ddda2b56d01b7afc19ef600f9363f
|
||||
F src/vtab.c ae657b1c22cff43863458e768a44f915c07bc0e4
|
||||
F src/wal.c 7bb3ad807afc7973406c805d5157ec7a2f65e146
|
||||
F src/wal.h 29c197540b19044e6cd73487017e5e47a1d3dac6
|
||||
F src/walker.c 3112bb3afe1d85dc52317cb1d752055e9a781f8f
|
||||
F src/where.c af623942514571895818b9b7ae11db95ae3b3d88
|
||||
F src/where.c 24c7494d8875ead994b4dfe5461340c27fd424ca
|
||||
F test/8_3_names.test 631ea964a3edb091cf73c3b540f6bcfdb36ce823
|
||||
F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2
|
||||
F test/alias.test 4529fbc152f190268a15f9384a5651bbbabc9d87
|
||||
F test/all.test 52fc8dee494092031a556911d404ca30a749a30b
|
||||
F test/alter.test 66f5818f9848c4f22de022a345fae25bcd30f8fb
|
||||
F test/alter.test 57d96ec9b320bd07af77567034488dcb6642c748
|
||||
F test/alter2.test 7ea05c7d92ac99349a802ef7ada17294dd647060
|
||||
F test/alter3.test 49c9d9fba2b8fcdce2dedeca97bbf1f369cc548d
|
||||
F test/alter4.test b2debc14d8cbe4c1d12ccd6a41eef88a8c1f15d5
|
||||
@ -288,16 +282,17 @@ F test/autoindex1.test 058d0b331ae6840a61bbee910d8cbae27bfd5991
|
||||
F test/autovacuum.test fcaf4616ae5bb18098db1cb36262565e5c841c3c
|
||||
F test/autovacuum_ioerr2.test 8a367b224183ad801e0e24dcb7d1501f45f244b4
|
||||
F test/avtrans.test 0252654f4295ddda3b2cce0e894812259e655a85
|
||||
F test/backcompat.test 71eeb75ea567c060774c4e8db4b0e703f21c7677
|
||||
F test/backup.test 6970614b002b056ae5bab5b76559905e02b6f0b2
|
||||
F test/backcompat.test bccbc64769d9c755ad65ee7c2f7336b86e3cc0c8
|
||||
F test/backup.test 717346db953e9e435c2a94916e4af177330d60d3
|
||||
F test/backup2.test 34986ef926ea522911a51dfdb2f8e99b7b75ebcf
|
||||
F test/backup_ioerr.test 40d208bc9224b666ee3ed423f49bc9062a36a9d0
|
||||
F test/backup_malloc.test 7162d604ec2b4683c4b3799a48657fb8b5e2d450
|
||||
F test/badutf.test d5360fc31f643d37a973ab0d8b4fb85799c3169f
|
||||
F test/badutf2.test f5bc7f2d280670ecd79b9cf4f0f1760c607fe51f
|
||||
F test/bc_common.tcl 5c8689cc6d2fb44b7c0968ae4f85eb26d50022fa
|
||||
F test/between.test 16b1776c6323faadb097a52d673e8e3d8be7d070
|
||||
F test/bigfile.test a8ec8073a20207456dab01a29ad9cde42b0dd103
|
||||
F test/bigfile2.test f8e83eca9abef60692a34255a2ebcb96aff897fc
|
||||
F test/bigfile.test 82dfe93ee7eb9e2e05641afa2b39ffd947a92ff1
|
||||
F test/bigfile2.test 852f948cb492aadab45b58f4d2f3b0832a115cb0
|
||||
F test/bigrow.test f0aeb7573dcb8caaafea76454be3ade29b7fc747
|
||||
F test/bind.test 3c7b320969000c441a70952b0b15938fbb66237c
|
||||
F test/bindxfer.test efecd12c580c14df5f4ad3b3e83c667744a4f7e0
|
||||
@ -312,15 +307,15 @@ F test/boundary3.test 56ef82096b4329aca2be74fa1e2b0f762ea0eb45
|
||||
F test/boundary4.tcl 0bb4b1a94f4fc5ae59b79b9a2b7a140c405e2983
|
||||
F test/boundary4.test 89e02fa66397b8a325d5eb102b5806f961f8ec4b
|
||||
F test/busy.test 76b4887f8b9160ba903c1ac22e8ff406ad6ae2f0
|
||||
F test/cache.test 754baab2f18089fc9bcba7afaeb4dc907c6c6de2
|
||||
F test/cache.test f64136b0893c293d0b910ed057b3b711249099a7
|
||||
F test/capi2.test 835d4cee9f542ea50fa8d01f3fe6de80b0627360
|
||||
F test/capi3.test 7200dff6acb17b9a4b6f9918f554eaae04968ddd
|
||||
F test/capi3.test 8dedb0050610e9ff95cd9d487beb0ce5f33a31ee
|
||||
F test/capi3b.test efb2b9cfd127efa84433cd7a2d72ce0454ae0dc4
|
||||
F test/capi3c.test ccf0acf045dbacd09f6229aa4efed670aaba76a9
|
||||
F test/capi3c.test 01f197d73f4d4d66316483662f475cab7ab5bd60
|
||||
F test/capi3d.test 17b57ca28be3e37e14c2ba8f787d292d84b724a1
|
||||
F test/capi3e.test f7408dda65c92b9056199fdc180f893015f83dde
|
||||
F test/cast.test 4c275cbdc8202d6f9c54a3596701719868ac7dc3
|
||||
F test/check.test db2b29d557544347d28e25b8406f5d5ecc3d1bc3
|
||||
F test/check.test 193f47ed43a8d29aca12b30cd30ceb105fbe710d
|
||||
F test/coalesce.test cee0dccb9fbd2d494b77234bccf9dc6c6786eb91
|
||||
F test/collate1.test e3eaa48c21e150814be1a7b852d2a8af24458d04
|
||||
F test/collate2.test 04cebe4a033be319d6ddbb3bbc69464e01700b49
|
||||
@ -355,7 +350,7 @@ F test/crash.test 519dc29f6fea151f015a23236e555239353946eb
|
||||
F test/crash2.test 5b14d4eb58b880e231361d3b609b216acda86651
|
||||
F test/crash3.test 8f5de9d32ab9ab95475a9efe7f47a940aa889418
|
||||
F test/crash4.test fe2821baf37168dc59dd733dcf7dba2a401487bc
|
||||
F test/crash5.test 69226a1b948d8961395b7ad2a1df084c212ce8cf
|
||||
F test/crash5.test 13b9ca94e048194bca070e26160ce76b406c56be
|
||||
F test/crash6.test 4c56f1e40d0291e1110790a99807aa875b1647ba
|
||||
F test/crash7.test 6c6a369af266af2ef50ab34df8f94d719065e2c1
|
||||
F test/crash8.test 38767cb504bbe491de6be4a7006b154973a2309f
|
||||
@ -364,8 +359,8 @@ F test/createtab.test b5de160630b209c4b8925bdcbbaf48cc90b67fe8
|
||||
F test/cse.test 277350a26264495e86b1785f34d2d0c8600e021c
|
||||
F test/ctime.test 7bd009071e242aac4f18521581536b652b789a47
|
||||
F test/date.test a18a2ce81add84b17b06559e82ad7bb91bc6ddff
|
||||
F test/dbstatus.test 3e978f8bdb2362a36a4be63c36a59f542c4cc2a3
|
||||
F test/dbstatus2.test dc57b0d9610851c0ff58a8e1b5b191678398b72a
|
||||
F test/dbstatus.test 207e5b63fcb7b9c3bb8e1fdf38ebd4654ad0e54b
|
||||
F test/dbstatus2.test b1de8250fde1f3474d6b86f0e89de38d84794f56
|
||||
F test/default.test 6faf23ccb300114924353007795aa9a8ec0aa9dc
|
||||
F test/delete.test a065b05d2ebf60fd16639c579a4adfb7c381c701
|
||||
F test/delete2.test 3a03f2cca1f9a67ec469915cb8babd6485db43fa
|
||||
@ -374,28 +369,28 @@ F test/descidx1.test 533dcbda614b0463b0ea029527fd27e5a9ab2d66
|
||||
F test/descidx2.test 9f1a0c83fd57f8667c82310ca21b30a350888b5d
|
||||
F test/descidx3.test fe720e8b37d59f4cef808b0bf4e1b391c2e56b6f
|
||||
F test/diskfull.test 106391384780753ea6896b7b4f005d10e9866b6e
|
||||
F test/distinct.test df5b11ad606439129c88720a86787bc9ca181f31
|
||||
F test/distinct.test da36612d05b9ed17e0425d4bfd7ab978d28a7e46
|
||||
F test/distinctagg.test 1a6ef9c87a58669438fc771450d7a72577417376
|
||||
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_dropview.test 583411e470458c5d76148542cfb5a5fa84c8f93e
|
||||
F test/e_expr.test 4e4399006b3d1ab333721b8e386cabb9fb6d5a89
|
||||
F test/e_fkey.test 38039b840ab19331000b0f0eb1d82baa7208a67a
|
||||
F test/e_expr.test 5489424d3d9a452ac3701cdf4b680ae31a157894
|
||||
F test/e_fkey.test 057eed81a41a2b21b1790032f4e8aaba0b2b0e17
|
||||
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_resolve.test dcce9308fb13b934ce29591105d031d3e14fbba6
|
||||
F test/e_select.test 6f488c22851c1c280ed74e16a9a1430706a3a8cb
|
||||
F test/e_select.test f5d4b81205701deacfae42051ae200969c41d2c0
|
||||
F test/e_select2.test 5c3d3da19c7b3e90ae444579db2b70098599ab92
|
||||
F test/e_update.test dba988a4d079156549a40854074ba4890b0a4577
|
||||
F test/e_uri.test 6f35b491f80dac005c8144f38b2dfb4d96483596
|
||||
F test/e_vacuum.test 5296e25ef871965bac010b9da083dd7e4734526e
|
||||
F test/e_update.test 161d5dc6a3ed9dd08f5264d13e20735d7a89f00c
|
||||
F test/e_uri.test e8b894474fdfe7b18b0c9cb2d911270de2ad64ce
|
||||
F test/e_vacuum.test 331da289ae186656cf5f2eb27f577a89c0c221af
|
||||
F test/enc.test e54531cd6bf941ee6760be041dff19a104c7acea
|
||||
F test/enc2.test 796c59832e2b9a52842f382ffda8f3e989db03ad
|
||||
F test/enc3.test 90683ad0e6ea587b9d5542ca93568af9a9858c40
|
||||
F test/enc4.test c8f1ce3618508fd0909945beb8b8831feef2c020
|
||||
F test/eqp.test f14fadd76da53405e9885e2431cacf7191d83cdb
|
||||
F test/eqp.test 6a389bba6ea113fd5179515001be788a38d53ec7
|
||||
F test/errmsg.test 3bb606db9d040cc6854459f8f5e5a2bcd9b7fd2a
|
||||
F test/eval.test bc269c365ba877554948441e91ad5373f9f91be3
|
||||
F test/exclusive.test a1b324cb21834a490cd052d409d34789cfef57cb
|
||||
@ -404,7 +399,7 @@ F test/exec.test e949714dc127eaa5ecc7d723efec1ec27118fdd7
|
||||
F test/exists.test 8f7b27b61c2fbe5822f0a1f899c715d14e416e30
|
||||
F test/expr.test 67c9fd6f8f829e239dc8b0f4a08a73c08b09196d
|
||||
F test/fallocate.test b5d34437bd7ab01d41b1464b8117aefd4d32160e
|
||||
F test/filectrl.test 4eb0178956ca25a756e6d79711a90fec7157b454
|
||||
F test/filectrl.test f0327bd804d9c7bd048fa7a151c5eab8e27df42b
|
||||
F test/filefmt.test ffa17b5aebc3eb4b1e3be1ccb5ee906ffbd97f6e
|
||||
F test/fkey1.test 01c7de578e11747e720c2d9aeef27f239853c4da
|
||||
F test/fkey2.test 080969fe219b3b082b0e097ac18c6af2e5b0631f
|
||||
@ -448,7 +443,7 @@ F test/fts2q.test b2fbbe038b7a31a52a6079b215e71226d8c6a682
|
||||
F test/fts2r.test b154c30b63061d8725e320fba1a39e2201cadd5e
|
||||
F test/fts2token.test d8070b241a15ff13592a9ae4a8b7c171af6f445a
|
||||
F test/fts3.test 672a040ea57036fb4b6fdc09027c18d7d24ab654
|
||||
F test/fts3_common.tcl 4d8eec9db565fed9098f45c378f28e1657802011
|
||||
F test/fts3_common.tcl 99cf6659b87c0f74f55963c2aea03b3a7d66ceb0
|
||||
F test/fts3aa.test 909d5f530d30a8e36b9328d67285eae6537c79c0
|
||||
F test/fts3ab.test 09aeaa162aee6513d9ff336b6932211008b9d1f9
|
||||
F test/fts3ac.test 636ed7486043055d4f126a0e385f2d5a82ebbf63
|
||||
@ -465,7 +460,7 @@ F test/fts3am.test 218aa6ba0dfc50c7c16b2022aac5c6be593d08d8
|
||||
F test/fts3an.test a49ccadc07a2f7d646ec1b81bc09da2d85a85b18
|
||||
F test/fts3ao.test e7b80272efcced57d1d087a9da5c690dd7c21fd9
|
||||
F test/fts3atoken.test 402ef2f7c2fb4b3d4fa0587df6441c1447e799b3
|
||||
F test/fts3auto.test c1a30b37002b7c764a96937fbc71065b73d69494
|
||||
F test/fts3auto.test b39f3f51227aea145eae6638690355dbdf9abf18
|
||||
F test/fts3aux1.test 0b02743955d56fc0d4d66236a26177bd1b726de0
|
||||
F test/fts3b.test e93bbb653e52afde110ad53bbd793f14fe7a8984
|
||||
F test/fts3c.test fc723a9cf10b397fdfc2b32e73c53c8b1ec02958
|
||||
@ -475,7 +470,7 @@ F test/fts3corrupt.test 7b0f91780ca36118d73324ec803187208ad33b32
|
||||
F test/fts3corrupt2.test 6d96efae2f8a6af3eeaf283aba437e6d0e5447ba
|
||||
F test/fts3cov.test e0fb00d8b715ddae4a94c305992dfc3ef70353d7
|
||||
F test/fts3d.test bf640d79722b720fa1c81834c48cdaa45d531b1a
|
||||
F test/fts3defer.test 2ea3fa028f8d9523f9c33dd8acc4555d567ea4ac
|
||||
F test/fts3defer.test 6c2707be1b05b9790ba8ff91d3391d5fb425269e
|
||||
F test/fts3defer2.test 35867d33ba6db03f6c73bd6f5fc333ae14f68c81
|
||||
F test/fts3drop.test 1b906e293d6773812587b3dc458cb9e8f3f0c297
|
||||
F test/fts3e.test 1f6c6ac9cc8b772ca256e6b22aaeed50c9350851
|
||||
@ -488,14 +483,20 @@ F test/fts3malloc.test b86ea33db9e8c58c0c2f8027a9fcadaf6a1568be
|
||||
F test/fts3matchinfo.test 6507fe1c342e542300d65ea637d4110eccf894e6
|
||||
F test/fts3near.test 2e318ee434d32babd27c167142e2b94ddbab4844
|
||||
F test/fts3prefix.test b36d4f00b128a51e7b386cc013a874246d9d7dc1
|
||||
F test/fts3prefix2.test 477ca96e67f60745b7ac931cfa6e9b080c562da5
|
||||
F test/fts3query.test ef79d31fdb355d094baec1c1b24b60439a1fb8a2
|
||||
F test/fts3rnd.test 1320d8826a845e38a96e769562bf83d7a92a15d0
|
||||
F test/fts3shared.test 8bb266521d7c5495c0ae522bb4d376ad5387d4a2
|
||||
F test/fts3snippet.test 8e956051221a34c7daeb504f023cb54d5fa5a8b2
|
||||
F test/fts3sort.test 95be0b19d7e41c44b29014f13ea8bddd495fd659
|
||||
F test/fts4aa.test 6e7f90420b837b2c685f3bcbe84c868492d40a68
|
||||
F test/fts4content.test 2624253c7e5a32d0c0d51f776dcd4526f0a51097
|
||||
F test/func.test 6c5ce11e3a0021ca3c0649234e2d4454c89110ca
|
||||
F test/fts4check.test 66fa274cab2b615f2fb338b257713aba8fad88a8
|
||||
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/func3.test 001021e5b88bd02a3b365a5c5fd8f6f49d39744a
|
||||
F test/fuzz.test 77fd50afc12847af50fcf1941679d90adebadde6
|
||||
@ -503,16 +504,18 @@ F test/fuzz2.test 207d0f9d06db3eaf47a6b7bfc835b8e2fc397167
|
||||
F test/fuzz3.test aec64345184d1662bd30e6a17851ff659d596dc5
|
||||
F test/fuzz_common.tcl a87dfbb88c2a6b08a38e9a070dabd129e617b45b
|
||||
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/icu.test 70df4faca133254c042d02ae342c0a141f2663f4
|
||||
F test/in.test 19b642bb134308980a92249750ea4ce3f6c75c2d
|
||||
F test/in.test 5941096407d8c133b9eff15bd3e666624b6cbde3
|
||||
F test/in2.test 5d4c61d17493c832f7d2d32bef785119e87bde75
|
||||
F test/in3.test 3cbf58c87f4052cee3a58b37b6389777505aa0c0
|
||||
F test/in4.test 64f3cc1acde1b9161ccdd8e5bde3daefdb5b2617
|
||||
F test/incrblob.test 3307c04876fe025e10256e3cc8050ab5a84aa27f
|
||||
F test/incrblob.test 26fde912a1e0aff158b3a84ef3b265f046aad3be
|
||||
F test/incrblob2.test edc3a96e557bd61fb39acc8d2edd43371fbbaa19
|
||||
F test/incrblob3.test aedbb35ea1b6450c33b98f2b6ed98e5020be8dc7
|
||||
F test/incrblob4.test 09be37d3dd996a31ea6993bba7837ece549414a8
|
||||
F test/incrblob_err.test d2562d2771ebffd4b3af89ef64c140dd44371597
|
||||
F test/incrblobfault.test 917c0292224c64a56ef7215fd633a3a82f805be0
|
||||
F test/incrvacuum.test d2a6ddf5e429720b5fe502766af747915ccf6c32
|
||||
@ -525,7 +528,7 @@ F test/index4.test 2983216eb8c86ee62d9ed7cb206b5cc3331c0026
|
||||
F test/indexedby.test be501e381b82b2f8ab406309ba7aac46e221f4ad
|
||||
F test/indexfault.test 31d4ab9a7d2f6e9616933eb079722362a883eb1d
|
||||
F test/init.test 15c823093fdabbf7b531fe22cf037134d09587a7
|
||||
F test/insert.test aef273dd1cee84cc92407469e6bd1b3cdcb76908
|
||||
F test/insert.test 489aa12a027c83d291f5034a83c8c32e6be1dca2
|
||||
F test/insert2.test 4f3a04d168c728ed5ec2c88842e772606c7ce435
|
||||
F test/insert3.test 1b7db95a03ad9c5013fdf7d6722b6cd66ee55e30
|
||||
F test/insert4.test 87f6798f31d60c4e177622fcc3663367e6ecbd90
|
||||
@ -533,9 +536,9 @@ F test/insert5.test 394f96728d1258f406fe5f5aeb0aaf29487c39a6
|
||||
F test/intarray.test 066b7d7ac38d25bf96f87f1b017bfc687551cdd4
|
||||
F test/interrupt.test 42e7cf98646fd9cb4a3b131a93ed3c50b9e149f1
|
||||
F test/intpkey.test 537669fd535f62632ca64828e435b9e54e8d677f
|
||||
F test/io.test b278aa8fa609ed0dcc825df31b2d9f526c5a52bd
|
||||
F test/io.test 36d251507d72e92b965fb2f0801c2f0b56335bcf
|
||||
F test/ioerr.test 40bb2cfcab63fb6aa7424cd97812a84bc16b5fb8
|
||||
F test/ioerr2.test 1b56cb80d5b0726ee3ba325ca175734541e32955
|
||||
F test/ioerr2.test 9d71166f8466eda510f1af6137bdabaa82b5408d
|
||||
F test/ioerr3.test d3cec5e1a11ad6d27527d0d38573fbff14c71bdd
|
||||
F test/ioerr4.test f130fe9e71008577b342b8874d52984bd04ede2c
|
||||
F test/ioerr5.test 2edfa4fb0f896f733071303b42224df8bedd9da4
|
||||
@ -544,10 +547,10 @@ F test/join2.test f2171c265e57ee298a27e57e7051d22962f9f324
|
||||
F test/join3.test 6f0c774ff1ba0489e6c88a3e77b9d3528fb4fda0
|
||||
F test/join4.test 1a352e4e267114444c29266ce79e941af5885916
|
||||
F test/join5.test 86675fc2919269aa923c84dd00ee4249b97990fe
|
||||
F test/join6.test bf82cf3f979e9eade83ad0d056a66c5ed71d1901
|
||||
F test/join6.test cfe6503791ceb0cbb509966740286ec423cbf10b
|
||||
F test/journal1.test 8b71ef1ed5798bdc0e6eb616d8694e2c2c188d4d
|
||||
F test/journal2.test ae06f566c28552c313ded3fee79a6c69e6d049b1
|
||||
F test/journal3.test 6fd28532c88b447db844186bc190523108b6dbb4
|
||||
F test/journal3.test ff8af941f9e06161d3db1b46bb9f965ff0e7f307
|
||||
F test/jrnlmode.test 9ee3a78f53d52cca737db69293d15dc41c0cbd36
|
||||
F test/jrnlmode2.test 81610545a4e6ed239ea8fa661891893385e23a1d
|
||||
F test/jrnlmode3.test 556b447a05be0e0963f4311e95ab1632b11c9eaa
|
||||
@ -600,17 +603,18 @@ F test/memsubsys2.test 3a1c1a9de48e5726faa85108b02459fae8cb9ee9
|
||||
F test/minmax.test 722d80816f7e096bf2c04f4111f1a6c1ba65453d
|
||||
F test/minmax2.test 33504c01a03bd99226144e4b03f7631a274d66e0
|
||||
F test/minmax3.test cc1e8b010136db0d01a6f2a29ba5a9f321034354
|
||||
F test/minmax4.test 536a3360470633a177e42fbc19660d146b51daef
|
||||
F test/misc1.test 55cb2bfbf4a8cd61f4be1effc30426ad41696bff
|
||||
F test/misc2.test 00d7de54eda90e237fc9a38b9e5ccc769ebf6d4d
|
||||
F test/misc3.test fe55130a43e444ee75e2156ff75dc96e964b5738
|
||||
F test/misc4.test 9c078510fbfff05a9869a0b6d8b86a623ad2c4f6
|
||||
F test/misc5.test 528468b26d03303b1f047146e5eefc941b9069f5
|
||||
F test/misc6.test 953cc693924d88e6117aeba16f46f0bf5abede91
|
||||
F test/misc7.test eafaa41b9133d7a2ded4641bbe5f340731d35a52
|
||||
F test/misc7.test 4337d84e441f36cee62656f9f7ba8bc22a7ca721
|
||||
F test/misuse.test ba4fb5d1a6101d1c171ea38b3c613d0661c83054
|
||||
F test/multiplex.test e08cc7177bd6d85990ee1d71100bb6c684c02256
|
||||
F test/multiplex2.test 580ca5817c7edbe4cc68fa150609c9473393003a
|
||||
F test/multiplex3.test 15903c343f1eaa4b00998b7ceacfc4987e4ccfe9
|
||||
F test/multiplex3.test d228f59eac91839a977eac19f21d053f03e4d101
|
||||
F test/mutex1.test 78b2b9bb320e51d156c4efdb71b99b051e7a4b41
|
||||
F test/mutex2.test bfeaeac2e73095b2ac32285d2756e3a65e681660
|
||||
F test/nan.test e9648b9d007c7045242af35e11a984d4b169443a
|
||||
@ -621,7 +625,7 @@ F test/notnull.test cc7c78340328e6112a13c3e311a9ab3127114347
|
||||
F test/null.test a8b09b8ed87852742343b33441a9240022108993
|
||||
F test/openv2.test 0d3040974bf402e19b7df4b783e447289d7ab394
|
||||
F test/oserror.test 50417780d0e0d7cd23cf12a8277bb44024765df3
|
||||
F test/pager1.test 9e9f5f1c6d4df4831dbff213b1262ef94bf72118
|
||||
F test/pager1.test 31fef8ff6d5cbb4643f430e31756312d361ecfdf
|
||||
F test/pager2.test 745b911dde3d1f24ae0870bd433dfa83d7c658c1
|
||||
F test/pager3.test 3856d9c80839be0668efee1b74811b1b7f7fc95f
|
||||
F test/pagerfault.test 452f2cc23e3bfcfa935f4442aec1da4fe1dc0442
|
||||
@ -631,19 +635,19 @@ F test/pageropt.test 9191867ed19a2b3db6c42d1b36b6fbc657cd1ab0
|
||||
F test/pagesize.test 1dd51367e752e742f58e861e65ed7390603827a0
|
||||
F test/pcache.test 065aa286e722ab24f2e51792c1f093bf60656b16
|
||||
F test/pcache2.test a83efe2dec0d392f814bfc998def1d1833942025
|
||||
F test/permutations.test fa6f0e5f13fe0b1d3f7a7613179b7f7b20028184
|
||||
F test/pragma.test 7fa35e53085812dac94c2bfcbb02c2a4ad35df5e
|
||||
F test/permutations.test dbda172249564f43ec556108a704581044c57dbd
|
||||
F test/pragma.test c51c148defe32bf4a419a522f95d26838d5cf677
|
||||
F test/pragma2.test 3a55f82b954242c642f8342b17dffc8b47472947
|
||||
F test/printf.test ec9870c4dce8686a37818e0bf1aba6e6a1863552
|
||||
F test/progress.test 5b075c3c790c7b2a61419bc199db87aaf48b8301
|
||||
F test/ptrchng.test ef1aa72d6cf35a2bbd0869a649b744e9d84977fc
|
||||
F test/quick.test 1681febc928d686362d50057c642f77a02c62e57
|
||||
F test/quota-glob.test 32901e9eed6705d68ca3faee2a06b73b57cb3c26
|
||||
F test/quota.test af47d25c166aa7b33ef25f21bb7f2afb29d82c77
|
||||
F test/quota2.test 1b8df088e604f2df573f96e726b5e518cb0cddaa
|
||||
F test/quota.test c2f778dab4c7fb07bcfa962cc5c762f36d8061dc
|
||||
F test/quota2.test bc9fdb2e46aace691c1a01a9cc8d097bd4d7c1ab
|
||||
F test/quote.test 215897dbe8de1a6f701265836d6601cc6ed103e6
|
||||
F test/randexpr1.tcl 40dec52119ed3a2b8b2a773bce24b63a3a746459
|
||||
F test/randexpr1.test 1084050991e9ba22c1c10edd8d84673b501cc25a
|
||||
F test/randexpr1.test eda062a97e60f9c38ae8d806b03b0ddf23d796df
|
||||
F test/rdonly.test c267d050a1d9a6a321de502b737daf28821a518d
|
||||
F test/reindex.test 44edd3966b474468b823d481eafef0c305022254
|
||||
F test/releasetest.mk 2eced2f9ae701fd0a29e714a241760503ccba25a
|
||||
@ -652,38 +656,45 @@ F test/rollback.test a1b4784b864331eae8b2a98c189efa2a8b11ff07
|
||||
F test/rowhash.test 0bc1d31415e4575d10cacf31e1a66b5cc0f8be81
|
||||
F test/rowid.test e58e0acef38b527ed1b0b70d3ada588f804af287
|
||||
F test/rtree.test 0c8d9dd458d6824e59683c19ab2ffa9ef946f798
|
||||
F test/savepoint.test e575217b07d6a6e895e66f4eda076570815e0027
|
||||
F test/savepoint.test f5acd87d0c7a5f4ad6c547b47fd18c0e1aeaf048
|
||||
F test/savepoint2.test 9b8543940572a2f01a18298c3135ad0c9f4f67d7
|
||||
F test/savepoint3.test e328085853b14898d78ceea00dfe7db18bb6a9ec
|
||||
F test/savepoint4.test c8f8159ade6d2acd9128be61e1230f1c1edc6cc0
|
||||
F test/savepoint5.test 0735db177e0ebbaedc39812c8d065075d563c4fd
|
||||
F test/savepoint6.test f41279c5e137139fa5c21485773332c7adb98cd7
|
||||
F test/savepoint7.test fbf319a7b2dda089ec5be30a424a0e95f121d423
|
||||
F test/schema.test 8f7999be894260f151adf15c2c7540f1c6d6a481
|
||||
F test/schema2.test 906408621ea881fdb496d878b1822572a34e32c5
|
||||
F test/schema3.test 1bc1008e1f8cb5654b248c55f27249366eb7ed38
|
||||
F test/schema4.test e6a66e20cc69f0e306667c08be7fda3d11707dc5
|
||||
F test/schema5.test 0103e4c0313b3725b5ae5600bdca53006ab53db3
|
||||
F test/securedel.test 87a2561151af1f1e349071a89fdd77059f50113c
|
||||
F test/select1.test f67ca2dfc05df41c7b86eb32ca409b427a5f43b0
|
||||
F test/select1.test deba017eed9daa5af33de868676c997e7eebb931
|
||||
F test/select2.test 352480e0e9c66eda9c3044e412abdf5be0215b56
|
||||
F test/select3.test 2ce595f8fb8e2ac10071d3b4e424cadd4634a054
|
||||
F test/select4.test 44aa6e7110592e18110b0b9cf5c024d37d23be17
|
||||
F test/select4.test 00179be44e531fe04c1c3f15df216439dff2519d
|
||||
F test/select5.test e758b8ef94f69b111df4cb819008856655dcd535
|
||||
F test/select6.test cc25a8650cf9a4d4f74e586c45a75f9836516b18
|
||||
F test/select7.test dad6f00f0d49728a879d6eb6451d4752db0b0abe
|
||||
F test/select8.test 391de11bdd52339c30580dabbbbe97e3e9a3c79d
|
||||
F test/select9.test 74c0fb2c6eecb0219cbed0cbe3df136f8fbf9343
|
||||
F test/select9.test c0ca3cd87a8ebb04de2cb1402c77df55d911a0ea
|
||||
F test/selectA.test 06d1032fa9009314c95394f2ca2e60d9f7ae8532
|
||||
F test/selectB.test 954e4e49cf1f896d61794e440669e03a27ceea25
|
||||
F test/selectC.test f9bf1bc4581b5b8158caa6e4e4f682acb379fb25
|
||||
F test/selectC.test 871fb55d884d3de5943c4057ebd22c2459e71977
|
||||
F test/server1.test 46803bd3fe8b99b30dbc5ff38ffc756f5c13a118
|
||||
F test/shared.test 34945a516532b11182c3eb26e31247eee3c9ae48
|
||||
F test/shared2.test 8f71d4eb4d5261280de92284df74172545c852cc
|
||||
F test/shared2.test 03eb4a8d372e290107d34b6ce1809919a698e879
|
||||
F test/shared3.test ebf77f023f4bdaa8f74f65822b559e86ce5c6257
|
||||
F test/shared4.test 72d90821e8d2fc918a08f16d32880868d8ee8e9d
|
||||
F test/shared6.test 866bb4982c45ce216c61ded5e8fde4e7e2f3ffa9
|
||||
F test/shared7.test 960760bc8d03e1419e70dea69cf41db62853616e
|
||||
F test/shared_err.test 91e26ec4f3fbe07951967955585137e2f18993de
|
||||
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/shrink.test 8c70f62b6e8eb4d54533de6d65bd06b1b9a17868
|
||||
F test/sidedelete.test f0ad71abe6233e3b153100f3b8d679b19a488329
|
||||
@ -699,15 +710,15 @@ F test/speed4.test abc0ad3399dcf9703abed2fff8705e4f8e416715
|
||||
F test/speed4p.explain 6b5f104ebeb34a038b2f714150f51d01143e59aa
|
||||
F test/speed4p.test 0e51908951677de5a969b723e03a27a1c45db38b
|
||||
F test/sqllimits1.test b1aae27cc98eceb845e7f7adf918561256e31298
|
||||
F test/stat.test 36bc951bdc2beac4224cc54396fd6a7dc65336f4
|
||||
F test/stat.test 08e8185b3fd5b010c90d7ad82b9dd4ea1cbf14b0
|
||||
F test/stmt.test 25d64e3dbf9a3ce89558667d7f39d966fe2a71b9
|
||||
F test/subquery.test b524f57c9574b2c0347045b4510ef795d4686796
|
||||
F test/subquery.test c5e0d183f1ae6251453338a465b32ae11326e0fa
|
||||
F test/subquery2.test edcad5c118f0531c2e21bf16a09bbb105252d4cd
|
||||
F test/subselect.test d24fd8757daf97dafd2e889c73ea4c4272dcf4e4
|
||||
F test/substr.test 18f57c4ca8a598805c4d64e304c418734d843c1a
|
||||
F test/superlock.test 1cde669f68d2dd37d6c9bd35eee1d95491ae3fc2
|
||||
F test/sync.test a34cd43e98b7fb84eabbf38f7ed8f7349b3f3d85
|
||||
F test/syscall.test 265cda616f56a297406728ee1e74c9b4a93aa6dd
|
||||
F test/syscall.test bea9bf329bff733c791310244617c2a76974e64a
|
||||
F test/sysfault.test c79441d88d23696fbec7b147dba98d42a04f523f
|
||||
F test/table.test a59d985ca366e39b17b175f387f9d5db5a18d4e2
|
||||
F test/tableapi.test 2674633fa95d80da917571ebdd759a14d9819126
|
||||
@ -715,7 +726,7 @@ F test/tclsqlite.test 1597d353308531527583481d14d9da52ea8ed0af
|
||||
F test/tempdb.test 19d0f66e2e3eeffd68661a11c83ba5e6ace9128c
|
||||
F test/temptable.test 51edd31c65ed1560dd600b1796e8325df96318e2
|
||||
F test/temptrigger.test 26670ed7a39cf2296a7f0a9e0a1d7bdb7abe936d
|
||||
F test/tester.tcl 001051eaf28c1040800f588a64c63e0bd0e1f36b
|
||||
F test/tester.tcl a55e066251bc05e26f9da00e76644ab649b26f3c
|
||||
F test/thread001.test 7cc2ce08f9cde95964736d11e91f9ab610f82f91
|
||||
F test/thread002.test e630504f8a06c00bf8bbe68528774dd96aeb2e58
|
||||
F test/thread003.test ee4c9efc3b86a6a2767516a37bd64251272560a7
|
||||
@ -727,15 +738,17 @@ F test/thread_common.tcl 334639cadcb9f912bf82aa73f49efd5282e6cadd
|
||||
F test/threadtest1.c 6029d9c5567db28e6dc908a0c63099c3ba6c383b
|
||||
F test/threadtest2.c ace893054fa134af3fc8d6e7cfecddb8e3acefb9
|
||||
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-2a5629202f.test 1ab32e084e9fc3d36be6dee2617530846a0eb0b6
|
||||
F test/tkt-2d1a5c67d.test b028a811049eb472cb2d3a43fc8ce4f6894eebda
|
||||
F test/tkt-2ea2425d34.test 1cf13e6f75d149b3209a0cb32927a82d3d79fb28
|
||||
F test/tkt-31338dca7e.test 1f714c14b6682c5db715e0bda347926a3456f7a9
|
||||
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-3a77c9714e.test 1675c22a5be71d7fa026e5db5daeeb4dd64f7824
|
||||
F test/tkt-3a77c9714e.test 32bb28afa8c63fc76e972e996193139b63551ed9
|
||||
F test/tkt-3fe897352e.test 10de1a67bd5c66b238a4c96abe55531b37bb4f00
|
||||
F test/tkt-4a03edc4c8.test 2865e4edbc075b954daa82f8da7cc973033ec76e
|
||||
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-752e1646fc.test ea78d88d14fe9866bdd991c634483334639e13bf
|
||||
F test/tkt-78e04e52ea.test ab52f0c1e2de6e46c910f4cc16b086bba05952b7
|
||||
F test/tkt-7bbfb7d442.test 8e7658f77d1ccea9d88dc9e255d3ed7fb68f8bdf
|
||||
F test/tkt-80ba201079.test a09684db1a0bd55b8838f606adccee456a51ddbf
|
||||
F test/tkt-7bbfb7d442.test dfa5c8097a8c353ae40705d6cddeb1f99c18b81a
|
||||
F test/tkt-80ba201079.test 9eb040d81c404f56838a6af93593f42790def63f
|
||||
F test/tkt-80e031a00f.test 9a154173461a4dbe2de49cda73963e04842d52f7
|
||||
F test/tkt-8454a207b9.test c583a9f814a82a2b5ba95207f55001c9f0cd816c
|
||||
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-b1d3a2e531.test 610ef582413171b379652663111b1f996d9f8f78
|
||||
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-bdc6bbbb38.test fc38bb09bdd440e3513a1f5f98fc60a075182d7d
|
||||
F test/tkt-c48d99d690.test bed446e3513ae10eec1b86fdd186ef750226c408
|
||||
F test/tkt-cbd054fa6b.test bd9fb546f63bc0c79d1776978d059fa51c5b1c63
|
||||
F test/tkt-d11f09d36e.test fb44f7961aa6d4b632fb7b9768239832210b5fc7
|
||||
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-f777251dc7a.test 6f24c053bc5cdb7e1e19be9a72c8887cf41d5e87
|
||||
F test/tkt-f777251dc7a.test af6531446c64bfd268416f07b4df7be7f9c749d2
|
||||
F test/tkt-f7b4edec.test d998a08ff2b18b7f62edce8e3044317c45efe6c7
|
||||
F test/tkt-f973c7ac31.test 1da0ed15ec2c7749fb5ce2828cd69d07153ad9f4
|
||||
F test/tkt-fa7bf5ec.test 9102dfea58aa371d78969da735f9392c57e2e035
|
||||
@ -820,7 +834,7 @@ F test/tkt3461.test 228ea328a5a21e8663f80ee3d212a6ad92549a19
|
||||
F test/tkt3493.test 1686cbde85f8721fc1bdc0ee72f2ef2f63139218
|
||||
F test/tkt3508.test d75704db9501625c7f7deec119fcaf1696aefb7d
|
||||
F test/tkt3522.test 22ce2ebbcb04a6be56c0977d405c207967318fd6
|
||||
F test/tkt3527.test ee4af96183579565987e58873a7490bc04934ffb
|
||||
F test/tkt3527.test 9e8f28a706c772d5a7cd1020c946fab6c74e3ae0
|
||||
F test/tkt3541.test 5dc257bde9bc833ab9cc6844bf170b998dbb950a
|
||||
F test/tkt3554.test f599967f279077bace39220cbe76085c7b423725
|
||||
F test/tkt3581.test 1966b7193f1e3f14951cce8c66907ae69454e9a3
|
||||
@ -831,13 +845,13 @@ F test/tkt3731.test 0c5f4cbffe102d43c3b2188af91a9e36348f974b
|
||||
F test/tkt3757.test 10cd679a88675c880533083fc79ac04324525595
|
||||
F test/tkt3761.test b95ea9c98f21cf91325f18a984887e62caceab33
|
||||
F test/tkt3762.test 2a9f3b03df44ec49ec0cfa8d5da6574c2a7853df
|
||||
F test/tkt3773.test 430b06567ce40285dfd2c4834a2a61816403efeb
|
||||
F test/tkt3773.test 7bca904d2a647a6a4a291bd86d7fd7c73855b789
|
||||
F test/tkt3791.test a6624b9a80b216a26cf473607f42f3e51898c267
|
||||
F test/tkt3793.test d90ffd75c52413908d15e1c44fc2ea9c80fcc449
|
||||
F test/tkt3810.test 90fa0635dfa7da9680c8cd3513350a49b3a8ae12
|
||||
F test/tkt3824.test 150aa00bb6220672e5f0eb14dc8eaa36750425f0
|
||||
F test/tkt3832.test 2300d10d57562b89875b72148338ac3e14f8847d
|
||||
F test/tkt3838.test d8490365a1c473d214f7878007e543410cbb715f
|
||||
F test/tkt3838.test 292e72489101cd1320d7278dc111c173ebf334d4
|
||||
F test/tkt3841.test 4659845bc53f809a5932c61c6ce8c5bb9d6b947f
|
||||
F test/tkt3871.test 43ecbc8d90dc83908e2a454aef345acc9d160c6f
|
||||
F test/tkt3879.test 2ad5bef2c87e9991ce941e054c31abe26ef7fb90
|
||||
@ -851,11 +865,11 @@ F test/tkt3997.test a335fa41ca3985660a139df7b734a26ef53284bd
|
||||
F test/tkt4018.test 7c2c9ba4df489c676a0a7a0e809a1fb9b2185bd1
|
||||
F test/tokenize.test ce430a7aed48fc98301611429595883fdfcab5d7
|
||||
F test/trace.test 4b36a41a3e9c7842151af6da5998f5080cdad9e5
|
||||
F test/trace2.test 962175290996d5f06dc4402ca218bbfc7df4cb20
|
||||
F test/trace2.test c1dc104a8d11a347c870cfea6235e3fc6f6cb06d
|
||||
F test/trans.test 6e1b4c6a42dba31bd65f8fa5e61a2708e08ddde6
|
||||
F test/trans2.test d5337e61de45e66b1fcbf9db833fa8c82e624b22
|
||||
F test/trans3.test d728abaa318ca364dc370e06576aa7e5fbed7e97
|
||||
F test/trigger1.test 38c657eaf9907344c9e0bcb16af94a452c6babde
|
||||
F test/trans3.test 373ac5183cc56be69f48ae44090e7f672939f732
|
||||
F test/trigger1.test de42feb7cd442787d38185ae74f5a1d7afa400cb
|
||||
F test/trigger2.test 834187beafd1db383af0c659cfa49b0576832816
|
||||
F test/trigger3.test d2c60d8be271c355d61727411e753181e877230a
|
||||
F test/trigger4.test 74700b76ebf3947b2f7a92405141eb2cf2a5d359
|
||||
@ -876,7 +890,7 @@ F test/unique.test 083c7fff74695bcc27a71d75699deba3595bc9c2
|
||||
F test/unixexcl.test a9870e46cc6f8390a494513d4f2bf55b5a8b3e46
|
||||
F test/unordered.test f53095cee37851bf30130fa1bf299a8845e837bb
|
||||
F test/update.test 8bc86fd7ef1a00014f76dc6a6a7c974df4aef172
|
||||
F test/uri.test 0d289d32396bdbc491e9dc845f1a52e13f861e0b
|
||||
F test/uri.test 78e869db1ff6331157b08ef089b1b3e65819c74c
|
||||
F test/utf16align.test 54cd35a27c005a9b6e7815d887718780b6a462ae
|
||||
F test/vacuum.test ce91c39f7f91a4273bf620efad21086b5aa6ef1d
|
||||
F test/vacuum2.test af432e6e3bfc0ea20a80cb86a03c7d9876d38324
|
||||
@ -885,7 +899,7 @@ F test/vacuum4.test d3f8ecff345f166911568f397d2432c16d2867d9
|
||||
F test/varint.test ab7b110089a08b9926ed7390e7e97bdefeb74102
|
||||
F test/veryquick.test 7701bb609fe8bf6535514e8b849a309e8f00573b
|
||||
F test/view.test b182a67ec43f490b156b5a710827a341be83dd17
|
||||
F test/vtab1.test 12fbb309ce830c4064e44f275eb02a65c2780076
|
||||
F test/vtab1.test e429a6835faa3870016c55d1178dcfead85f936a
|
||||
F test/vtab2.test 7bcffc050da5c68f4f312e49e443063e2d391c0d
|
||||
F test/vtab3.test baad99fd27217f5d6db10660522e0b7192446de1
|
||||
F test/vtab4.test 942f8b8280b3ea8a41dae20e7822d065ca1cb275
|
||||
@ -897,22 +911,23 @@ F test/vtab9.test ea58d2b95d61955f87226381716b2d0b1d4e4f9b
|
||||
F test/vtabA.test c86e1990b7e1e2bb34602a06fffa4c69f2b516dc
|
||||
F test/vtabB.test 04df5dc531b9f44d9ca65b9c1b79f12b5922a796
|
||||
F test/vtabC.test 4528f459a13136f982e75614d120aef165f17292
|
||||
F test/vtabD.test 74167b1578e5886fe4c886d6bef2fd1406444c42
|
||||
F test/vtabD.test 05b3f1d77117271671089e48719524b676842e96
|
||||
F test/vtabE.test 7c4693638d7797ce2eda17af74292b97e705cc61
|
||||
F test/vtabF.test fd5ad376f5a34fe0891df1f3cddb4fe7c3eb077e
|
||||
F test/vtab_alter.test 9e374885248f69e251bdaacf480b04a197f125e5
|
||||
F test/vtab_err.test 0d4d8eb4def1d053ac7c5050df3024fd47a3fbd8
|
||||
F test/vtab_shared.test 0eff9ce4f19facbe0a3e693f6c14b80711a4222d
|
||||
F test/wal.test edefe316b4125d7f68004ea53c5e73c398d436cc
|
||||
F test/wal2.test f11883dd3cb7f647c5d2acfd7b5c6d4ba5770cc9
|
||||
F test/vtab_shared.test 82f463886e18d7f8395a4b6167c91815efe54839
|
||||
F test/wal.test b3d28d655371bf3f6500c679f526e9860544fe70
|
||||
F test/wal2.test d5021064bebfc717fe2bf4db2536ea030b76a773
|
||||
F test/wal3.test 6504bbf348b2d6dfade64a064f1050fd617e8706
|
||||
F test/wal4.test 4744e155cd6299c6bd99d3eab1c82f77db9cdb3c
|
||||
F test/wal5.test f58ed4b8b542f71c7441da12fbd769d99b362437
|
||||
F test/wal6.test 2e3bc767d9c2ce35c47106148d43fcbd072a93b3
|
||||
F test/wal7.test 2ae8f427d240099cc4b2dfef63cff44e2a68a1bd
|
||||
F test/wal8.test 5ab217d21f7e5e86af2933a4ffd0d8357cc2c0bd
|
||||
F test/wal_common.tcl a98f17fba96206122eff624db0ab13ec377be4fe
|
||||
F test/walbak.test b9f68e39646375c2b877be906babcc15d38b4877
|
||||
F test/walbig.test 0ab8a430ef420a3114f7092e0f30fc9585ffa155
|
||||
F test/walbig.test f437473a16cfb314867c6b5d1dbcd519e73e3434
|
||||
F test/walcksum.test f5447800a157c9e2234fbb8e80243f0813941bde
|
||||
F test/walcrash.test 4457436593be8c136f9148487c7dccd5e9013af2
|
||||
F test/walcrash2.test 019d60b89d96c1937adb2b30b850ac7e86e5a142
|
||||
@ -926,16 +941,16 @@ F test/walro.test e6bb27762c9f22601cbb8bff6e0acfd124e74b63
|
||||
F test/walshared.test 6dda2293880c300baf5d791c307f653094585761
|
||||
F test/walslow.test e7be6d9888f83aa5d3d3c7c08aa9b5c28b93609a
|
||||
F test/walthread.test a2ed5270eb695284d4ad27d252517bdc3317ee2a
|
||||
F test/where.test de337a3fe0a459ec7c93db16a519657a90552330
|
||||
F test/where.test 4c9f69987ed2aa0173fa930f2b41ab9879478cd8
|
||||
F test/where2.test 43d4becaf5a5df854e6c21d624a1cb84c6904554
|
||||
F test/where3.test 667e75642102c97a00bf9b23d3cb267db321d006
|
||||
F test/where4.test e9b9e2f2f98f00379e6031db6a6fca29bae782a2
|
||||
F test/where5.test fdf66f96d29a064b63eb543e28da4dfdccd81ad2
|
||||
F test/where6.test 5da5a98cec820d488e82708301b96cb8c18a258b
|
||||
F test/where7.test 814d7373413398e074f134cff5f8872e2c08bd3b
|
||||
F test/where7.test 5c566388f0cc318b0032ce860f4ac5548e3c265a
|
||||
F test/where8.test a6c740fd286d7883e274e17b6230a9d672a7ab1f
|
||||
F test/where8m.test da346596e19d54f0aba35ebade032a7c47d79739
|
||||
F test/where9.test bed66dcfc69a54a99661c0c9906189cb5e58f4e2
|
||||
F test/where9.test ae98dc22ef9b6f2bc81e9f164e41b38faa9bda06
|
||||
F test/whereA.test 24c234263c8fe358f079d5e57d884fb569d2da0a
|
||||
F test/whereB.test 0def95db3bdec220a731c7e4bec5930327c1d8c5
|
||||
F test/whereC.test 13ff5ec0dba407c0e0c075980c75b3275a6774e5
|
||||
@ -943,16 +958,16 @@ F test/wherelimit.test 5e9fd41e79bb2b2d588ed999d641d9c965619b31
|
||||
F test/win32lock.test b2a539e85ae6b2d78475e016a9636b4451dc7fb9
|
||||
F test/zeroblob.test caaecfb4f908f7bc086ed238668049f96774d688
|
||||
F test/zerodamage.test 0de750389990b1078bab203c712dc3fefd1d8b82
|
||||
F tool/build-shell.sh 12aa4391073a777fcb6dcc490b219a018ae98bac
|
||||
F tool/build-shell.sh b64a481901fc9ffe5ca8812a2a9255b6cfb77381
|
||||
F tool/diffdb.c 7524b1b5df217c20cd0431f6789851a4e0cb191b
|
||||
F tool/extract.c 054069d81b095fbdc189a6f5d4466e40380505e2
|
||||
F tool/fragck.tcl 5265a95126abcf6ab357f7efa544787e5963f439
|
||||
F tool/genfkey.README cf68fddd4643bbe3ff8e31b8b6d8b0a1b85e20f4
|
||||
F tool/genfkey.test 4196a8928b78f51d54ef58e99e99401ab2f0a7e5
|
||||
F tool/getlock.c f4c39b651370156cae979501a7b156bdba50e7ce
|
||||
F tool/lemon.c 445f18999b700d83b83a5d9be00c596546c21052
|
||||
F tool/lemon.c 90f46af31c92b940fec25b491f39409fd95dcdfa
|
||||
F tool/lempar.c 01ca97f87610d1dac6d8cd96ab109ab1130e76dc
|
||||
F tool/mkkeywordhash.c d2e6b4a5965e23afb80fbe74bb54648cd371f309
|
||||
F tool/mkkeywordhash.c bb52064aa614e1426445e4b2b9b00eeecd23cc79
|
||||
F tool/mkopts.tcl 66ac10d240cc6e86abd37dc908d50382f84ff46e
|
||||
F tool/mkspeedsql.tcl a1a334d288f7adfe6e996f2e712becf076745c97
|
||||
F tool/mksqlite3c-noext.tcl 105023aa86f696a74b1d6a4929d1e1c3baf9471c
|
||||
@ -964,17 +979,12 @@ F tool/omittest.tcl 72a49b8a9a8b0bf213a438180307a0df836d4380
|
||||
F tool/opcodeDoc.awk b3a2a3d5d3075b8bd90b7afe24283efdd586659c
|
||||
F tool/restore_jrnl.tcl 6957a34f8f1f0f8285e07536225ec3b292a9024a
|
||||
F tool/rollback-test.c 9fc98427d1e23e84429d7e6d07d9094fbdec65a5
|
||||
F tool/shell1.test 20dfe7099cf2afe37aecd69afb7678d14f7a0abf
|
||||
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/showdb.c 2e28d8e499b016485672e9a7ac65dacc0d28ff69
|
||||
F tool/showjournal.c b62cecaab86a4053d944c276bb5232e4d17ece02
|
||||
F tool/showwal.c f09e5a80a293919290ec85a6a37c85a5ddcf37d9
|
||||
F tool/soak1.tcl 8d407956e1a45b485a8e072470a3e629a27037fe
|
||||
F tool/space_used.tcl f714c41a59e326b8b9042f415b628b561bafa06b
|
||||
F tool/spaceanal.tcl 15f6cd939b4ecc14d061de7e8ace89e26c30c40b
|
||||
F tool/spaceanal.tcl e42273000686a4afbf6a5e5d7fb12be65e92afb1
|
||||
F tool/speedtest.tcl 06c76698485ccf597b9e7dbb1ac70706eb873355
|
||||
F tool/speedtest16.c c8a9c793df96db7e4933f0852abb7a03d48f2e81
|
||||
F tool/speedtest2.tcl ee2149167303ba8e95af97873c575c3e0fab58ff
|
||||
@ -985,10 +995,12 @@ F tool/symbols-mingw.sh 4dbcea7e74768305384c9fd2ed2b41bbf9f0414d
|
||||
F tool/symbols.sh fec58532668296d7c7dc48be9c87f75ccdb5814f
|
||||
F tool/tostr.awk e75472c2f98dd76e06b8c9c1367f4ab07e122d06
|
||||
F tool/vdbe-compress.tcl d70ea6d8a19e3571d7ab8c9b75cba86d1173ff0f
|
||||
F tool/warnings-clang.sh 9f406d66e750e8ac031c63a9ef3248aaa347ef2a
|
||||
F tool/warnings-clang.sh a8a0a3babda96dfb1ff51adda3cbbf3dfb7266c2
|
||||
F tool/warnings.sh fbc018d67fd7395f440c28f33ef0f94420226381
|
||||
P 9da1cd0a06aca4eb3fb21fa4d321b9e3e07444b4
|
||||
R 81a3b6b323708ac95becda2f7731a3a4
|
||||
T +sym-version-3.7.10 *
|
||||
P af525b5d25b5dee20528acc8321b8c6ca9aa98ae
|
||||
R 25bb989b178d8b31e0e59b449786abed
|
||||
T +bgcolor * #d0c0ff
|
||||
T +sym-release *
|
||||
T +sym-version-3.7.12 *
|
||||
U drh
|
||||
Z 1c2c75389d69d7c8466f72bd2161ada0
|
||||
Z 093fff96464341c2fff7d3363f677b3a
|
||||
|
@ -1 +1 @@
|
||||
ebd01a8deffb5024a5d7494eef800d2366d97204
|
||||
8654aa9540fe9fd210899d83d17f3f407096c004
|
||||
|
@ -530,7 +530,7 @@ void sqlite3AlterRenameTable(
|
||||
"WHEN name LIKE 'sqlite_autoindex%%' AND type='index' THEN "
|
||||
"'sqlite_autoindex_' || %Q || substr(name,%d+18) "
|
||||
"ELSE name END "
|
||||
"WHERE tbl_name=%Q AND "
|
||||
"WHERE tbl_name=%Q COLLATE nocase AND "
|
||||
"(type='table' OR type='index' OR type='trigger');",
|
||||
zDb, SCHEMA_TABLE(iDb), zName, zName, zName,
|
||||
#ifndef SQLITE_OMIT_TRIGGER
|
||||
|
@ -932,6 +932,7 @@ static int loadStat3(sqlite3 *db, const char *zDb){
|
||||
int eType; /* Datatype of a sample */
|
||||
IndexSample *pSample; /* A slot in pIdx->aSample[] */
|
||||
|
||||
assert( db->lookaside.bEnabled==0 );
|
||||
if( !sqlite3FindTable(db, "sqlite_stat3", zDb) ){
|
||||
return SQLITE_OK;
|
||||
}
|
||||
@ -958,7 +959,7 @@ static int loadStat3(sqlite3 *db, const char *zDb){
|
||||
if( pIdx==0 ) continue;
|
||||
assert( pIdx->nSample==0 );
|
||||
pIdx->nSample = nSample;
|
||||
pIdx->aSample = sqlite3MallocZero( nSample*sizeof(IndexSample) );
|
||||
pIdx->aSample = sqlite3DbMallocZero(db, nSample*sizeof(IndexSample));
|
||||
pIdx->avgEq = pIdx->aiRowEst[1];
|
||||
if( pIdx->aSample==0 ){
|
||||
db->mallocFailed = 1;
|
||||
@ -1031,7 +1032,7 @@ static int loadStat3(sqlite3 *db, const char *zDb){
|
||||
if( n < 1){
|
||||
pSample->u.z = 0;
|
||||
}else{
|
||||
pSample->u.z = sqlite3Malloc(n);
|
||||
pSample->u.z = sqlite3DbMallocRaw(db, n);
|
||||
if( pSample->u.z==0 ){
|
||||
db->mallocFailed = 1;
|
||||
sqlite3_finalize(pStmt);
|
||||
@ -1107,7 +1108,10 @@ int sqlite3AnalysisLoad(sqlite3 *db, int iDb){
|
||||
/* Load the statistics from the sqlite_stat3 table. */
|
||||
#ifdef SQLITE_ENABLE_STAT3
|
||||
if( rc==SQLITE_OK ){
|
||||
int lookasideEnabled = db->lookaside.bEnabled;
|
||||
db->lookaside.bEnabled = 0;
|
||||
rc = loadStat3(db, sInfo.zDatabase);
|
||||
db->lookaside.bEnabled = lookasideEnabled;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -568,7 +568,7 @@ int sqlite3_backup_finish(sqlite3_backup *p){
|
||||
}
|
||||
|
||||
/* 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. */
|
||||
rc = (p->rc==SQLITE_DONE) ? SQLITE_OK : p->rc;
|
||||
|
156
src/btree.c
156
src/btree.c
@ -870,12 +870,10 @@ static u8 *findOverflowCell(MemPage *pPage, int iCell){
|
||||
assert( sqlite3_mutex_held(pPage->pBt->mutex) );
|
||||
for(i=pPage->nOverflow-1; i>=0; i--){
|
||||
int k;
|
||||
struct _OvflCell *pOvfl;
|
||||
pOvfl = &pPage->aOvfl[i];
|
||||
k = pOvfl->idx;
|
||||
k = pPage->aiOvfl[i];
|
||||
if( k<=iCell ){
|
||||
if( k==iCell ){
|
||||
return pOvfl->pCell;
|
||||
return pPage->apOvfl[i];
|
||||
}
|
||||
iCell--;
|
||||
}
|
||||
@ -1689,11 +1687,8 @@ static int btreeInvokeBusyHandler(void *pArg){
|
||||
** If zFilename is ":memory:" then an in-memory database is created
|
||||
** that is automatically destroyed when it is closed.
|
||||
**
|
||||
** The "flags" parameter is a bitmask that might contain bits
|
||||
** BTREE_OMIT_JOURNAL and/or BTREE_NO_READLOCK. The BTREE_NO_READLOCK
|
||||
** 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.
|
||||
** The "flags" parameter is a bitmask that might contain bits like
|
||||
** BTREE_OMIT_JOURNAL and/or BTREE_MEMORY.
|
||||
**
|
||||
** 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
|
||||
@ -1740,9 +1735,6 @@ int sqlite3BtreeOpen(
|
||||
/* A BTREE_SINGLE database is always a temporary and/or ephemeral */
|
||||
assert( (flags & BTREE_SINGLE)==0 || isTempDb );
|
||||
|
||||
if( db->flags & SQLITE_NoReadlock ){
|
||||
flags |= BTREE_NO_READLOCK;
|
||||
}
|
||||
if( isMemdb ){
|
||||
flags |= BTREE_MEMORY;
|
||||
}
|
||||
@ -2049,7 +2041,7 @@ int sqlite3BtreeClose(Btree *p){
|
||||
** The call to sqlite3BtreeRollback() drops any table-locks held by
|
||||
** this handle.
|
||||
*/
|
||||
sqlite3BtreeRollback(p);
|
||||
sqlite3BtreeRollback(p, SQLITE_OK);
|
||||
sqlite3BtreeLeave(p);
|
||||
|
||||
/* 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){
|
||||
BtCursor *p;
|
||||
if( pBtree==0 ) return;
|
||||
sqlite3BtreeEnter(pBtree);
|
||||
for(p=pBtree->pBt->pCursor; p; p=p->pNext){
|
||||
int i;
|
||||
@ -3310,25 +3303,20 @@ void sqlite3BtreeTripAllCursors(Btree *pBtree, int errCode){
|
||||
** This will release the write lock on the database file. If there
|
||||
** are no active cursors, it also releases the read lock.
|
||||
*/
|
||||
int sqlite3BtreeRollback(Btree *p){
|
||||
int sqlite3BtreeRollback(Btree *p, int tripCode){
|
||||
int rc;
|
||||
BtShared *pBt = p->pBt;
|
||||
MemPage *pPage1;
|
||||
|
||||
sqlite3BtreeEnter(p);
|
||||
rc = saveAllCursors(pBt, 0, 0);
|
||||
#ifndef SQLITE_OMIT_SHARED_CACHE
|
||||
if( rc!=SQLITE_OK ){
|
||||
/* This is a horrible situation. An IO or malloc() error occurred whilst
|
||||
** trying to save cursor positions. If this is an automatic rollback (as
|
||||
** the result of a constraint, malloc() failure or IO error) then
|
||||
** the cache may be internally inconsistent (not contain valid trees) so
|
||||
** 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);
|
||||
if( tripCode==SQLITE_OK ){
|
||||
rc = tripCode = saveAllCursors(pBt, 0, 0);
|
||||
}else{
|
||||
rc = SQLITE_OK;
|
||||
}
|
||||
if( tripCode ){
|
||||
sqlite3BtreeTripAllCursors(p, tripCode);
|
||||
}
|
||||
#endif
|
||||
btreeIntegrity(p);
|
||||
|
||||
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
|
||||
** 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
|
||||
** 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.
|
||||
** Allocating a new entry in pPage->aCell[] implies that
|
||||
** pPage->nOverflow is incremented.
|
||||
@ -5561,7 +5549,8 @@ static void insertCell(
|
||||
|
||||
assert( i>=0 && i<=pPage->nCell+pPage->nOverflow );
|
||||
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) );
|
||||
/* 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
|
||||
@ -5578,9 +5567,9 @@ static void insertCell(
|
||||
put4byte(pCell, iChild);
|
||||
}
|
||||
j = pPage->nOverflow++;
|
||||
assert( j<(int)(sizeof(pPage->aOvfl)/sizeof(pPage->aOvfl[0])) );
|
||||
pPage->aOvfl[j].pCell = pCell;
|
||||
pPage->aOvfl[j].idx = (u16)i;
|
||||
assert( j<(int)(sizeof(pPage->apOvfl)/sizeof(pPage->apOvfl[0])) );
|
||||
pPage->apOvfl[j] = pCell;
|
||||
pPage->aiOvfl[j] = (u16)i;
|
||||
}else{
|
||||
int rc = sqlite3PagerWrite(pPage->pDbPage);
|
||||
if( rc!=SQLITE_OK ){
|
||||
@ -5728,7 +5717,7 @@ static int balance_quick(MemPage *pParent, MemPage *pPage, u8 *pSpace){
|
||||
if( rc==SQLITE_OK ){
|
||||
|
||||
u8 *pOut = &pSpace[4];
|
||||
u8 *pCell = pPage->aOvfl[0].pCell;
|
||||
u8 *pCell = pPage->apOvfl[0];
|
||||
u16 szCell = cellSizePtr(pPage, pCell);
|
||||
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.
|
||||
**
|
||||
** 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().
|
||||
**
|
||||
@ -5975,7 +5964,7 @@ static int balance_nonroot(
|
||||
** is called (indirectly) from sqlite3BtreeDelete().
|
||||
*/
|
||||
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 ){
|
||||
return SQLITE_NOMEM;
|
||||
@ -6022,8 +6011,8 @@ static int balance_nonroot(
|
||||
nMaxCells += 1+apOld[i]->nCell+apOld[i]->nOverflow;
|
||||
if( (i--)==0 ) break;
|
||||
|
||||
if( i+nxDiv==pParent->aOvfl[0].idx && pParent->nOverflow ){
|
||||
apDiv[i] = pParent->aOvfl[0].pCell;
|
||||
if( i+nxDiv==pParent->aiOvfl[0] && pParent->nOverflow ){
|
||||
apDiv[i] = pParent->apOvfl[0];
|
||||
pgno = get4byte(apDiv[i]);
|
||||
szNew[i] = cellSizePtr(pParent, apDiv[i]);
|
||||
pParent->nOverflow = 0;
|
||||
@ -6464,7 +6453,7 @@ static int balance_nonroot(
|
||||
MemPage *pOld = apCopy[0];
|
||||
int nOverflow = pOld->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 */
|
||||
k = 0; /* Current 'new' sibling page */
|
||||
for(i=0; i<nCell; i++){
|
||||
@ -6478,14 +6467,14 @@ static int balance_nonroot(
|
||||
iNextOld = i + !leafData + pOld->nCell + pOld->nOverflow;
|
||||
if( pOld->nOverflow ){
|
||||
nOverflow = pOld->nOverflow;
|
||||
iOverflow = i + !leafData + pOld->aOvfl[0].idx;
|
||||
iOverflow = i + !leafData + pOld->aiOvfl[0];
|
||||
}
|
||||
isDivider = !leafData;
|
||||
}
|
||||
|
||||
assert(nOverflow>0 || iOverflow<i );
|
||||
assert(nOverflow<2 || pOld->aOvfl[0].idx==pOld->aOvfl[1].idx-1);
|
||||
assert(nOverflow<3 || pOld->aOvfl[1].idx==pOld->aOvfl[2].idx-1);
|
||||
assert(nOverflow<2 || pOld->aiOvfl[0]==pOld->aiOvfl[1]-1);
|
||||
assert(nOverflow<3 || pOld->aiOvfl[1]==pOld->aiOvfl[2]-1);
|
||||
if( i==iOverflow ){
|
||||
isDivider = 1;
|
||||
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));
|
||||
|
||||
/* 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;
|
||||
|
||||
/* 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
|
||||
if( pPage->hasData
|
||||
&& pPage->nOverflow==1
|
||||
&& pPage->aOvfl[0].idx==pPage->nCell
|
||||
&& pPage->aiOvfl[0]==pPage->nCell
|
||||
&& pParent->pgno!=1
|
||||
&& pParent->nCell==iIdx
|
||||
){
|
||||
@ -6797,13 +6789,6 @@ int sqlite3BtreeInsert(
|
||||
** blob of associated data. */
|
||||
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.
|
||||
**
|
||||
** 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);
|
||||
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 ){
|
||||
rc = btreeMoveto(pCur, pKey, nKey, appendBias, &loc);
|
||||
if( rc ) return rc;
|
||||
@ -6927,12 +6920,6 @@ int sqlite3BtreeDelete(BtCursor *pCur){
|
||||
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;
|
||||
iCellIdx = pCur->aiIdx[iCellDepth];
|
||||
pPage = pCur->apPage[iCellDepth];
|
||||
@ -6958,6 +6945,13 @@ int sqlite3BtreeDelete(BtCursor *pCur){
|
||||
*/
|
||||
rc = saveAllCursors(pBt, pCur->pgnoRoot, pCur);
|
||||
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);
|
||||
if( rc ) return rc;
|
||||
rc = clearCell(pPage, pCell);
|
||||
@ -7239,13 +7233,13 @@ int sqlite3BtreeClearTable(Btree *p, int iTable, int *pnChange){
|
||||
sqlite3BtreeEnter(p);
|
||||
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);
|
||||
|
||||
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);
|
||||
}
|
||||
sqlite3BtreeLeave(p);
|
||||
@ -7560,6 +7554,25 @@ static void checkAppendMsg(
|
||||
#endif /* 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
|
||||
** 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);
|
||||
return 1;
|
||||
}
|
||||
if( pCheck->anRef[iPage]==1 ){
|
||||
if( getPageReferenced(pCheck, iPage) ){
|
||||
checkAppendMsg(pCheck, zContext, "2nd reference to page %d", iPage);
|
||||
return 1;
|
||||
}
|
||||
return (pCheck->anRef[iPage]++)>1;
|
||||
setPageReferenced(pCheck, iPage);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifndef SQLITE_OMIT_AUTOVACUUM
|
||||
@ -7954,17 +7968,15 @@ char *sqlite3BtreeIntegrityCheck(
|
||||
sqlite3BtreeLeave(p);
|
||||
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;
|
||||
sqlite3BtreeLeave(p);
|
||||
return 0;
|
||||
}
|
||||
for(i=0; i<=sCheck.nPage; i++){ sCheck.anRef[i] = 0; }
|
||||
i = PENDING_BYTE_PAGE(pBt);
|
||||
if( i<=sCheck.nPage ){
|
||||
sCheck.anRef[i] = 1;
|
||||
}
|
||||
if( i<=sCheck.nPage ) setPageReferenced(&sCheck, i);
|
||||
sqlite3StrAccumInit(&sCheck.errMsg, zErr, sizeof(zErr), 20000);
|
||||
sCheck.errMsg.useMalloc = 2;
|
||||
|
||||
@ -7989,18 +8001,18 @@ char *sqlite3BtreeIntegrityCheck(
|
||||
*/
|
||||
for(i=1; i<=sCheck.nPage && sCheck.mxErr; i++){
|
||||
#ifdef SQLITE_OMIT_AUTOVACUUM
|
||||
if( sCheck.anRef[i]==0 ){
|
||||
if( getPageReferenced(&sCheck, i)==0 ){
|
||||
checkAppendMsg(&sCheck, 0, "Page %d is never used", i);
|
||||
}
|
||||
#else
|
||||
/* If the database supports auto-vacuum, make sure no tables contain
|
||||
** references to pointer-map pages.
|
||||
*/
|
||||
if( sCheck.anRef[i]==0 &&
|
||||
if( getPageReferenced(&sCheck, i)==0 &&
|
||||
(PTRMAP_PAGENO(pBt, i)!=i || !pBt->autoVacuum) ){
|
||||
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) ){
|
||||
checkAppendMsg(&sCheck, 0, "Pointer map page %d is referenced", i);
|
||||
}
|
||||
@ -8021,7 +8033,7 @@ char *sqlite3BtreeIntegrityCheck(
|
||||
/* Clean up and report errors.
|
||||
*/
|
||||
sqlite3BtreeLeave(p);
|
||||
sqlite3_free(sCheck.anRef);
|
||||
sqlite3_free(sCheck.aPgRef);
|
||||
if( sCheck.mallocFailed ){
|
||||
sqlite3StrAccumReset(&sCheck.errMsg);
|
||||
*pnErr = sCheck.nErr+1;
|
||||
|
@ -57,10 +57,9 @@ int sqlite3BtreeOpen(
|
||||
** pager.h.
|
||||
*/
|
||||
#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 4 /* This is an in-memory DB */
|
||||
#define BTREE_SINGLE 8 /* The file contains at most 1 b-tree */
|
||||
#define BTREE_UNORDERED 16 /* Use of a hash implementation is OK */
|
||||
#define BTREE_MEMORY 2 /* This is an in-memory DB */
|
||||
#define BTREE_SINGLE 4 /* The file contains at most 1 b-tree */
|
||||
#define BTREE_UNORDERED 8 /* Use of a hash implementation is OK */
|
||||
|
||||
int sqlite3BtreeClose(Btree*);
|
||||
int sqlite3BtreeSetCacheSize(Btree*,int);
|
||||
@ -78,7 +77,7 @@ int sqlite3BtreeBeginTrans(Btree*,int);
|
||||
int sqlite3BtreeCommitPhaseOne(Btree*, const char *zMaster);
|
||||
int sqlite3BtreeCommitPhaseTwo(Btree*, int);
|
||||
int sqlite3BtreeCommit(Btree*);
|
||||
int sqlite3BtreeRollback(Btree*);
|
||||
int sqlite3BtreeRollback(Btree*,int);
|
||||
int sqlite3BtreeBeginStmt(Btree*,int);
|
||||
int sqlite3BtreeCreateTable(Btree*, int*, int flags);
|
||||
int sqlite3BtreeIsInTrans(Btree*);
|
||||
|
@ -284,10 +284,9 @@ struct MemPage {
|
||||
u16 nFree; /* Number of free bytes on the page */
|
||||
u16 nCell; /* Number of cells on this page, local and ovfl */
|
||||
u16 maskPage; /* Mask for page offset */
|
||||
struct _OvflCell { /* Cells that will not fit on aData[] */
|
||||
u8 *pCell; /* Pointers to the body of the overflow cell */
|
||||
u16 idx; /* Insert this cell before idx-th non-overflow cell */
|
||||
} aOvfl[5];
|
||||
u16 aiOvfl[5]; /* Insert the i-th overflow cell before the aiOvfl-th
|
||||
** non-overflow cell */
|
||||
u8 *apOvfl[5]; /* Pointers to the body of overflow cells */
|
||||
BtShared *pBt; /* Pointer to BtShared that this page is part of */
|
||||
u8 *aData; /* Pointer to disk image of the page 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 */
|
||||
BtCursor *pNext, *pPrev; /* Forms a linked list of all cursors */
|
||||
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 */
|
||||
sqlite3_int64 cachedRowid; /* Next rowid cache. 0 means not valid */
|
||||
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 eState; /* One of the CURSOR_XXX constants (see below) */
|
||||
#ifndef SQLITE_OMIT_INCRBLOB
|
||||
Pgno *aOverflow; /* Cache of overflow page locations */
|
||||
u8 isIncrblobHandle; /* True if this cursor is an incr. io handle */
|
||||
#endif
|
||||
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
|
||||
** 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;
|
||||
struct IntegrityCk {
|
||||
BtShared *pBt; /* The tree being checked out */
|
||||
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 */
|
||||
int *anRef; /* Number of times each page is referenced */
|
||||
int mxErr; /* Stop accumulating errors when this reaches zero */
|
||||
int nErr; /* Number of messages written to zErrMsg so far */
|
||||
int mallocFailed; /* A memory allocation error has occurred */
|
||||
|
61
src/build.c
61
src/build.c
@ -537,7 +537,7 @@ void sqlite3DeleteTable(sqlite3 *db, Table *pTable){
|
||||
sqlite3DbFree(db, pTable->zColAff);
|
||||
sqlite3SelectDelete(db, pTable->pSelect);
|
||||
#ifndef SQLITE_OMIT_CHECK
|
||||
sqlite3ExprDelete(db, pTable->pCheck);
|
||||
sqlite3ExprListDelete(db, pTable->pCheck);
|
||||
#endif
|
||||
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||||
sqlite3VtabClear(db, pTable);
|
||||
@ -1200,15 +1200,17 @@ void sqlite3AddCheckConstraint(
|
||||
Parse *pParse, /* Parsing context */
|
||||
Expr *pCheckExpr /* The check expression */
|
||||
){
|
||||
sqlite3 *db = pParse->db;
|
||||
#ifndef SQLITE_OMIT_CHECK
|
||||
Table *pTab = pParse->pNewTable;
|
||||
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
|
||||
#endif
|
||||
{
|
||||
sqlite3ExprDelete(db, pCheckExpr);
|
||||
sqlite3ExprDelete(pParse->db, pCheckExpr);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1478,6 +1480,8 @@ void sqlite3EndTable(
|
||||
if( p->pCheck ){
|
||||
SrcList sSrc; /* Fake SrcList 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(&sSrc, 0, sizeof(sSrc));
|
||||
@ -1488,8 +1492,11 @@ void sqlite3EndTable(
|
||||
sNC.pParse = pParse;
|
||||
sNC.pSrcList = &sSrc;
|
||||
sNC.isCheck = 1;
|
||||
if( sqlite3ResolveExprNames(&sNC, p->pCheck) ){
|
||||
return;
|
||||
pList = p->pCheck;
|
||||
for(i=0; i<pList->nExpr; i++){
|
||||
if( sqlite3ResolveExprNames(&sNC, pList->a[i].pExpr) ){
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif /* !defined(SQLITE_OMIT_CHECK) */
|
||||
@ -1638,7 +1645,6 @@ void sqlite3EndTable(
|
||||
return;
|
||||
}
|
||||
pParse->pNewTable = 0;
|
||||
db->nTable++;
|
||||
db->flags |= SQLITE_InternChanges;
|
||||
|
||||
#ifndef SQLITE_OMIT_ALTERTABLE
|
||||
@ -3041,45 +3047,43 @@ exit_drop_index:
|
||||
}
|
||||
|
||||
/*
|
||||
** pArray is a pointer to an array of objects. Each object in the
|
||||
** array is szEntry bytes in size. This routine allocates a new
|
||||
** object on the end of the array.
|
||||
** pArray is a pointer to an array of objects. Each object in the
|
||||
** array is szEntry bytes in size. This routine uses sqlite3DbRealloc()
|
||||
** 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
|
||||
** the previously allocated size of the array. initSize is the
|
||||
** suggested initial array size allocation.
|
||||
** When this function is called, *pnEntry contains the current size of
|
||||
** the array (in entries - so the allocation is ((*pnEntry) * szEntry) bytes
|
||||
** 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
|
||||
** might be the same as the pArray parameter or it might be a different
|
||||
** pointer if the array was resized.
|
||||
** Otherwise, if the realloc() fails, *pIdx is set to -1, *pnEntry remains
|
||||
** unchanged and a copy of pArray returned.
|
||||
*/
|
||||
void *sqlite3ArrayAllocate(
|
||||
sqlite3 *db, /* Connection to notify of malloc failures */
|
||||
void *pArray, /* Array of objects. Might be reallocated */
|
||||
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 *pnAlloc, /* Current size of the allocation, in elements */
|
||||
int *pIdx /* Write the index of a new slot here */
|
||||
){
|
||||
char *z;
|
||||
if( *pnEntry >= *pnAlloc ){
|
||||
void *pNew;
|
||||
int newSize;
|
||||
newSize = (*pnAlloc)*2 + initSize;
|
||||
pNew = sqlite3DbRealloc(db, pArray, newSize*szEntry);
|
||||
int n = *pnEntry;
|
||||
if( (n & (n-1))==0 ){
|
||||
int sz = (n==0) ? 1 : 2*n;
|
||||
void *pNew = sqlite3DbRealloc(db, pArray, sz*szEntry);
|
||||
if( pNew==0 ){
|
||||
*pIdx = -1;
|
||||
return pArray;
|
||||
}
|
||||
*pnAlloc = sqlite3DbMallocSize(db, pNew)/szEntry;
|
||||
pArray = pNew;
|
||||
}
|
||||
z = (char*)pArray;
|
||||
memset(&z[*pnEntry * szEntry], 0, szEntry);
|
||||
*pIdx = *pnEntry;
|
||||
memset(&z[n * szEntry], 0, szEntry);
|
||||
*pIdx = n;
|
||||
++*pnEntry;
|
||||
return pArray;
|
||||
}
|
||||
@ -3095,15 +3099,12 @@ IdList *sqlite3IdListAppend(sqlite3 *db, IdList *pList, Token *pToken){
|
||||
if( pList==0 ){
|
||||
pList = sqlite3DbMallocZero(db, sizeof(IdList) );
|
||||
if( pList==0 ) return 0;
|
||||
pList->nAlloc = 0;
|
||||
}
|
||||
pList->a = sqlite3ArrayAllocate(
|
||||
db,
|
||||
pList->a,
|
||||
sizeof(pList->a[0]),
|
||||
5,
|
||||
&pList->nId,
|
||||
&pList->nAlloc,
|
||||
&i
|
||||
);
|
||||
if( i<0 ){
|
||||
|
@ -223,38 +223,57 @@ CollSeq *sqlite3FindCollSeq(
|
||||
** that uses encoding enc. The value returned indicates how well the
|
||||
** 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:
|
||||
**
|
||||
** 0: Not a match, or if nArg<0 and the function is has no implementation.
|
||||
** 1: A variable arguments function that prefers UTF-8 when a UTF-16
|
||||
** encoding is requested, or vice versa.
|
||||
** 2: A variable arguments function that uses UTF-16BE when UTF-16LE is
|
||||
** requested, or vice versa.
|
||||
** 3: A variable arguments function using the same text encoding.
|
||||
** 4: A function with the exact number of arguments requested that
|
||||
** 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.
|
||||
** 0: Not a match.
|
||||
** 1: UTF8/16 conversion required and function takes any number of arguments.
|
||||
** 2: UTF16 byte order change required and function takes any number of args.
|
||||
** 3: encoding matches and function takes any number of arguments
|
||||
** 4: UTF8/16 conversion required - argument count matches exactly
|
||||
** 5: UTF16 byte order conversion required - argument count matches exactly
|
||||
** 6: Perfect match: encoding and argument count match exactly.
|
||||
**
|
||||
** 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){
|
||||
int match = 0;
|
||||
if( p->nArg==-1 || p->nArg==nArg
|
||||
|| (nArg==-1 && (p->xFunc!=0 || p->xStep!=0))
|
||||
){
|
||||
#define FUNC_PERFECT_MATCH 6 /* The score for a perfect match */
|
||||
static int matchQuality(
|
||||
FuncDef *p, /* The function we are evaluating for match quality */
|
||||
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;
|
||||
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;
|
||||
}
|
||||
|
||||
@ -310,13 +329,12 @@ void sqlite3FuncDefInsert(
|
||||
**
|
||||
** If the createFlag argument is true, then a new (blank) FuncDef
|
||||
** structure is created and liked into the "db" structure if a
|
||||
** no matching function previously existed. When createFlag is true
|
||||
** and the nArg parameter is -1, then only a function that accepts
|
||||
** any number of arguments will be returned.
|
||||
** no matching function previously existed.
|
||||
**
|
||||
** If createFlag is false and nArg is -1, then the first valid
|
||||
** function found is returned. A function is valid if either xFunc
|
||||
** or xStep is non-zero.
|
||||
** If nArg is -2, then the first valid function found is returned. A
|
||||
** function is valid if either xFunc or xStep is non-zero. The nArg==(-2)
|
||||
** 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
|
||||
** 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 nArg, /* Number of arguments. -1 means any number */
|
||||
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 *pBest = 0; /* Best match found so far */
|
||||
int bestScore = 0; /* Score of best match */
|
||||
int h; /* Hash value */
|
||||
|
||||
|
||||
assert( nArg>=(-2) );
|
||||
assert( nArg>=(-1) || createFlag==0 );
|
||||
assert( enc==SQLITE_UTF8 || enc==SQLITE_UTF16LE || enc==SQLITE_UTF16BE );
|
||||
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
|
||||
** 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->zName = (char *)&pBest[1];
|
||||
pBest->nArg = (u16)nArg;
|
||||
|
@ -374,7 +374,7 @@ void sqlite3DeleteFrom(
|
||||
pParse, pTabList, pWhere, 0, 0, WHERE_DUPLICATES_OK
|
||||
);
|
||||
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);
|
||||
if( db->flags & SQLITE_CountRows ){
|
||||
sqlite3VdbeAddOp2(v, OP_AddImm, memCnt, 1);
|
||||
|
158
src/expr.c
158
src/expr.c
@ -484,23 +484,55 @@ Expr *sqlite3PExpr(
|
||||
Expr *pRight, /* Right operand */
|
||||
const Token *pToken /* Argument token */
|
||||
){
|
||||
Expr *p = sqlite3ExprAlloc(pParse->db, op, pToken, 1);
|
||||
sqlite3ExprAttachSubtrees(pParse->db, p, pLeft, pRight);
|
||||
Expr *p;
|
||||
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 ) {
|
||||
sqlite3ExprCheckHeight(pParse, p->nHeight);
|
||||
}
|
||||
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
|
||||
** 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){
|
||||
if( pLeft==0 ){
|
||||
return pRight;
|
||||
}else if( pRight==0 ){
|
||||
return pLeft;
|
||||
}else if( exprAlwaysFalse(pLeft) || exprAlwaysFalse(pRight) ){
|
||||
sqlite3ExprDelete(db, pLeft);
|
||||
sqlite3ExprDelete(db, pRight);
|
||||
return sqlite3ExprAlloc(db, TK_INTEGER, &sqlite3IntTokens[0], 0);
|
||||
}else{
|
||||
Expr *pNew = sqlite3ExprAlloc(db, TK_AND, 0, 0);
|
||||
sqlite3ExprAttachSubtrees(db, pNew, pLeft, pRight);
|
||||
@ -856,8 +888,9 @@ ExprList *sqlite3ExprListDup(sqlite3 *db, ExprList *p, int flags){
|
||||
pNew = sqlite3DbMallocRaw(db, sizeof(*pNew) );
|
||||
if( pNew==0 ) return 0;
|
||||
pNew->iECursor = 0;
|
||||
pNew->nExpr = pNew->nAlloc = p->nExpr;
|
||||
pNew->a = pItem = sqlite3DbMallocRaw(db, p->nExpr*sizeof(p->a[0]) );
|
||||
pNew->nExpr = i = p->nExpr;
|
||||
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 ){
|
||||
sqlite3DbFree(db, pNew);
|
||||
return 0;
|
||||
@ -925,12 +958,15 @@ IdList *sqlite3IdListDup(sqlite3 *db, IdList *p){
|
||||
if( p==0 ) return 0;
|
||||
pNew = sqlite3DbMallocRaw(db, sizeof(*pNew) );
|
||||
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]) );
|
||||
if( pNew->a==0 ){
|
||||
sqlite3DbFree(db, pNew);
|
||||
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++){
|
||||
struct IdList_item *pNewItem = &pNew->a[i];
|
||||
struct IdList_item *pOldItem = &p->a[i];
|
||||
@ -992,17 +1028,16 @@ ExprList *sqlite3ExprListAppend(
|
||||
if( pList==0 ){
|
||||
goto no_mem;
|
||||
}
|
||||
assert( pList->nAlloc==0 );
|
||||
}
|
||||
if( pList->nAlloc<=pList->nExpr ){
|
||||
pList->a = sqlite3DbMallocRaw(db, sizeof(pList->a[0]));
|
||||
if( pList->a==0 ) goto no_mem;
|
||||
}else if( (pList->nExpr & (pList->nExpr-1))==0 ){
|
||||
struct ExprList_item *a;
|
||||
int n = pList->nAlloc*2 + 4;
|
||||
a = sqlite3DbRealloc(db, pList->a, n*sizeof(pList->a[0]));
|
||||
assert( pList->nExpr>0 );
|
||||
a = sqlite3DbRealloc(db, pList->a, pList->nExpr*2*sizeof(pList->a[0]));
|
||||
if( a==0 ){
|
||||
goto no_mem;
|
||||
}
|
||||
pList->a = a;
|
||||
pList->nAlloc = sqlite3DbMallocSize(db, a)/sizeof(a[0]);
|
||||
}
|
||||
assert( pList->a!=0 );
|
||||
if( 1 ){
|
||||
@ -1093,8 +1128,7 @@ void sqlite3ExprListDelete(sqlite3 *db, ExprList *pList){
|
||||
int i;
|
||||
struct ExprList_item *pItem;
|
||||
if( pList==0 ) return;
|
||||
assert( pList->a!=0 || (pList->nExpr==0 && pList->nAlloc==0) );
|
||||
assert( pList->nExpr<=pList->nAlloc );
|
||||
assert( pList->a!=0 || pList->nExpr==0 );
|
||||
for(pItem=pList->a, i=0; i<pList->nExpr; i++, pItem++){
|
||||
sqlite3ExprDelete(db, pItem->pExpr);
|
||||
sqlite3DbFree(db, pItem->zName);
|
||||
@ -2030,15 +2064,6 @@ void sqlite3ExprCacheStore(Parse *pParse, int iTab, int iCol, int iReg){
|
||||
*/
|
||||
#ifndef NDEBUG
|
||||
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 );
|
||||
}
|
||||
#endif
|
||||
@ -2173,7 +2198,8 @@ int sqlite3ExprCodeGetColumn(
|
||||
Table *pTab, /* Description of the table we are reading from */
|
||||
int iColumn, /* Index of the table column */
|
||||
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;
|
||||
int i;
|
||||
@ -2188,7 +2214,11 @@ int sqlite3ExprCodeGetColumn(
|
||||
}
|
||||
assert( v!=0 );
|
||||
sqlite3ExprCodeGetColumnOfTable(v, pTab, iTable, iColumn, iReg);
|
||||
sqlite3ExprCacheStore(pParse, iTable, iColumn, iReg);
|
||||
if( p5 ){
|
||||
sqlite3VdbeChangeP5(v, p5);
|
||||
}else{
|
||||
sqlite3ExprCacheStore(pParse, iTable, iColumn, iReg);
|
||||
}
|
||||
return iReg;
|
||||
}
|
||||
|
||||
@ -2316,7 +2346,8 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){
|
||||
inReg = pExpr->iColumn + pParse->ckBase;
|
||||
}else{
|
||||
inReg = sqlite3ExprCodeGetColumn(pParse, pExpr->pTab,
|
||||
pExpr->iColumn, pExpr->iTable, target);
|
||||
pExpr->iColumn, pExpr->iTable, target,
|
||||
pExpr->op2);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -2593,6 +2624,25 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){
|
||||
|
||||
if( pFarg ){
|
||||
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 */
|
||||
sqlite3ExprCodeExprList(pParse, pFarg, r1, 1);
|
||||
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 ){
|
||||
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( strcmp(pA->u.zToken,pB->u.zToken)!=0 ){
|
||||
return 2;
|
||||
@ -3765,6 +3815,41 @@ int sqlite3ExprListCompare(ExprList *pA, ExprList *pB){
|
||||
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
|
||||
** the new element. Return a negative number if malloc fails.
|
||||
@ -3775,9 +3860,7 @@ static int addAggInfoColumn(sqlite3 *db, AggInfo *pInfo){
|
||||
db,
|
||||
pInfo->aCol,
|
||||
sizeof(pInfo->aCol[0]),
|
||||
3,
|
||||
&pInfo->nColumn,
|
||||
&pInfo->nColumnAlloc,
|
||||
&i
|
||||
);
|
||||
return i;
|
||||
@ -3793,9 +3876,7 @@ static int addAggInfoFunc(sqlite3 *db, AggInfo *pInfo){
|
||||
db,
|
||||
pInfo->aFunc,
|
||||
sizeof(pInfo->aFunc[0]),
|
||||
3,
|
||||
&pInfo->nFunc,
|
||||
&pInfo->nFuncAlloc,
|
||||
&i
|
||||
);
|
||||
return i;
|
||||
@ -3884,9 +3965,7 @@ static int analyzeAggregate(Walker *pWalker, Expr *pExpr){
|
||||
return WRC_Prune;
|
||||
}
|
||||
case TK_AGG_FUNCTION: {
|
||||
/* The pNC->nDepth==0 test causes aggregate functions in subqueries
|
||||
** to be ignored */
|
||||
if( pNC->nDepth==0 ){
|
||||
if( !sqlite3FunctionUsesOtherSrc(pExpr, pSrcList) ){
|
||||
/* Check to see if pExpr is a duplicate of another aggregate
|
||||
** function that is already in the pAggInfo structure
|
||||
*/
|
||||
@ -3930,15 +4009,9 @@ static int analyzeAggregate(Walker *pWalker, Expr *pExpr){
|
||||
return WRC_Continue;
|
||||
}
|
||||
static int analyzeAggregatesInSelect(Walker *pWalker, Select *pSelect){
|
||||
NameContext *pNC = pWalker->u.pNC;
|
||||
if( pNC->nDepth==0 ){
|
||||
pNC->nDepth++;
|
||||
sqlite3WalkSelect(pWalker, pSelect);
|
||||
pNC->nDepth--;
|
||||
return WRC_Prune;
|
||||
}else{
|
||||
return WRC_Continue;
|
||||
}
|
||||
UNUSED_PARAMETER(pWalker);
|
||||
UNUSED_PARAMETER(pSelect);
|
||||
return WRC_Continue;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -3951,6 +4024,7 @@ static int analyzeAggregatesInSelect(Walker *pWalker, Select *pSelect){
|
||||
*/
|
||||
void sqlite3ExprAnalyzeAggregates(NameContext *pNC, Expr *pExpr){
|
||||
Walker w;
|
||||
memset(&w, 0, sizeof(w));
|
||||
w.xExprCallback = analyzeAggregate;
|
||||
w.xSelectCallback = analyzeAggregatesInSelect;
|
||||
w.u.pNC = pNC;
|
||||
|
29
src/func.c
29
src/func.c
@ -28,6 +28,14 @@ static CollSeq *sqlite3GetFuncCollSeq(sqlite3_context *context){
|
||||
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
|
||||
*/
|
||||
@ -408,7 +416,7 @@ static void randomFunc(
|
||||
** 2s complement of that positive value. The end result can
|
||||
** therefore be no less than -9223372036854775807.
|
||||
*/
|
||||
r = -(r ^ (((sqlite3_int64)1)<<63));
|
||||
r = -(r & LARGEST_INT64);
|
||||
}
|
||||
sqlite3_result_int64(context, r);
|
||||
}
|
||||
@ -1334,11 +1342,12 @@ static void minmaxStep(
|
||||
Mem *pBest;
|
||||
UNUSED_PARAMETER(NotUsed);
|
||||
|
||||
if( sqlite3_value_type(argv[0])==SQLITE_NULL ) return;
|
||||
pBest = (Mem *)sqlite3_aggregate_context(context, sizeof(*pBest));
|
||||
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 cmp;
|
||||
CollSeq *pColl = sqlite3GetFuncCollSeq(context);
|
||||
@ -1354,6 +1363,8 @@ static void minmaxStep(
|
||||
cmp = sqlite3MemCompare(pBest, pArg, pColl);
|
||||
if( (max && cmp<0) || (!max && cmp>0) ){
|
||||
sqlite3VdbeMemCopy(pBest, pArg);
|
||||
}else{
|
||||
sqlite3SkipAccumulatorLoad(context);
|
||||
}
|
||||
}else{
|
||||
sqlite3VdbeMemCopy(pBest, pArg);
|
||||
@ -1363,7 +1374,7 @@ static void minMaxFinalize(sqlite3_context *context){
|
||||
sqlite3_value *pRes;
|
||||
pRes = (sqlite3_value *)sqlite3_aggregate_context(context, 0);
|
||||
if( pRes ){
|
||||
if( ALWAYS(pRes->flags) ){
|
||||
if( pRes->flags ){
|
||||
sqlite3_result_value(context, pRes);
|
||||
}
|
||||
sqlite3VdbeMemRelease(pRes);
|
||||
@ -1537,8 +1548,8 @@ void sqlite3RegisterGlobalFunctions(void){
|
||||
FUNCTION(max, -1, 1, 1, minmaxFunc ),
|
||||
FUNCTION(max, 0, 1, 1, 0 ),
|
||||
AGGREGATE(max, 1, 1, 1, minmaxStep, minMaxFinalize ),
|
||||
FUNCTION(typeof, 1, 0, 0, typeofFunc ),
|
||||
FUNCTION(length, 1, 0, 0, lengthFunc ),
|
||||
FUNCTION2(typeof, 1, 0, 0, typeofFunc, SQLITE_FUNC_TYPEOF),
|
||||
FUNCTION2(length, 1, 0, 0, lengthFunc, SQLITE_FUNC_LENGTH),
|
||||
FUNCTION(substr, 2, 0, 0, substrFunc ),
|
||||
FUNCTION(substr, 3, 0, 0, substrFunc ),
|
||||
FUNCTION(abs, 1, 0, 0, absFunc ),
|
||||
@ -1550,11 +1561,9 @@ void sqlite3RegisterGlobalFunctions(void){
|
||||
FUNCTION(lower, 1, 0, 0, lowerFunc ),
|
||||
FUNCTION(coalesce, 1, 0, 0, 0 ),
|
||||
FUNCTION(coalesce, 0, 0, 0, 0 ),
|
||||
/* FUNCTION(coalesce, -1, 0, 0, ifnullFunc ), */
|
||||
{-1,SQLITE_UTF8,SQLITE_FUNC_COALESCE,0,0,ifnullFunc,0,0,"coalesce",0,0},
|
||||
FUNCTION2(coalesce, -1, 0, 0, ifnullFunc, SQLITE_FUNC_COALESCE),
|
||||
FUNCTION(hex, 1, 0, 0, hexFunc ),
|
||||
/* FUNCTION(ifnull, 2, 0, 0, ifnullFunc ), */
|
||||
{2,SQLITE_UTF8,SQLITE_FUNC_COALESCE,0,0,ifnullFunc,0,0,"ifnull",0,0},
|
||||
FUNCTION2(ifnull, 2, 0, 0, ifnullFunc, SQLITE_FUNC_COALESCE),
|
||||
FUNCTION(random, 0, 0, 0, randomFunc ),
|
||||
FUNCTION(randomblob, 1, 0, 0, randomBlob ),
|
||||
FUNCTION(nullif, 2, 0, 1, nullifFunc ),
|
||||
|
39
src/insert.c
39
src/insert.c
@ -1157,9 +1157,11 @@ void sqlite3GenerateConstraintChecks(
|
||||
int regData; /* Register containing first data column */
|
||||
int iCur; /* Table cursor number */
|
||||
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 regOldRowid = (rowidChng && isUpdate) ? rowidChng : regRowid;
|
||||
|
||||
db = pParse->db;
|
||||
v = sqlite3GetVdbe(pParse);
|
||||
assert( v!=0 );
|
||||
assert( pTab->pSelect==0 ); /* This table is not a VIEW */
|
||||
@ -1192,7 +1194,7 @@ void sqlite3GenerateConstraintChecks(
|
||||
char *zMsg;
|
||||
sqlite3VdbeAddOp3(v, OP_HaltIfNull,
|
||||
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);
|
||||
sqlite3VdbeChangeP4(v, -1, zMsg, P4_DYNAMIC);
|
||||
break;
|
||||
@ -1214,18 +1216,27 @@ void sqlite3GenerateConstraintChecks(
|
||||
/* Test all CHECK constraints
|
||||
*/
|
||||
#ifndef SQLITE_OMIT_CHECK
|
||||
if( pTab->pCheck && (pParse->db->flags & SQLITE_IgnoreChecks)==0 ){
|
||||
int allOk = sqlite3VdbeMakeLabel(v);
|
||||
if( pTab->pCheck && (db->flags & SQLITE_IgnoreChecks)==0 ){
|
||||
ExprList *pCheck = pTab->pCheck;
|
||||
pParse->ckBase = regData;
|
||||
sqlite3ExprIfTrue(pParse, pTab->pCheck, allOk, SQLITE_JUMPIFNULL);
|
||||
onError = overrideError!=OE_Default ? overrideError : OE_Abort;
|
||||
if( onError==OE_Ignore ){
|
||||
sqlite3VdbeAddOp2(v, OP_Goto, 0, ignoreDest);
|
||||
}else{
|
||||
if( onError==OE_Replace ) onError = OE_Abort; /* IMP: R-15569-63625 */
|
||||
sqlite3HaltConstraint(pParse, onError, 0, 0);
|
||||
for(i=0; i<pCheck->nExpr; i++){
|
||||
int allOk = sqlite3VdbeMakeLabel(v);
|
||||
sqlite3ExprIfTrue(pParse, pCheck->a[i].pExpr, allOk, SQLITE_JUMPIFNULL);
|
||||
if( onError==OE_Ignore ){
|
||||
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) */
|
||||
|
||||
@ -1281,7 +1292,7 @@ void sqlite3GenerateConstraintChecks(
|
||||
** table.
|
||||
*/
|
||||
Trigger *pTrigger = 0;
|
||||
if( pParse->db->flags&SQLITE_RecTriggers ){
|
||||
if( db->flags&SQLITE_RecTriggers ){
|
||||
pTrigger = sqlite3TriggersExist(pParse, pTab, TK_DELETE, 0, 0);
|
||||
}
|
||||
if( pTrigger || sqlite3FkRequired(pParse, pTab, 0, 0) ){
|
||||
@ -1370,7 +1381,7 @@ void sqlite3GenerateConstraintChecks(
|
||||
char *zErr;
|
||||
|
||||
sqlite3StrAccumInit(&errMsg, 0, 0, 200);
|
||||
errMsg.db = pParse->db;
|
||||
errMsg.db = db;
|
||||
zSep = pIdx->nColumn>1 ? "columns " : "column ";
|
||||
for(j=0; j<pIdx->nColumn; j++){
|
||||
char *zCol = pTab->aCol[pIdx->aiColumn[j]].zName;
|
||||
@ -1394,7 +1405,7 @@ void sqlite3GenerateConstraintChecks(
|
||||
Trigger *pTrigger = 0;
|
||||
assert( onError==OE_Replace );
|
||||
sqlite3MultiWrite(pParse);
|
||||
if( pParse->db->flags&SQLITE_RecTriggers ){
|
||||
if( db->flags&SQLITE_RecTriggers ){
|
||||
pTrigger = sqlite3TriggersExist(pParse, pTab, TK_DELETE, 0, 0);
|
||||
}
|
||||
sqlite3GenerateRowDelete(
|
||||
@ -1724,7 +1735,7 @@ static int xferOptimization(
|
||||
}
|
||||
}
|
||||
#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 */
|
||||
}
|
||||
#endif
|
||||
|
116
src/main.c
116
src/main.c
@ -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 inTrans = 0;
|
||||
assert( sqlite3_mutex_held(db->mutex) );
|
||||
sqlite3BeginBenignMalloc();
|
||||
for(i=0; i<db->nDb; i++){
|
||||
if( db->aDb[i].pBt ){
|
||||
if( sqlite3BtreeIsInTrans(db->aDb[i].pBt) ){
|
||||
Btree *p = db->aDb[i].pBt;
|
||||
if( p ){
|
||||
if( sqlite3BtreeIsInTrans(p) ){
|
||||
inTrans = 1;
|
||||
}
|
||||
sqlite3BtreeRollback(db->aDb[i].pBt);
|
||||
sqlite3BtreeRollback(p, tripCode);
|
||||
db->aDb[i].inTrans = 0;
|
||||
}
|
||||
}
|
||||
@ -916,12 +920,21 @@ const char *sqlite3ErrStr(int rc){
|
||||
/* SQLITE_RANGE */ "bind or column index out of range",
|
||||
/* SQLITE_NOTADB */ "file is encrypted or is not a database",
|
||||
};
|
||||
rc &= 0xff;
|
||||
if( ALWAYS(rc>=0) && rc<(int)(sizeof(aMsg)/sizeof(aMsg[0])) && aMsg[rc]!=0 ){
|
||||
return aMsg[rc];
|
||||
}else{
|
||||
return "unknown error";
|
||||
const char *zErr = "unknown error";
|
||||
switch( rc ){
|
||||
case SQLITE_ABORT_ROLLBACK: {
|
||||
zErr = "abort due to ROLLBACK";
|
||||
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 */
|
||||
|
||||
/*** EXPERIMENTAL ***
|
||||
**
|
||||
** Register a function to be invoked when a transaction comments.
|
||||
/*
|
||||
** Register a function to be invoked when a transaction commits.
|
||||
** If the invoked function returns non-zero, then the commit becomes a
|
||||
** 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 rc = SQLITE_ERROR;
|
||||
int iDb;
|
||||
Btree *pBtree;
|
||||
|
||||
sqlite3_mutex_enter(db->mutex);
|
||||
if( zDbName==0 ){
|
||||
iDb = 0;
|
||||
}else{
|
||||
for(iDb=0; iDb<db->nDb; iDb++){
|
||||
if( strcmp(db->aDb[iDb].zName, zDbName)==0 ) break;
|
||||
}
|
||||
}
|
||||
if( iDb<db->nDb ){
|
||||
Btree *pBtree = db->aDb[iDb].pBt;
|
||||
if( pBtree ){
|
||||
Pager *pPager;
|
||||
sqlite3_file *fd;
|
||||
sqlite3BtreeEnter(pBtree);
|
||||
pPager = sqlite3BtreePager(pBtree);
|
||||
assert( pPager!=0 );
|
||||
fd = sqlite3PagerFile(pPager);
|
||||
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);
|
||||
pBtree = sqlite3DbNameToBtree(db, zDbName);
|
||||
if( pBtree ){
|
||||
Pager *pPager;
|
||||
sqlite3_file *fd;
|
||||
sqlite3BtreeEnter(pBtree);
|
||||
pPager = sqlite3BtreePager(pBtree);
|
||||
assert( pPager!=0 );
|
||||
fd = sqlite3PagerFile(pPager);
|
||||
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);
|
||||
}
|
||||
sqlite3_mutex_leave(db->mutex);
|
||||
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){
|
||||
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
|
||||
** connection.
|
||||
** Return the Btree pointer identified by zDbName. Return NULL if not found.
|
||||
*/
|
||||
const char *sqlite3_db_filename(sqlite3 *db, const char *zDbName){
|
||||
Btree *sqlite3DbNameToBtree(sqlite3 *db, const char *zDbName){
|
||||
int i;
|
||||
for(i=0; i<db->nDb; i++){
|
||||
if( db->aDb[i].pBt && sqlite3StrICmp(zDbName, db->aDb[i].zName)==0 ){
|
||||
return sqlite3BtreeGetFilename(db->aDb[i].pBt);
|
||||
if( db->aDb[i].pBt
|
||||
&& (zDbName==0 || sqlite3StrICmp(zDbName, db->aDb[i].zName)==0)
|
||||
){
|
||||
return db->aDb[i].pBt;
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
56
src/mem1.c
56
src/mem1.c
@ -15,7 +15,31 @@
|
||||
** to obtain the memory it needs.
|
||||
**
|
||||
** 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"
|
||||
|
||||
@ -27,17 +51,19 @@
|
||||
#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
|
||||
# define HAVE_MALLOC_USABLE_SIZE 1
|
||||
# define malloc_usable_size _msize
|
||||
#if defined(_MSC_VER) && !defined(SQLITE_WITHOUT_MSIZE)
|
||||
# define SQLITE_MALLOCSIZE _msize
|
||||
#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 <malloc/malloc.h>
|
||||
@ -52,17 +78,23 @@ static malloc_zone_t* _sqliteZone_;
|
||||
#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_FREE(x) free(x)
|
||||
#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
|
||||
#include <malloc.h>
|
||||
#define SQLITE_MALLOCSIZE(x) malloc_usable_size(x)
|
||||
# ifndef SQLITE_MALLOCSIZE
|
||||
# define SQLITE_MALLOCSIZE(x) malloc_usable_size(x)
|
||||
# endif
|
||||
#else
|
||||
#undef SQLITE_MALLOCSIZE
|
||||
# undef SQLITE_MALLOCSIZE
|
||||
#endif
|
||||
|
||||
#endif /* __APPLE__ or not __APPLE__ */
|
||||
@ -184,7 +216,7 @@ static int sqlite3MemRoundup(int n){
|
||||
** Initialize this module.
|
||||
*/
|
||||
static int sqlite3MemInit(void *NotUsed){
|
||||
#if defined(__APPLE__)
|
||||
#if defined(__APPLE__) && !defined(SQLITE_WITHOUT_ZONEMALLOC)
|
||||
int cpuCount;
|
||||
size_t len;
|
||||
if( _sqliteZone_ ){
|
||||
|
18
src/os.h
18
src/os.h
@ -91,11 +91,23 @@
|
||||
|
||||
/*
|
||||
** 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
|
||||
#else
|
||||
# define SQLITE_OS_WINNT 0
|
||||
#endif
|
||||
|
||||
/*
|
||||
|
139
src/os_unix.c
139
src/os_unix.c
@ -165,8 +165,8 @@
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Default permissions when creating auto proxy dir
|
||||
*/
|
||||
** Default permissions when creating auto proxy dir
|
||||
*/
|
||||
#ifndef SQLITE_DEFAULT_PROXYDIR_PERMISSIONS
|
||||
# define SQLITE_DEFAULT_PROXYDIR_PERMISSIONS 0755
|
||||
#endif
|
||||
@ -211,7 +211,7 @@ struct unixFile {
|
||||
unixInodeInfo *pInode; /* Info about locks on this inode */
|
||||
int h; /* The file descriptor */
|
||||
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 */
|
||||
void *lockingContext; /* Locking style specific state */
|
||||
UnixUnusedFd *pUnused; /* Pre-allocated UnixUnusedFd */
|
||||
@ -262,6 +262,7 @@ struct unixFile {
|
||||
#define UNIXFILE_DELETE 0x20 /* Delete on close */
|
||||
#define UNIXFILE_URI 0x40 /* Filename might have query parameters */
|
||||
#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
|
||||
@ -418,6 +419,12 @@ static struct unix_syscall {
|
||||
{ "rmdir", (sqlite3_syscall_ptr)rmdir, 0 },
|
||||
#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 */
|
||||
|
||||
/*
|
||||
@ -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){
|
||||
int rc;
|
||||
do{ rc = osOpen(z,f,m); }while( rc<0 && errno==EINTR );
|
||||
return rc;
|
||||
static int robust_open(const char *z, int f, mode_t m){
|
||||
int fd;
|
||||
mode_t m2;
|
||||
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';
|
||||
fd = robust_open(zDirname, O_RDONLY|O_BINARY, 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));
|
||||
}
|
||||
}
|
||||
@ -3389,7 +3427,7 @@ static int unixTruncate(sqlite3_file *id, i64 nByte){
|
||||
** actual file size after the operation may be larger than the requested
|
||||
** size).
|
||||
*/
|
||||
if( pFile->szChunk ){
|
||||
if( pFile->szChunk>0 ){
|
||||
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
|
||||
** 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 are subject to the current umask setting.
|
||||
** with the same permissions.
|
||||
*/
|
||||
if( osFstat(pDbFd->h, &sStat) && pInode->bProcessLock==0 ){
|
||||
rc = SQLITE_IOERR_FSTAT;
|
||||
@ -3901,10 +3938,19 @@ static int unixOpenSharedMemory(unixFile *pDbFd){
|
||||
}
|
||||
pShmNode->h = robust_open(zShmFilename, openFlags, (sStat.st_mode&0777));
|
||||
if( pShmNode->h<0 ){
|
||||
if( pShmNode->h<0 ){
|
||||
rc = unixLogError(SQLITE_CANTOPEN_BKPT, "open", zShmFilename);
|
||||
goto shm_open_err;
|
||||
}
|
||||
rc = unixLogError(SQLITE_CANTOPEN_BKPT, "open", zShmFilename);
|
||||
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.
|
||||
@ -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
|
||||
** returned and the value of *pMode is not modified.
|
||||
**
|
||||
** If the file being opened is a temporary file, it is always created with
|
||||
** the octal permissions 0600 (read/writable by owner only). If the file
|
||||
** is a database or master journal file, it is created with the permissions
|
||||
** mask SQLITE_DEFAULT_FILE_PERMISSIONS.
|
||||
**
|
||||
** Finally, if the file being opened is a WAL or regular journal file, then
|
||||
** In most cases cases, this routine sets *pMode to 0, which will become
|
||||
** an indication to robust_open() to create the file using
|
||||
** SQLITE_DEFAULT_FILE_PERMISSIONS adjusted by the umask.
|
||||
** But if the file being opened is a WAL or regular journal file, then
|
||||
** this function queries the file-system for the permissions on the
|
||||
** corresponding database file and sets *pMode to this value. Whenever
|
||||
** 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(
|
||||
const char *zPath, /* Path of file (possibly) being created */
|
||||
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 */
|
||||
*pMode = SQLITE_DEFAULT_FILE_PERMISSIONS;
|
||||
*pMode = 0;
|
||||
*pUid = 0;
|
||||
*pGid = 0;
|
||||
if( flags & (SQLITE_OPEN_WAL|SQLITE_OPEN_MAIN_JOURNAL) ){
|
||||
char zDb[MAX_PATHNAME+1]; /* Database file path */
|
||||
int nDb; /* Number of valid bytes in zDb */
|
||||
@ -4935,6 +4983,8 @@ static int findCreateFileMode(
|
||||
|
||||
if( 0==osStat(zDb, &sStat) ){
|
||||
*pMode = sStat.st_mode & 0777;
|
||||
*pUid = sStat.st_uid;
|
||||
*pGid = sStat.st_gid;
|
||||
}else{
|
||||
rc = SQLITE_IOERR_FSTAT;
|
||||
}
|
||||
@ -5081,7 +5131,9 @@ static int unixOpen(
|
||||
|
||||
if( fd<0 ){
|
||||
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 ){
|
||||
assert( !p->pUnused );
|
||||
assert( eType==SQLITE_OPEN_WAL || eType==SQLITE_OPEN_MAIN_JOURNAL );
|
||||
@ -5102,6 +5154,17 @@ static int unixOpen(
|
||||
rc = unixLogError(SQLITE_CANTOPEN_BKPT, "open", zName);
|
||||
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 );
|
||||
if( pOutFlags ){
|
||||
@ -5126,10 +5189,6 @@ static int unixOpen(
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef FD_CLOEXEC
|
||||
osFcntl(fd, F_SETFD, osFcntl(fd, F_GETFD, 0) | FD_CLOEXEC);
|
||||
#endif
|
||||
|
||||
noLock = eType!=SQLITE_OPEN_MAIN_DB;
|
||||
|
||||
|
||||
@ -5413,7 +5472,7 @@ static int unixRandomness(sqlite3_vfs *NotUsed, int nBuf, char *zBuf){
|
||||
memset(zBuf, 0, nBuf);
|
||||
#if !defined(SQLITE_TEST)
|
||||
{
|
||||
int pid, fd;
|
||||
int pid, fd, got;
|
||||
fd = robust_open("/dev/urandom", O_RDONLY, 0);
|
||||
if( fd<0 ){
|
||||
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 );
|
||||
nBuf = sizeof(t) + sizeof(pid);
|
||||
}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__);
|
||||
}
|
||||
}
|
||||
@ -5828,17 +5887,17 @@ static int proxyCreateUnixFile(
|
||||
}
|
||||
}
|
||||
if( fd<0 ){
|
||||
fd = robust_open(path, openFlags, SQLITE_DEFAULT_FILE_PERMISSIONS);
|
||||
fd = robust_open(path, openFlags, 0);
|
||||
terrno = errno;
|
||||
if( fd<0 && errno==ENOENT && islockfile ){
|
||||
if( proxyCreateLockPath(path) == SQLITE_OK ){
|
||||
fd = robust_open(path, openFlags, SQLITE_DEFAULT_FILE_PERMISSIONS);
|
||||
fd = robust_open(path, openFlags, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
if( fd<0 ){
|
||||
openFlags = O_RDONLY;
|
||||
fd = robust_open(path, openFlags, SQLITE_DEFAULT_FILE_PERMISSIONS);
|
||||
fd = robust_open(path, openFlags, 0);
|
||||
terrno = errno;
|
||||
}
|
||||
if( fd<0 ){
|
||||
@ -5962,8 +6021,7 @@ static int proxyBreakConchLock(unixFile *pFile, uuid_t myHostID){
|
||||
goto end_breaklock;
|
||||
}
|
||||
/* write it out to the temporary break file */
|
||||
fd = robust_open(tPath, (O_RDWR|O_CREAT|O_EXCL),
|
||||
SQLITE_DEFAULT_FILE_PERMISSIONS);
|
||||
fd = robust_open(tPath, (O_RDWR|O_CREAT|O_EXCL), 0);
|
||||
if( fd<0 ){
|
||||
sqlite3_snprintf(sizeof(errmsg), errmsg, "create failed (%d)", errno);
|
||||
goto end_breaklock;
|
||||
@ -6240,8 +6298,7 @@ static int proxyTakeConch(unixFile *pFile){
|
||||
robust_close(pFile, pFile->h, __LINE__);
|
||||
}
|
||||
pFile->h = -1;
|
||||
fd = robust_open(pCtx->dbPath, pFile->openFlags,
|
||||
SQLITE_DEFAULT_FILE_PERMISSIONS);
|
||||
fd = robust_open(pCtx->dbPath, pFile->openFlags, 0);
|
||||
OSTRACE(("TRANSPROXY: OPEN %d\n", fd));
|
||||
if( fd>=0 ){
|
||||
pFile->h = fd;
|
||||
@ -6810,7 +6867,7 @@ int sqlite3_os_init(void){
|
||||
|
||||
/* Double-check that the aSyscall[] array has been constructed
|
||||
** correctly. See ticket [bb3a86e890c8e96ab] */
|
||||
assert( ArraySize(aSyscall)==20 );
|
||||
assert( ArraySize(aSyscall)==22 );
|
||||
|
||||
/* Register all VFSes defined in the aVfs[] array */
|
||||
for(i=0; i<(sizeof(aVfs)/sizeof(sqlite3_vfs)); i++){
|
||||
|
136
src/os_win.c
136
src/os_win.c
@ -1616,6 +1616,9 @@ static int winClose(sqlite3_file *id){
|
||||
}
|
||||
#endif
|
||||
OSTRACE(("CLOSE %d %s\n", pFile->h, rc ? "ok" : "failed"));
|
||||
if( rc ){
|
||||
pFile->h = NULL;
|
||||
}
|
||||
OpenCounter(-1);
|
||||
return rc ? SQLITE_OK
|
||||
: winLogError(SQLITE_IOERR_CLOSE, osGetLastError(),
|
||||
@ -1633,6 +1636,9 @@ static int winRead(
|
||||
int amt, /* Number of bytes to read */
|
||||
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 */
|
||||
DWORD nRead; /* Number of bytes actually read from file */
|
||||
int nRetry = 0; /* Number of retrys */
|
||||
@ -1641,10 +1647,18 @@ static int winRead(
|
||||
SimulateIOError(return SQLITE_IOERR_READ);
|
||||
OSTRACE(("READ %d lock=%d\n", pFile->h, pFile->locktype));
|
||||
|
||||
#if SQLITE_OS_WINCE
|
||||
if( seekWinFile(pFile, offset) ){
|
||||
return SQLITE_FULL;
|
||||
}
|
||||
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;
|
||||
if( retryIoerr(&nRetry, &lastErrno) ) continue;
|
||||
pFile->lastErrno = lastErrno;
|
||||
@ -1671,7 +1685,7 @@ static int winWrite(
|
||||
int amt, /* Number of bytes to write */
|
||||
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 */
|
||||
int nRetry = 0; /* Number of retries */
|
||||
|
||||
@ -1682,19 +1696,44 @@ static int winWrite(
|
||||
|
||||
OSTRACE(("WRITE %d lock=%d\n", pFile->h, pFile->locktype));
|
||||
|
||||
#if SQLITE_OS_WINCE
|
||||
rc = seekWinFile(pFile, offset);
|
||||
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 */
|
||||
int nRem = amt; /* Number of bytes yet to be written */
|
||||
DWORD nWrite; /* Bytes written by each WriteFile() call */
|
||||
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 ){
|
||||
#if SQLITE_OS_WINCE
|
||||
if( !osWriteFile(pFile->h, aRem, nRem, &nWrite, 0) ){
|
||||
#else
|
||||
if( !osWriteFile(pFile->h, aRem, nRem, &nWrite, &overlapped) ){
|
||||
#endif
|
||||
if( retryIoerr(&nRetry, &lastErrno) ) continue;
|
||||
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;
|
||||
nRem -= nWrite;
|
||||
}
|
||||
@ -2992,6 +3031,35 @@ static int getTempname(int nBuf, char *zBuf){
|
||||
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.
|
||||
*/
|
||||
@ -3098,6 +3166,11 @@ static int winOpen(
|
||||
return SQLITE_IOERR_NOMEM;
|
||||
}
|
||||
|
||||
if( winIsDir(zConverted) ){
|
||||
sqlite3_free(zConverted);
|
||||
return SQLITE_CANTOPEN_ISDIR;
|
||||
}
|
||||
|
||||
if( isReadWrite ){
|
||||
dwDesiredAccess = GENERIC_READ | GENERIC_WRITE;
|
||||
}else{
|
||||
@ -3147,11 +3220,9 @@ static int winOpen(
|
||||
dwCreationDisposition,
|
||||
dwFlagsAndAttributes,
|
||||
NULL))==INVALID_HANDLE_VALUE &&
|
||||
retryIoerr(&cnt, &lastErrno) ){}
|
||||
/* 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,
|
||||
** it's important to not reference them for WINCE builds.
|
||||
*/
|
||||
retryIoerr(&cnt, &lastErrno) ){
|
||||
/* Noop */
|
||||
}
|
||||
#if SQLITE_OS_WINCE==0
|
||||
}else{
|
||||
while( (h = osCreateFileA((LPCSTR)zConverted,
|
||||
@ -3160,7 +3231,9 @@ static int winOpen(
|
||||
dwCreationDisposition,
|
||||
dwFlagsAndAttributes,
|
||||
NULL))==INVALID_HANDLE_VALUE &&
|
||||
retryIoerr(&cnt, &lastErrno) ){}
|
||||
retryIoerr(&cnt, &lastErrno) ){
|
||||
/* Noop */
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -3240,6 +3313,7 @@ static int winDelete(
|
||||
){
|
||||
int cnt = 0;
|
||||
int rc;
|
||||
DWORD attr;
|
||||
DWORD lastErrno;
|
||||
void *zConverted;
|
||||
UNUSED_PARAMETER(pVfs);
|
||||
@ -3251,20 +3325,50 @@ static int winDelete(
|
||||
return SQLITE_IOERR_NOMEM;
|
||||
}
|
||||
if( isNT() ){
|
||||
rc = 1;
|
||||
while( osGetFileAttributesW(zConverted)!=INVALID_FILE_ATTRIBUTES &&
|
||||
(rc = osDeleteFileW(zConverted))==0 && retryIoerr(&cnt, &lastErrno) ){}
|
||||
rc = rc ? SQLITE_OK : SQLITE_ERROR;
|
||||
do {
|
||||
attr = osGetFileAttributesW(zConverted);
|
||||
if ( attr==INVALID_FILE_ATTRIBUTES ){
|
||||
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.
|
||||
** 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
|
||||
}else{
|
||||
rc = 1;
|
||||
while( osGetFileAttributesA(zConverted)!=INVALID_FILE_ATTRIBUTES &&
|
||||
(rc = osDeleteFileA(zConverted))==0 && retryIoerr(&cnt, &lastErrno) ){}
|
||||
rc = rc ? SQLITE_OK : SQLITE_ERROR;
|
||||
do {
|
||||
attr = osGetFileAttributesA(zConverted);
|
||||
if ( attr==INVALID_FILE_ATTRIBUTES ){
|
||||
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
|
||||
}
|
||||
if( rc ){
|
||||
|
92
src/pager.c
92
src/pager.c
@ -612,7 +612,6 @@ struct Pager {
|
||||
u8 exclusiveMode; /* Boolean. True if locking_mode==EXCLUSIVE */
|
||||
u8 journalMode; /* One of the PAGER_JOURNALMODE_* values */
|
||||
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 fullSync; /* Do extra syncs of the journal for robustness */
|
||||
u8 ckptSyncFlags; /* SYNC_NORMAL or SYNC_FULL for checkpoint */
|
||||
@ -671,9 +670,9 @@ struct Pager {
|
||||
char *zJournal; /* Name of the journal file */
|
||||
int (*xBusyHandler)(void*); /* Function to call when busy */
|
||||
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
|
||||
int nRead, nWrite; /* Database pages read/written */
|
||||
int nRead; /* Database pages read */
|
||||
#endif
|
||||
void (*xReiniter)(DbPage*); /* Call this routine when reloading pages */
|
||||
#ifdef SQLITE_HAS_CODEC
|
||||
@ -690,6 +689,15 @@ struct Pager {
|
||||
#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
|
||||
** testing purposes only. These variables do not exist in
|
||||
@ -860,7 +868,7 @@ static int assert_pager_state(Pager *p){
|
||||
case PAGER_READER:
|
||||
assert( pPager->errCode==SQLITE_OK );
|
||||
assert( p->eLock!=UNKNOWN_LOCK );
|
||||
assert( p->eLock>=SHARED_LOCK || p->noReadlock );
|
||||
assert( p->eLock>=SHARED_LOCK );
|
||||
break;
|
||||
|
||||
case PAGER_WRITER_LOCKED:
|
||||
@ -2972,6 +2980,7 @@ static int pagerWalFrames(
|
||||
int isCommit /* True if this is a commit */
|
||||
){
|
||||
int rc; /* Return code */
|
||||
int nList; /* Number of pages in pList */
|
||||
#if defined(SQLITE_DEBUG) || defined(SQLITE_CHECK_PAGES)
|
||||
PgHdr *p; /* For looping over pages */
|
||||
#endif
|
||||
@ -2985,6 +2994,7 @@ static int pagerWalFrames(
|
||||
}
|
||||
#endif
|
||||
|
||||
assert( pList->pDirty==0 || isCommit );
|
||||
if( isCommit ){
|
||||
/* 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.
|
||||
@ -2992,11 +3002,18 @@ static int pagerWalFrames(
|
||||
** list here. */
|
||||
PgHdr *p;
|
||||
PgHdr **ppNext = &pList;
|
||||
for(p=pList; (*ppNext = p); p=p->pDirty){
|
||||
if( p->pgno<=nTruncate ) ppNext = &p->pDirty;
|
||||
nList = 0;
|
||||
for(p=pList; (*ppNext = p)!=0; p=p->pDirty){
|
||||
if( p->pgno<=nTruncate ){
|
||||
ppNext = &p->pDirty;
|
||||
nList++;
|
||||
}
|
||||
}
|
||||
assert( pList );
|
||||
}else{
|
||||
nList = 1;
|
||||
}
|
||||
pPager->aStat[PAGER_STAT_WRITE] += nList;
|
||||
|
||||
if( pList->pgno==1 ) pager_write_changecounter(pList);
|
||||
rc = sqlite3WalFrames(pPager->pWal,
|
||||
@ -3069,7 +3086,7 @@ static int pagerPagecount(Pager *pPager, Pgno *pnPage){
|
||||
** contains no valid committed transactions.
|
||||
*/
|
||||
assert( pPager->eState==PAGER_OPEN );
|
||||
assert( pPager->eLock>=SHARED_LOCK || pPager->noReadlock );
|
||||
assert( pPager->eLock>=SHARED_LOCK );
|
||||
nPage = sqlite3WalDbsize(pPager->pWal);
|
||||
|
||||
/* 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){
|
||||
int rc = SQLITE_OK;
|
||||
assert( pPager->eState==PAGER_OPEN );
|
||||
assert( pPager->eLock>=SHARED_LOCK || pPager->noReadlock );
|
||||
assert( pPager->eLock>=SHARED_LOCK );
|
||||
|
||||
if( !pPager->tempFile ){
|
||||
int isWal; /* True if WAL file exists */
|
||||
@ -4064,6 +4081,7 @@ static int pager_write_pagelist(Pager *pPager, PgHdr *pList){
|
||||
if( pgno>pPager->dbFileSize ){
|
||||
pPager->dbFileSize = pgno;
|
||||
}
|
||||
pPager->aStat[PAGER_STAT_WRITE]++;
|
||||
|
||||
/* Update any backup objects copying the contents of this pager. */
|
||||
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)));
|
||||
IOTRACE(("PGOUT %p %d\n", pPager, pgno));
|
||||
PAGER_INCR(sqlite3_pager_writedb_count);
|
||||
PAGER_INCR(pPager->nWrite);
|
||||
}else{
|
||||
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
|
||||
** 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
|
||||
** 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 */
|
||||
int nPathname = 0; /* Number of bytes in zPathname */
|
||||
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 */
|
||||
u32 szPageDflt = SQLITE_DEFAULT_PAGE_SIZE; /* Default page size */
|
||||
const char *zUri = 0; /* URI args to copy */
|
||||
@ -4525,7 +4541,6 @@ int sqlite3PagerOpen(
|
||||
IOTRACE(("OPEN %p %s\n", pPager, pPager->zFilename))
|
||||
|
||||
pPager->useJournal = (u8)useJournal;
|
||||
pPager->noReadlock = (noReadlock && readOnly) ?1:0;
|
||||
/* pPager->stmtOpen = 0; */
|
||||
/* pPager->stmtInUse = 0; */
|
||||
/* pPager->nRef = 0; */
|
||||
@ -4747,14 +4762,11 @@ int sqlite3PagerSharedLock(Pager *pPager){
|
||||
int bHotJournal = 1; /* True if there exists a hot journal-file */
|
||||
|
||||
assert( !MEMDB );
|
||||
assert( pPager->noReadlock==0 || pPager->readOnly );
|
||||
|
||||
if( pPager->noReadlock==0 ){
|
||||
rc = pager_wait_on_lock(pPager, SHARED_LOCK);
|
||||
if( rc!=SQLITE_OK ){
|
||||
assert( pPager->eLock==NO_LOCK || pPager->eLock==UNKNOWN_LOCK );
|
||||
goto failed;
|
||||
}
|
||||
rc = pager_wait_on_lock(pPager, SHARED_LOCK);
|
||||
if( rc!=SQLITE_OK ){
|
||||
assert( pPager->eLock==NO_LOCK || pPager->eLock==UNKNOWN_LOCK );
|
||||
goto failed;
|
||||
}
|
||||
|
||||
/* 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
|
||||
** the page. Return without further ado. */
|
||||
assert( pgno<=PAGER_MAX_PGNO && pgno!=PAGER_MJ_PGNO(pPager) );
|
||||
pPager->nHit++;
|
||||
pPager->aStat[PAGER_STAT_HIT]++;
|
||||
return SQLITE_OK;
|
||||
|
||||
}else{
|
||||
@ -5077,7 +5089,7 @@ int sqlite3PagerAcquire(
|
||||
IOTRACE(("ZERO %p %d\n", pPager, pgno));
|
||||
}else{
|
||||
assert( pPg->pPager==pPager );
|
||||
pPager->nMiss++;
|
||||
pPager->aStat[PAGER_STAT_MISS]++;
|
||||
rc = readDbPage(pPg);
|
||||
if( rc!=SQLITE_OK ){
|
||||
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);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3OsWrite(pPager->fd, zBuf, pPager->pageSize, 0);
|
||||
pPager->aStat[PAGER_STAT_WRITE]++;
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
pPager->changeCountDone = 1;
|
||||
@ -6105,11 +6118,11 @@ int *sqlite3PagerStats(Pager *pPager){
|
||||
a[3] = pPager->eState==PAGER_OPEN ? -1 : (int) pPager->dbSize;
|
||||
a[4] = pPager->eState;
|
||||
a[5] = pPager->errCode;
|
||||
a[6] = pPager->nHit;
|
||||
a[7] = pPager->nMiss;
|
||||
a[6] = pPager->aStat[PAGER_STAT_HIT];
|
||||
a[7] = pPager->aStat[PAGER_STAT_MISS];
|
||||
a[8] = 0; /* Used to be pPager->nOvfl */
|
||||
a[9] = pPager->nRead;
|
||||
a[10] = pPager->nWrite;
|
||||
a[10] = pPager->aStat[PAGER_STAT_WRITE];
|
||||
return a;
|
||||
}
|
||||
#endif
|
||||
@ -6122,20 +6135,19 @@ int *sqlite3PagerStats(Pager *pPager){
|
||||
** returning.
|
||||
*/
|
||||
void sqlite3PagerCacheStat(Pager *pPager, int eStat, int reset, int *pnVal){
|
||||
int *piStat;
|
||||
|
||||
assert( eStat==SQLITE_DBSTATUS_CACHE_HIT
|
||||
|| 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 ){
|
||||
*piStat = 0;
|
||||
pPager->aStat[eStat - SQLITE_DBSTATUS_CACHE_HIT] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@ -6762,7 +6774,7 @@ static int pagerOpenWal(Pager *pPager){
|
||||
int rc = SQLITE_OK;
|
||||
|
||||
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
|
||||
** heap-memory for the wal-index instead of the VFS shared-memory
|
||||
@ -6877,6 +6889,20 @@ int sqlite3PagerCloseWal(Pager *pPager){
|
||||
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
|
||||
/*
|
||||
** This function is called by the wal module when writing page content
|
||||
|
@ -58,8 +58,7 @@ typedef struct PgHdr DbPage;
|
||||
** 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_NO_READLOCK 0x0002 /* Omit readlocks on readonly files */
|
||||
#define PAGER_MEMORY 0x0004 /* In-memory database */
|
||||
#define PAGER_MEMORY 0x0002 /* In-memory database */
|
||||
|
||||
/*
|
||||
** Valid values for the second argument to sqlite3PagerLockingMode().
|
||||
@ -144,6 +143,9 @@ int sqlite3PagerWalSupported(Pager *pPager);
|
||||
int sqlite3PagerWalCallback(Pager *pPager);
|
||||
int sqlite3PagerOpenWal(Pager *pPager, int *pisOpen);
|
||||
int sqlite3PagerCloseWal(Pager *pPager);
|
||||
#ifdef SQLITE_ENABLE_ZIPVFS
|
||||
int sqlite3PagerWalFramesize(Pager *pPager);
|
||||
#endif
|
||||
|
||||
/* Functions used to query pager state and configuration. */
|
||||
u8 sqlite3PagerIsreadonly(Pager*);
|
||||
|
118
src/parse.y
118
src/parse.y
@ -75,7 +75,7 @@ struct LimitVal {
|
||||
*/
|
||||
struct LikeOp {
|
||||
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; };
|
||||
|
||||
/*
|
||||
** One or more VALUES claues
|
||||
*/
|
||||
struct ValueList {
|
||||
ExprList *pList;
|
||||
Select *pSelect;
|
||||
};
|
||||
|
||||
} // end %include
|
||||
|
||||
// Input is a single SQL command
|
||||
@ -177,6 +185,7 @@ column(A) ::= columnid(X) type carglist. {
|
||||
columnid(A) ::= nm(X). {
|
||||
sqlite3AddColumn(pParse,&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
|
||||
// column name and column type in a CREATE TABLE statement.
|
||||
//
|
||||
carglist ::= carglist carg.
|
||||
carglist ::= carglist ccons.
|
||||
carglist ::= .
|
||||
carg ::= CONSTRAINT nm ccons.
|
||||
carg ::= ccons.
|
||||
ccons ::= CONSTRAINT nm(X). {pParse->constraintName = X;}
|
||||
ccons ::= DEFAULT term(X). {sqlite3AddDefaultValue(pParse,&X);}
|
||||
ccons ::= DEFAULT LP expr(X) RP. {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 IMMEDIATE. {A = 0;}
|
||||
|
||||
// For the time being, the only constraint we care about is the primary
|
||||
// key and UNIQUE. Both create indices.
|
||||
//
|
||||
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_opt(A) ::= . {A.n = 0; A.z = 0;}
|
||||
conslist_opt(A) ::= COMMA(X) conslist. {A = X;}
|
||||
conslist ::= conslist tconscomma 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).
|
||||
{sqlite3AddPrimaryKey(pParse,X,R,I,0);}
|
||||
tcons ::= UNIQUE LP idxlist(X) RP onconf(R).
|
||||
@ -573,20 +579,17 @@ using_opt(U) ::= . {U = 0;}
|
||||
%destructor orderby_opt {sqlite3ExprListDelete(pParse->db, $$);}
|
||||
%type sortlist {ExprList*}
|
||||
%destructor sortlist {sqlite3ExprListDelete(pParse->db, $$);}
|
||||
%type sortitem {Expr*}
|
||||
%destructor sortitem {sqlite3ExprDelete(pParse->db, $$);}
|
||||
|
||||
orderby_opt(A) ::= . {A = 0;}
|
||||
orderby_opt(A) ::= ORDER BY sortlist(X). {A = X;}
|
||||
sortlist(A) ::= sortlist(X) COMMA sortitem(Y) sortorder(Z). {
|
||||
A = sqlite3ExprListAppend(pParse,X,Y);
|
||||
sortlist(A) ::= sortlist(X) COMMA expr(Y) sortorder(Z). {
|
||||
A = sqlite3ExprListAppend(pParse,X,Y.pExpr);
|
||||
if( A ) A->a[A->nExpr-1].sortOrder = (u8)Z;
|
||||
}
|
||||
sortlist(A) ::= sortitem(Y) sortorder(Z). {
|
||||
A = sqlite3ExprListAppend(pParse,0,Y);
|
||||
sortlist(A) ::= expr(Y) sortorder(Z). {
|
||||
A = sqlite3ExprListAppend(pParse,0,Y.pExpr);
|
||||
if( A && ALWAYS(A->a) ) A->a[0].sortOrder = (u8)Z;
|
||||
}
|
||||
sortitem(A) ::= expr(X). {A = X.pExpr;}
|
||||
|
||||
%type sortorder {int}
|
||||
|
||||
@ -679,9 +682,8 @@ setlist(A) ::= nm(X) EQ expr(Y). {
|
||||
|
||||
////////////////////////// The INSERT command /////////////////////////////////
|
||||
//
|
||||
cmd ::= insert_cmd(R) INTO fullname(X) inscollist_opt(F)
|
||||
VALUES LP itemlist(Y) RP.
|
||||
{sqlite3Insert(pParse, X, Y, 0, F, R);}
|
||||
cmd ::= insert_cmd(R) INTO fullname(X) inscollist_opt(F) valuelist(Y).
|
||||
{sqlite3Insert(pParse, X, Y.pList, Y.pSelect, F, R);}
|
||||
cmd ::= insert_cmd(R) INTO fullname(X) inscollist_opt(F) select(S).
|
||||
{sqlite3Insert(pParse, X, 0, S, F, R);}
|
||||
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) ::= 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*}
|
||||
%destructor itemlist {sqlite3ExprListDelete(pParse->db, $$);}
|
||||
|
||||
itemlist(A) ::= itemlist(X) COMMA expr(Y).
|
||||
{A = sqlite3ExprListAppend(pParse,X,Y.pExpr);}
|
||||
itemlist(A) ::= expr(X).
|
||||
{A = sqlite3ExprListAppend(pParse,0,X.pExpr);}
|
||||
// Since a list of VALUEs is inplemented as a compound SELECT, we have
|
||||
// to disable the value list option if compound SELECTs are disabled.
|
||||
%ifndef SQLITE_OMIT_COMPOUND_SELECT
|
||||
valuelist(A) ::= valuelist(X) COMMA LP exprlist(Y) RP. {
|
||||
Select *pRight = sqlite3SelectNew(pParse, Y, 0, 0, 0, 0, 0, 0, 0, 0);
|
||||
if( X.pList ){
|
||||
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*}
|
||||
%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);}
|
||||
expr(A) ::= expr(X) CONCAT(OP) expr(Y). {spanBinaryExpr(&A,pParse,@OP,&X,&Y);}
|
||||
%type likeop {struct LikeOp}
|
||||
likeop(A) ::= LIKE_KW(X). {A.eOperator = X; A.not = 0;}
|
||||
likeop(A) ::= NOT LIKE_KW(X). {A.eOperator = X; A.not = 1;}
|
||||
likeop(A) ::= MATCH(X). {A.eOperator = X; A.not = 0;}
|
||||
likeop(A) ::= NOT MATCH(X). {A.eOperator = X; A.not = 1;}
|
||||
likeop(A) ::= LIKE_KW(X). {A.eOperator = X; A.bNot = 0;}
|
||||
likeop(A) ::= NOT LIKE_KW(X). {A.eOperator = X; A.bNot = 1;}
|
||||
likeop(A) ::= MATCH(X). {A.eOperator = X; A.bNot = 0;}
|
||||
likeop(A) ::= NOT MATCH(X). {A.eOperator = X; A.bNot = 1;}
|
||||
expr(A) ::= expr(X) likeop(OP) expr(Y). [LIKE_KW] {
|
||||
ExprList *pList;
|
||||
pList = sqlite3ExprListAppend(pParse,0, Y.pExpr);
|
||||
pList = sqlite3ExprListAppend(pParse,pList, X.pExpr);
|
||||
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.zEnd = Y.zEnd;
|
||||
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, E.pExpr);
|
||||
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.zEnd = E.zEnd;
|
||||
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) ::= DEFAULT(X). {A = X;}
|
||||
%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;}
|
||||
number(A) ::= INTEGER|FLOAT(X). {A = X;}
|
||||
plus_opt ::= PLUS.
|
||||
plus_opt ::= .
|
||||
|
||||
//////////////////////////// The CREATE TRIGGER command /////////////////////
|
||||
|
||||
@ -1261,8 +1294,8 @@ trigger_cmd(A) ::=
|
||||
|
||||
// INSERT
|
||||
trigger_cmd(A) ::=
|
||||
insert_cmd(R) INTO trnm(X) inscollist_opt(F) VALUES LP itemlist(Y) RP.
|
||||
{A = sqlite3TriggerInsertStep(pParse->db, &X, F, Y, 0, R);}
|
||||
insert_cmd(R) INTO trnm(X) inscollist_opt(F) valuelist(Y).
|
||||
{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).
|
||||
{A = sqlite3TriggerInsertStep(pParse->db, &X, F, 0, S, R);}
|
||||
@ -1356,8 +1389,9 @@ kwcolumn_opt ::= COLUMNKW.
|
||||
%ifndef SQLITE_OMIT_VIRTUALTABLE
|
||||
cmd ::= create_vtab. {sqlite3VtabFinishParse(pParse,0);}
|
||||
cmd ::= create_vtab LP vtabarglist RP(X). {sqlite3VtabFinishParse(pParse,&X);}
|
||||
create_vtab ::= createkw VIRTUAL TABLE nm(X) dbnm(Y) USING nm(Z). {
|
||||
sqlite3VtabBeginParse(pParse, &X, &Y, &Z);
|
||||
create_vtab ::= createkw VIRTUAL TABLE ifnotexists(E)
|
||||
nm(X) dbnm(Y) USING nm(Z). {
|
||||
sqlite3VtabBeginParse(pParse, &X, &Y, &Z, E);
|
||||
}
|
||||
vtabarglist ::= vtabarg.
|
||||
vtabarglist ::= vtabarglist COMMA vtabarg.
|
||||
|
@ -27,8 +27,8 @@ struct PgHdr {
|
||||
void *pData; /* Page data */
|
||||
void *pExtra; /* Extra content */
|
||||
PgHdr *pDirty; /* Transient list of dirty pages */
|
||||
Pgno pgno; /* Page number for this page */
|
||||
Pager *pPager; /* The pager this page is part of */
|
||||
Pgno pgno; /* Page number for this page */
|
||||
#ifdef SQLITE_CHECK_PAGES
|
||||
u32 pageHash; /* Hash of page content */
|
||||
#endif
|
||||
|
@ -76,6 +76,7 @@ struct PCache1 {
|
||||
unsigned int nMin; /* Minimum number of pages reserved */
|
||||
unsigned int nMax; /* Configured "cache_size" value */
|
||||
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
|
||||
** 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 nHash; /* Number of slots in apHash[] */
|
||||
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 */
|
||||
/* Above requires no mutex. Use mutex below for variable that follow. */
|
||||
sqlite3_mutex *mutex; /* Mutex for accessing the following: */
|
||||
int nFreeSlot; /* Number of unused pcache slots */
|
||||
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
|
||||
** 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
|
||||
|
64
src/pragma.c
64
src/pragma.c
@ -16,14 +16,15 @@
|
||||
/*
|
||||
** 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
|
||||
** 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
|
||||
** should be passed into sqlite3BtreeSetSafetyLevel(). The is done
|
||||
** 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.
|
||||
*/
|
||||
static u8 getSafetyLevel(const char *z){
|
||||
static u8 getSafetyLevel(const char *z, int omitFull, int dflt){
|
||||
/* 123456789 123456789 */
|
||||
static const char zText[] = "onoffalseyestruefull";
|
||||
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);
|
||||
}
|
||||
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 ){
|
||||
return iValue[i];
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
return dflt;
|
||||
}
|
||||
|
||||
/*
|
||||
** Interpret the given string as a boolean value.
|
||||
*/
|
||||
u8 sqlite3GetBoolean(const char *z){
|
||||
return getSafetyLevel(z)&1;
|
||||
u8 sqlite3GetBoolean(const char *z, int dflt){
|
||||
return getSafetyLevel(z,1,dflt)!=0;
|
||||
}
|
||||
|
||||
/* 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
|
||||
/* The following is VERY experimental */
|
||||
{ "writable_schema", SQLITE_WriteSchema|SQLITE_RecoveryMode },
|
||||
{ "omit_readlock", SQLITE_NoReadlock },
|
||||
|
||||
/* TODO: Maybe it shouldn't be possible to change the ReadUncommitted
|
||||
** 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);
|
||||
}
|
||||
|
||||
if( sqlite3GetBoolean(zRight) ){
|
||||
if( sqlite3GetBoolean(zRight, 0) ){
|
||||
db->flags |= mask;
|
||||
}else{
|
||||
db->flags &= ~mask;
|
||||
@ -312,9 +312,12 @@ void sqlite3Pragma(
|
||||
const char *zDb = 0; /* The database name */
|
||||
Token *pId; /* Pointer to <id> token */
|
||||
int iDb; /* Database index for <database> */
|
||||
sqlite3 *db = pParse->db;
|
||||
Db *pDb;
|
||||
Vdbe *v = pParse->pVdbe = sqlite3VdbeCreate(db);
|
||||
char *aFcntl[4]; /* Argument to SQLITE_FCNTL_PRAGMA */
|
||||
int rc; /* return value form SQLITE_FCNTL_PRAGMA */
|
||||
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;
|
||||
sqlite3VdbeRunOnlyOnce(v);
|
||||
pParse->nMem = 2;
|
||||
@ -345,6 +348,34 @@ void sqlite3Pragma(
|
||||
if( sqlite3AuthCheck(pParse, SQLITE_PRAGMA, zLeft, zRight, zDb) ){
|
||||
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)
|
||||
/*
|
||||
@ -437,7 +468,7 @@ void sqlite3Pragma(
|
||||
int b = -1;
|
||||
assert( pBt!=0 );
|
||||
if( zRight ){
|
||||
b = sqlite3GetBoolean(zRight);
|
||||
b = sqlite3GetBoolean(zRight, 0);
|
||||
}
|
||||
if( pId2->n==0 && b>=0 ){
|
||||
int ii;
|
||||
@ -632,7 +663,7 @@ void sqlite3Pragma(
|
||||
** creates the database file. It is important that it is created
|
||||
** as an auto-vacuum capable db.
|
||||
*/
|
||||
int rc = sqlite3BtreeSetAutoVacuum(pBt, eAuto);
|
||||
rc = sqlite3BtreeSetAutoVacuum(pBt, eAuto);
|
||||
if( rc==SQLITE_OK && (eAuto==1 || eAuto==2) ){
|
||||
/* When setting the auto_vacuum mode to either "full" or
|
||||
** "incremental", write the value of meta[6] in the database
|
||||
@ -750,7 +781,6 @@ void sqlite3Pragma(
|
||||
}else{
|
||||
#ifndef SQLITE_OMIT_WSD
|
||||
if( zRight[0] ){
|
||||
int rc;
|
||||
int res;
|
||||
rc = sqlite3OsAccess(db->pVfs, zRight, SQLITE_ACCESS_READWRITE, &res);
|
||||
if( rc!=SQLITE_OK || res==0 ){
|
||||
@ -842,7 +872,7 @@ void sqlite3Pragma(
|
||||
sqlite3ErrorMsg(pParse,
|
||||
"Safety level may not be changed inside a transaction");
|
||||
}else{
|
||||
pDb->safety_level = getSafetyLevel(zRight)+1;
|
||||
pDb->safety_level = getSafetyLevel(zRight,0,1)+1;
|
||||
}
|
||||
}
|
||||
}else
|
||||
@ -1041,7 +1071,7 @@ void sqlite3Pragma(
|
||||
#ifndef NDEBUG
|
||||
if( sqlite3StrICmp(zLeft, "parser_trace")==0 ){
|
||||
if( zRight ){
|
||||
if( sqlite3GetBoolean(zRight) ){
|
||||
if( sqlite3GetBoolean(zRight, 0) ){
|
||||
sqlite3ParserTrace(stderr, "parser: ");
|
||||
}else{
|
||||
sqlite3ParserTrace(0, 0);
|
||||
@ -1055,7 +1085,7 @@ void sqlite3Pragma(
|
||||
*/
|
||||
if( sqlite3StrICmp(zLeft, "case_sensitive_like")==0 ){
|
||||
if( zRight ){
|
||||
sqlite3RegisterLikeFunctions(db, sqlite3GetBoolean(zRight));
|
||||
sqlite3RegisterLikeFunctions(db, sqlite3GetBoolean(zRight, 0));
|
||||
}
|
||||
}else
|
||||
|
||||
|
@ -533,7 +533,7 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
|
||||
nId = sqlite3Strlen30(zId);
|
||||
pDef = sqlite3FindFunction(pParse->db, zId, nId, n, enc, 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 ){
|
||||
no_such_func = 1;
|
||||
}else{
|
||||
@ -883,7 +883,7 @@ static int resolveOrderGroupBy(
|
||||
ExprList *pOrderBy, /* An ORDER BY or GROUP BY clause to resolve */
|
||||
const char *zType /* Either "ORDER" or "GROUP", as appropriate */
|
||||
){
|
||||
int i; /* Loop counter */
|
||||
int i, j; /* Loop counters */
|
||||
int iCol; /* Column number */
|
||||
struct ExprList_item *pItem; /* A term of the ORDER BY clause */
|
||||
Parse *pParse; /* Parsing context */
|
||||
@ -920,6 +920,11 @@ static int resolveOrderGroupBy(
|
||||
if( sqlite3ResolveExprNames(pNC, pE) ){
|
||||
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);
|
||||
}
|
||||
|
212
src/rowset.c
212
src/rowset.c
@ -76,6 +76,11 @@
|
||||
|
||||
/*
|
||||
** 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 {
|
||||
i64 v; /* ROWID value for this entry */
|
||||
@ -105,12 +110,18 @@ struct RowSet {
|
||||
struct RowSetEntry *pEntry; /* List of entries using pRight */
|
||||
struct RowSetEntry *pLast; /* Last entry on the pEntry list */
|
||||
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 */
|
||||
u8 isSorted; /* True if pEntry is sorted */
|
||||
u8 rsFlags; /* Various flags */
|
||||
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
|
||||
** 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->pEntry = 0;
|
||||
p->pLast = 0;
|
||||
p->pTree = 0;
|
||||
p->pForest = 0;
|
||||
p->pFresh = (struct RowSetEntry*)(ROUND8(sizeof(*p)) + (char*)p);
|
||||
p->nFresh = (u16)((N - ROUND8(sizeof(*p)))/sizeof(struct RowSetEntry));
|
||||
p->isSorted = 1;
|
||||
p->rsFlags = ROWSET_SORTED;
|
||||
p->iBatch = 0;
|
||||
return p;
|
||||
}
|
||||
@ -154,8 +165,33 @@ void sqlite3RowSetClear(RowSet *p){
|
||||
p->nFresh = 0;
|
||||
p->pEntry = 0;
|
||||
p->pLast = 0;
|
||||
p->pTree = 0;
|
||||
p->isSorted = 1;
|
||||
p->pForest = 0;
|
||||
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){
|
||||
struct RowSetEntry *pEntry; /* The new entry */
|
||||
struct RowSetEntry *pLast; /* The last prior entry */
|
||||
assert( p!=0 );
|
||||
if( p->nFresh==0 ){
|
||||
struct RowSetChunk *pNew;
|
||||
pNew = sqlite3DbMallocRaw(p->db, sizeof(*pNew));
|
||||
if( pNew==0 ){
|
||||
return;
|
||||
}
|
||||
pNew->pNextChunk = p->pChunk;
|
||||
p->pChunk = pNew;
|
||||
p->pFresh = pNew->aEntry;
|
||||
p->nFresh = ROWSET_ENTRY_PER_CHUNK;
|
||||
}
|
||||
pEntry = p->pFresh++;
|
||||
p->nFresh--;
|
||||
|
||||
/* This routine is never called after sqlite3RowSetNext() */
|
||||
assert( p!=0 && (p->rsFlags & ROWSET_NEXT)==0 );
|
||||
|
||||
pEntry = rowSetEntryAlloc(p);
|
||||
if( pEntry==0 ) return;
|
||||
pEntry->v = rowid;
|
||||
pEntry->pRight = 0;
|
||||
pLast = p->pLast;
|
||||
if( pLast ){
|
||||
if( p->isSorted && rowid<=pLast->v ){
|
||||
p->isSorted = 0;
|
||||
if( (p->rsFlags & ROWSET_SORTED)!=0 && rowid<=pLast->v ){
|
||||
p->rsFlags &= ~ROWSET_SORTED;
|
||||
}
|
||||
pLast->pRight = pEntry;
|
||||
}else{
|
||||
assert( p->pEntry==0 ); /* Fires if INSERT after SMALLEST */
|
||||
p->pEntry = pEntry;
|
||||
}
|
||||
p->pLast = pEntry;
|
||||
@ -202,7 +229,7 @@ void sqlite3RowSetInsert(RowSet *p, i64 rowid){
|
||||
** The input lists are connected via pRight pointers and are
|
||||
** 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 *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;
|
||||
struct RowSetEntry *pEntry;
|
||||
struct RowSetEntry *aBucket[40];
|
||||
struct RowSetEntry *pNext, *aBucket[40];
|
||||
|
||||
assert( p->isSorted==0 );
|
||||
memset(aBucket, 0, sizeof(aBucket));
|
||||
while( p->pEntry ){
|
||||
pEntry = p->pEntry;
|
||||
p->pEntry = pEntry->pRight;
|
||||
pEntry->pRight = 0;
|
||||
while( pIn ){
|
||||
pNext = pIn->pRight;
|
||||
pIn->pRight = 0;
|
||||
for(i=0; aBucket[i]; i++){
|
||||
pEntry = rowSetMerge(aBucket[i], pEntry);
|
||||
pIn = rowSetEntryMerge(aBucket[i], pIn);
|
||||
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++){
|
||||
pEntry = rowSetMerge(pEntry, aBucket[i]);
|
||||
pIn = rowSetEntryMerge(pIn, aBucket[i]);
|
||||
}
|
||||
p->pEntry = pEntry;
|
||||
p->pLast = 0;
|
||||
p->isSorted = 1;
|
||||
return pIn;
|
||||
}
|
||||
|
||||
|
||||
@ -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
|
||||
** sorted already. If there is a binary tree on p->pTree, then
|
||||
** convert it into a list too and merge it into the p->pEntry list.
|
||||
** Take all the entries on p->pEntry and on the trees in p->pForest and
|
||||
** sort them all together into one big ordered list on p->pEntry.
|
||||
**
|
||||
** This routine should only be called once in the life of a RowSet.
|
||||
*/
|
||||
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;
|
||||
rowSetTreeToList(p->pTree, &pHead, &pTail);
|
||||
p->pTree = 0;
|
||||
p->pEntry = rowSetMerge(p->pEntry, pHead);
|
||||
|
||||
/* While this module could theoretically support it, sqlite3RowSetNext()
|
||||
** is never called after sqlite3RowSetText() for the same RowSet. So
|
||||
** there is never a forest to deal with. Should this change, simply
|
||||
** 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.
|
||||
*/
|
||||
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 ){
|
||||
*pRowid = p->pEntry->v;
|
||||
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
|
||||
** 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){
|
||||
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( pRowSet->pEntry ){
|
||||
rowSetToList(pRowSet);
|
||||
pRowSet->pTree = rowSetListToTree(pRowSet->pEntry);
|
||||
p = pRowSet->pEntry;
|
||||
if( p ){
|
||||
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->pLast = 0;
|
||||
pRowSet->rsFlags |= ROWSET_SORTED;
|
||||
}
|
||||
pRowSet->iBatch = iBatch;
|
||||
}
|
||||
p = pRowSet->pTree;
|
||||
while( p ){
|
||||
if( p->v<iRowid ){
|
||||
p = p->pRight;
|
||||
}else if( p->v>iRowid ){
|
||||
p = p->pLeft;
|
||||
}else{
|
||||
return 1;
|
||||
|
||||
/* Test to see if the iRowid value appears anywhere in the forest.
|
||||
** Return 1 if it does and 0 if not.
|
||||
*/
|
||||
for(pTree = pRowSet->pForest; pTree; pTree=pTree->pRight){
|
||||
p = pTree->pLeft;
|
||||
while( p ){
|
||||
if( p->v<iRowid ){
|
||||
p = p->pRight;
|
||||
}else if( p->v>iRowid ){
|
||||
p = p->pLeft;
|
||||
}else{
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
|
43
src/select.c
43
src/select.c
@ -73,6 +73,7 @@ Select *sqlite3SelectNew(
|
||||
pEList = sqlite3ExprListAppend(pParse, 0, sqlite3Expr(db,TK_ALL,0));
|
||||
}
|
||||
pNew->pEList = pEList;
|
||||
if( pSrc==0 ) pSrc = sqlite3DbMallocZero(db, sizeof(*pSrc));
|
||||
pNew->pSrc = pSrc;
|
||||
pNew->pWhere = pWhere;
|
||||
pNew->pGroupBy = pGroupBy;
|
||||
@ -1257,9 +1258,17 @@ static int selectColumnsFromExprList(
|
||||
char *zName; /* Column name */
|
||||
int nName; /* Size of name in zName[] */
|
||||
|
||||
*pnCol = nCol = pEList->nExpr;
|
||||
aCol = *paCol = sqlite3DbMallocZero(db, sizeof(aCol[0])*nCol);
|
||||
if( aCol==0 ) return SQLITE_NOMEM;
|
||||
if( pEList ){
|
||||
nCol = pEList->nExpr;
|
||||
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++){
|
||||
/* Get an appropriate name for the column
|
||||
*/
|
||||
@ -1611,8 +1620,12 @@ static int multiSelect(
|
||||
*/
|
||||
assert( p->pEList && pPrior->pEList );
|
||||
if( p->pEList->nExpr!=pPrior->pEList->nExpr ){
|
||||
sqlite3ErrorMsg(pParse, "SELECTs to the left and right of %s"
|
||||
" do not have the same number of result columns", selectOpName(p->op));
|
||||
if( p->selFlags & SF_Values ){
|
||||
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;
|
||||
goto multi_select_end;
|
||||
}
|
||||
@ -2228,7 +2241,7 @@ static int multiSelectOrderBy(
|
||||
pNew->flags |= EP_IntValue;
|
||||
pNew->u.iValue = i;
|
||||
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 */
|
||||
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;
|
||||
|
||||
/* 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){
|
||||
Vdbe *v = pParse->pVdbe;
|
||||
int i;
|
||||
int regHit = 0;
|
||||
int addrHitTest = 0;
|
||||
struct AggInfo_func *pF;
|
||||
struct AggInfo_col *pC;
|
||||
|
||||
@ -3626,7 +3642,8 @@ static void updateAccumulator(Parse *pParse, AggInfo *pAggInfo){
|
||||
if( !pColl ){
|
||||
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,
|
||||
(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
|
||||
** values to an OP_Copy.
|
||||
*/
|
||||
if( regHit ){
|
||||
addrHitTest = sqlite3VdbeAddOp1(v, OP_If, regHit);
|
||||
}
|
||||
sqlite3ExprCacheClear(pParse);
|
||||
for(i=0, pC=pAggInfo->aCol; i<pAggInfo->nAccumulator; i++, pC++){
|
||||
sqlite3ExprCode(pParse, pC->pExpr, pC->iMem);
|
||||
}
|
||||
pAggInfo->directMode = 0;
|
||||
sqlite3ExprCacheClear(pParse);
|
||||
if( addrHitTest ){
|
||||
sqlite3VdbeJumpHere(v, addrHitTest);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@ -4214,7 +4237,7 @@ int sqlite3Select(
|
||||
int r2;
|
||||
|
||||
r2 = sqlite3ExprCodeGetColumn(pParse,
|
||||
pCol->pTab, pCol->iColumn, pCol->iTable, r1);
|
||||
pCol->pTab, pCol->iColumn, pCol->iTable, r1, 0);
|
||||
if( r1!=r2 ){
|
||||
sqlite3VdbeAddOp2(v, OP_SCopy, r2, r1);
|
||||
}
|
||||
@ -4595,4 +4618,4 @@ void sqlite3ExplainSelect(Vdbe *pVdbe, Select *p){
|
||||
|
||||
/* End of the structure debug printing code
|
||||
*****************************************************************************/
|
||||
#endif /* defined(SQLITE_TEST) || defined(SQLITE_DEBUG) */
|
||||
#endif /* defined(SQLITE_ENABLE_TREE_EXPLAIN) */
|
||||
|
385
src/shell.c
385
src/shell.c
@ -57,7 +57,7 @@
|
||||
# include <readline/history.h>
|
||||
#endif
|
||||
#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 read_history(X)
|
||||
# define write_history(X)
|
||||
@ -68,6 +68,8 @@
|
||||
# include <io.h>
|
||||
#define isatty(h) _isatty(h)
|
||||
#define access(f,m) _access((f),(m))
|
||||
#define popen(a,b) _popen((a),(b))
|
||||
#define pclose(x) _pclose(x)
|
||||
#else
|
||||
/* Make sure isatty() has a prototype.
|
||||
*/
|
||||
@ -334,10 +336,11 @@ static void shellstaticFunc(
|
||||
** The interface is like "readline" but no command-line editing
|
||||
** is done.
|
||||
*/
|
||||
static char *local_getline(char *zPrompt, FILE *in){
|
||||
static char *local_getline(char *zPrompt, FILE *in, int csvFlag){
|
||||
char *zLine;
|
||||
int nLine;
|
||||
int n;
|
||||
int inQuote = 0;
|
||||
|
||||
if( zPrompt && *zPrompt ){
|
||||
printf("%s",zPrompt);
|
||||
@ -361,8 +364,11 @@ static char *local_getline(char *zPrompt, FILE *in){
|
||||
zLine[n] = 0;
|
||||
break;
|
||||
}
|
||||
while( zLine[n] ){ n++; }
|
||||
if( n>0 && zLine[n-1]=='\n' ){
|
||||
while( zLine[n] ){
|
||||
if( zLine[n]=='"' ) inQuote = !inQuote;
|
||||
n++;
|
||||
}
|
||||
if( n>0 && zLine[n-1]=='\n' && (!inQuote || !csvFlag) ){
|
||||
n--;
|
||||
if( n>0 && zLine[n-1]=='\r' ) n--;
|
||||
zLine[n] = 0;
|
||||
@ -383,7 +389,7 @@ static char *one_input_line(const char *zPrior, FILE *in){
|
||||
char *zPrompt;
|
||||
char *zResult;
|
||||
if( in!=0 ){
|
||||
return local_getline(0, in);
|
||||
return local_getline(0, in, 0);
|
||||
}
|
||||
if( zPrior && zPrior[0] ){
|
||||
zPrompt = continuePrompt;
|
||||
@ -415,6 +421,7 @@ struct callback_data {
|
||||
int statsOn; /* True to display memory stats before each finalize */
|
||||
int cnt; /* Number of records displayed so far */
|
||||
FILE *out; /* Write results here */
|
||||
FILE *traceOut; /* Output for sqlite3_trace() */
|
||||
int nErr; /* Number of errors seen */
|
||||
int mode; /* An output mode setting */
|
||||
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;
|
||||
char *zBlob = (char *)pBlob;
|
||||
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,"'");
|
||||
}
|
||||
|
||||
@ -614,8 +621,7 @@ static const char needCsvQuote[] = {
|
||||
/*
|
||||
** 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 null value. Strings are quoted using ANSI-C rules. Numbers
|
||||
** appear outside of quotes.
|
||||
** the null value. Strings are quoted if necessary.
|
||||
*/
|
||||
static void output_csv(struct callback_data *p, const char *z, int bSep){
|
||||
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
|
||||
** that result column on a line by itself with a semicolon terminator.
|
||||
** Execute a query statement that will generate SQL output. Print
|
||||
** 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
|
||||
** querying the SQLITE_MASTER table.
|
||||
** If the number of columns is 1 and that column contains text "--"
|
||||
** 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(
|
||||
struct callback_data *p, /* Query context */
|
||||
@ -947,6 +956,9 @@ static int run_table_dump_query(
|
||||
){
|
||||
sqlite3_stmt *pSelect;
|
||||
int rc;
|
||||
int nResult;
|
||||
int i;
|
||||
const char *z;
|
||||
rc = sqlite3_prepare(p->db, zSelect, -1, &pSelect, 0);
|
||||
if( rc!=SQLITE_OK || !pSelect ){
|
||||
fprintf(p->out, "/**** ERROR: (%d) %s *****/\n", rc, sqlite3_errmsg(p->db));
|
||||
@ -954,12 +966,24 @@ static int run_table_dump_query(
|
||||
return rc;
|
||||
}
|
||||
rc = sqlite3_step(pSelect);
|
||||
nResult = sqlite3_column_count(pSelect);
|
||||
while( rc==SQLITE_ROW ){
|
||||
if( zFirstRow ){
|
||||
fprintf(p->out, "%s", zFirstRow);
|
||||
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_finalize(pSelect);
|
||||
@ -1056,6 +1080,9 @@ static int display_stats(
|
||||
sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_MISS, &iCur, &iHiwtr, 1);
|
||||
fprintf(pArg->out, "Page cache misses: %d\n", iCur);
|
||||
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);
|
||||
fprintf(pArg->out, "Schema Heap Usage: %d bytes\n", iCur);
|
||||
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);
|
||||
/* 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, '"');
|
||||
if( zTmp ){
|
||||
zSelect = appendText(zSelect, zTmp, '\'');
|
||||
free(zTmp);
|
||||
}
|
||||
zSelect = appendText(zSelect, " || ' VALUES(' || ", 0);
|
||||
rc = sqlite3_step(pTableInfo);
|
||||
@ -1290,7 +1320,7 @@ static int dump_callback(void *pArg, int nArg, char **azArg, char **azCol){
|
||||
zSelect = appendText(zSelect, zText, '"');
|
||||
rc = sqlite3_step(pTableInfo);
|
||||
if( rc==SQLITE_ROW ){
|
||||
zSelect = appendText(zSelect, ") || ',' || ", 0);
|
||||
zSelect = appendText(zSelect, "), ", 0);
|
||||
}else{
|
||||
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);
|
||||
run_table_dump_query(p, zSelect, 0);
|
||||
}
|
||||
if( zSelect ) free(zSelect);
|
||||
free(zSelect);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@ -1339,7 +1369,7 @@ static int run_schema_dump_query(
|
||||
}
|
||||
zQ2 = malloc( len+100 );
|
||||
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);
|
||||
if( rc ){
|
||||
fprintf(p->out, "/****** ERROR: %s ******/\n", zErr);
|
||||
@ -1405,6 +1435,7 @@ static char zHelp[] =
|
||||
" If TABLE specified, only list tables matching\n"
|
||||
" LIKE pattern TABLE.\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"
|
||||
".width NUM1 NUM2 ... Set column widths for \"column\" mode\n"
|
||||
;
|
||||
@ -1494,6 +1525,52 @@ static int booleanValue(char *zArg){
|
||||
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
|
||||
** process that line.
|
||||
@ -1573,6 +1650,13 @@ static int do_meta_command(char *zLine, struct callback_data *p){
|
||||
bail_on_error = booleanValue(azArg[1]);
|
||||
}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 ){
|
||||
struct callback_data data;
|
||||
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);
|
||||
zCommit = "COMMIT";
|
||||
while( (zLine = local_getline(0, in))!=0 ){
|
||||
char *z;
|
||||
while( (zLine = local_getline(0, in, 1))!=0 ){
|
||||
char *z, c;
|
||||
int inQuote = 0;
|
||||
lineno++;
|
||||
azCol[0] = zLine;
|
||||
for(i=0, z=zLine; *z && *z!='\n' && *z!='\r'; z++){
|
||||
if( *z==p->separator[0] && strncmp(z, p->separator, nSep)==0 ){
|
||||
for(i=0, z=zLine; (c = *z)!=0; z++){
|
||||
if( c=='"' ) inQuote = !inQuote;
|
||||
if( c=='\n' ) lineno++;
|
||||
if( !inQuote && c==p->separator[0] && strncmp(z,p->separator,nSep)==0 ){
|
||||
*z = 0;
|
||||
i++;
|
||||
if( i<nCol ){
|
||||
@ -1794,6 +1881,14 @@ static int do_meta_command(char *zLine, struct callback_data *p){
|
||||
break; /* from while */
|
||||
}
|
||||
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_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 ){
|
||||
const char *zFile = azArg[1];
|
||||
if( p->pLog && p->pLog!=stdout && p->pLog!=stderr ){
|
||||
fclose(p->pLog);
|
||||
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);
|
||||
}
|
||||
}
|
||||
output_file_close(p->pLog);
|
||||
p->pLog = output_file_open(zFile);
|
||||
}else
|
||||
|
||||
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
|
||||
|
||||
if( c=='o' && strncmp(azArg[0], "output", n)==0 && nArg==2 ){
|
||||
if( p->out!=stdout ){
|
||||
fclose(p->out);
|
||||
}
|
||||
if( strcmp(azArg[1],"stdout")==0 ){
|
||||
p->out = stdout;
|
||||
sqlite3_snprintf(sizeof(p->outfile), p->outfile, "stdout");
|
||||
if( p->outfile[0]=='|' ){
|
||||
pclose(p->out);
|
||||
}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 ){
|
||||
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;
|
||||
rc = 1;
|
||||
} else {
|
||||
sqlite3_snprintf(sizeof(p->outfile), p->outfile, "%s", azArg[1]);
|
||||
sqlite3_snprintf(sizeof(p->outfile), p->outfile, "%s", azArg[1]);
|
||||
}
|
||||
}
|
||||
}else
|
||||
@ -2156,46 +2248,71 @@ static int do_meta_command(char *zLine, struct callback_data *p){
|
||||
}else
|
||||
|
||||
if( c=='t' && n>1 && strncmp(azArg[0], "tables", n)==0 && nArg<3 ){
|
||||
sqlite3_stmt *pStmt;
|
||||
char **azResult;
|
||||
int nRow;
|
||||
char *zErrMsg;
|
||||
int nRow, nAlloc;
|
||||
char *zSql = 0;
|
||||
int ii;
|
||||
open_db(p);
|
||||
if( nArg==1 ){
|
||||
rc = sqlite3_get_table(p->db,
|
||||
"SELECT name FROM sqlite_master "
|
||||
"WHERE type IN ('table','view') AND name NOT LIKE 'sqlite_%' "
|
||||
"UNION ALL "
|
||||
"SELECT name FROM sqlite_temp_master "
|
||||
"WHERE type IN ('table','view') "
|
||||
"ORDER BY 1",
|
||||
&azResult, &nRow, 0, &zErrMsg
|
||||
);
|
||||
}else{
|
||||
zShellStatic = azArg[1];
|
||||
rc = sqlite3_get_table(p->db,
|
||||
"SELECT name FROM sqlite_master "
|
||||
"WHERE type IN ('table','view') AND name LIKE shellstatic() "
|
||||
"UNION ALL "
|
||||
"SELECT name FROM sqlite_temp_master "
|
||||
"WHERE type IN ('table','view') AND name LIKE shellstatic() "
|
||||
"ORDER BY 1",
|
||||
&azResult, &nRow, 0, &zErrMsg
|
||||
);
|
||||
zShellStatic = 0;
|
||||
rc = sqlite3_prepare_v2(p->db, "PRAGMA database_list", -1, &pStmt, 0);
|
||||
if( rc ) return rc;
|
||||
zSql = sqlite3_mprintf(
|
||||
"SELECT name FROM sqlite_master"
|
||||
" WHERE type IN ('table','view')"
|
||||
" AND name NOT LIKE 'sqlite_%%'"
|
||||
" AND name LIKE ?1");
|
||||
while( sqlite3_step(pStmt)==SQLITE_ROW ){
|
||||
const char *zDbName = (const char*)sqlite3_column_text(pStmt, 1);
|
||||
if( zDbName==0 || strcmp(zDbName,"main")==0 ) continue;
|
||||
if( strcmp(zDbName,"temp")==0 ){
|
||||
zSql = sqlite3_mprintf(
|
||||
"%z UNION ALL "
|
||||
"SELECT 'temp.' || name FROM sqlite_temp_master"
|
||||
" WHERE type IN ('table','view')"
|
||||
" AND name NOT LIKE 'sqlite_%%'"
|
||||
" AND name LIKE ?1", zSql);
|
||||
}else{
|
||||
zSql = sqlite3_mprintf(
|
||||
"%z UNION ALL "
|
||||
"SELECT '%q.' || name FROM \"%w\".sqlite_master"
|
||||
" WHERE type IN ('table','view')"
|
||||
" AND name NOT LIKE 'sqlite_%%'"
|
||||
" AND name LIKE ?1", zSql, zDbName, zDbName);
|
||||
}
|
||||
}
|
||||
if( zErrMsg ){
|
||||
fprintf(stderr,"Error: %s\n", zErrMsg);
|
||||
sqlite3_free(zErrMsg);
|
||||
rc = 1;
|
||||
}else if( rc != SQLITE_OK ){
|
||||
fprintf(stderr,"Error: querying sqlite_master and sqlite_temp_master\n");
|
||||
rc = 1;
|
||||
sqlite3_finalize(pStmt);
|
||||
zSql = sqlite3_mprintf("%z ORDER BY 1", zSql);
|
||||
rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0);
|
||||
sqlite3_free(zSql);
|
||||
if( rc ) return rc;
|
||||
nRow = nAlloc = 0;
|
||||
azResult = 0;
|
||||
if( nArg>1 ){
|
||||
sqlite3_bind_text(pStmt, 1, azArg[1], -1, SQLITE_TRANSIENT);
|
||||
}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 i, j;
|
||||
int nPrintCol, nPrintRow;
|
||||
for(i=1; i<=nRow; i++){
|
||||
if( azResult[i]==0 ) continue;
|
||||
for(i=0; i<nRow; i++){
|
||||
len = strlen30(azResult[i]);
|
||||
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;
|
||||
nPrintRow = (nRow + nPrintCol - 1)/nPrintCol;
|
||||
for(i=0; i<nPrintRow; i++){
|
||||
for(j=i+1; j<=nRow; j+=nPrintRow){
|
||||
char *zSp = j<=nPrintRow ? "" : " ";
|
||||
for(j=i; j<nRow; j+=nPrintRow){
|
||||
char *zSp = j<nPrintRow ? "" : " ";
|
||||
printf("%s%-*s", zSp, maxlen, azResult[j] ? azResult[j] : "");
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
sqlite3_free_table(azResult);
|
||||
for(ii=0; ii<nRow; ii++) sqlite3_free(azResult[ii]);
|
||||
sqlite3_free(azResult);
|
||||
}else
|
||||
|
||||
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]);
|
||||
}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 ){
|
||||
printf("SQLite %s %s\n" /*extra-version-info*/,
|
||||
sqlite3_libversion(), sqlite3_sourceid());
|
||||
@ -2468,7 +2599,9 @@ static int process_input(struct callback_data *p, FILE *in){
|
||||
free(zLine);
|
||||
zLine = one_input_line(zSql, in);
|
||||
if( zLine==0 ){
|
||||
break; /* We have reached EOF */
|
||||
/* End of input */
|
||||
if( stdin_is_interactive ) printf("\n");
|
||||
break;
|
||||
}
|
||||
if( seenInterrupt ){
|
||||
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
|
||||
** 0 return indicates an error of some kind. Space to hold the
|
||||
** resulting string is obtained from malloc(). The calling
|
||||
** function should free the result.
|
||||
** 0 return indicates an error of some kind.
|
||||
*/
|
||||
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)
|
||||
struct passwd *pwent;
|
||||
@ -2573,7 +2705,7 @@ static char *find_home_dir(void){
|
||||
#if defined(_WIN32_WCE)
|
||||
/* Windows CE (arm-wince-mingw32ce-gcc) does not provide getenv()
|
||||
*/
|
||||
home_dir = strdup("/");
|
||||
home_dir = "/";
|
||||
#else
|
||||
|
||||
#if defined(_WIN32) || defined(WIN32) || defined(__OS2__)
|
||||
@ -2629,7 +2761,6 @@ static int process_sqliterc(
|
||||
const char *sqliterc = sqliterc_override;
|
||||
char *zBuf = 0;
|
||||
FILE *in = NULL;
|
||||
int nBuf;
|
||||
int rc = 0;
|
||||
|
||||
if (sqliterc == NULL) {
|
||||
@ -2640,15 +2771,8 @@ static int process_sqliterc(
|
||||
#endif
|
||||
return 1;
|
||||
}
|
||||
nBuf = strlen30(home_dir) + 16;
|
||||
zBuf = malloc( nBuf );
|
||||
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;
|
||||
zBuf = sqlite3_mprintf("%s/.sqliterc",home_dir);
|
||||
sqliterc = zBuf;
|
||||
}
|
||||
in = fopen(sqliterc,"rb");
|
||||
if( in ){
|
||||
@ -2658,7 +2782,7 @@ static int process_sqliterc(
|
||||
rc = process_input(p,in);
|
||||
fclose(in);
|
||||
}
|
||||
free(zBuf);
|
||||
sqlite3_free(zBuf);
|
||||
return rc;
|
||||
}
|
||||
|
||||
@ -2666,29 +2790,30 @@ static int process_sqliterc(
|
||||
** Show available command line options
|
||||
*/
|
||||
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"
|
||||
" -interactive force interactive I/O\n"
|
||||
" -batch force batch I/O\n"
|
||||
" -column set output mode to 'column'\n"
|
||||
" -cmd command run \"command\" before reading stdin\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"
|
||||
" -interactive force interactive I/O\n"
|
||||
" -line set output mode to 'line'\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"
|
||||
" -stats print memory stats before each finalize\n"
|
||||
" -nullvalue 'text' set text string for NULL values\n"
|
||||
" -version show SQLite version\n"
|
||||
" -vfs NAME use NAME as the default VFS\n"
|
||||
#ifdef SQLITE_ENABLE_VFSTRACE
|
||||
" -vfstrace enable tracing of all VFS calls\n"
|
||||
#endif
|
||||
#ifdef SQLITE_ENABLE_MULTIPLEX
|
||||
" -multiplex enable the multiplexor VFS\n"
|
||||
#endif
|
||||
;
|
||||
static void usage(int showDetail){
|
||||
fprintf(stderr,
|
||||
@ -2751,19 +2876,22 @@ int main(int argc, char **argv){
|
||||
char *z;
|
||||
if( argv[i][0]!='-' ) break;
|
||||
z = argv[i];
|
||||
if( z[0]=='-' && z[1]=='-' ) z++;
|
||||
if( strcmp(argv[i],"-separator")==0 || strcmp(argv[i],"-nullvalue")==0 ){
|
||||
if( z[1]=='-' ) z++;
|
||||
if( strcmp(z,"-separator")==0
|
||||
|| strcmp(z,"-nullvalue")==0
|
||||
|| strcmp(z,"-cmd")==0
|
||||
){
|
||||
i++;
|
||||
}else if( strcmp(argv[i],"-init")==0 ){
|
||||
}else if( strcmp(z,"-init")==0 ){
|
||||
i++;
|
||||
zInitFile = argv[i];
|
||||
/* Need to check for batch mode here to so we can avoid printing
|
||||
** informational messages (like from process_sqliterc) before
|
||||
** 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;
|
||||
}else if( strcmp(argv[i],"-heap")==0 ){
|
||||
}else if( strcmp(z,"-heap")==0 ){
|
||||
#if defined(SQLITE_ENABLE_MEMSYS3) || defined(SQLITE_ENABLE_MEMSYS5)
|
||||
int j, c;
|
||||
const char *zSize;
|
||||
@ -2780,7 +2908,7 @@ int main(int argc, char **argv){
|
||||
sqlite3_config(SQLITE_CONFIG_HEAP, malloc((int)szHeap), (int)szHeap, 64);
|
||||
#endif
|
||||
#ifdef SQLITE_ENABLE_VFSTRACE
|
||||
}else if( strcmp(argv[i],"-vfstrace")==0 ){
|
||||
}else if( strcmp(z,"-vfstrace")==0 ){
|
||||
extern int vfstrace_register(
|
||||
const char *zTraceName,
|
||||
const char *zOldVfsName,
|
||||
@ -2791,11 +2919,11 @@ int main(int argc, char **argv){
|
||||
vfstrace_register("trace",0,(int(*)(const char*,void*))fputs,stderr,1);
|
||||
#endif
|
||||
#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);
|
||||
sqlite3_multiplex_initialize(0, 1);
|
||||
#endif
|
||||
}else if( strcmp(argv[i],"-vfs")==0 ){
|
||||
}else if( strcmp(z,"-vfs")==0 ){
|
||||
sqlite3_vfs *pVfs = sqlite3_vfs_find(argv[++i]);
|
||||
if( pVfs ){
|
||||
sqlite3_vfs_register(pVfs, 1);
|
||||
@ -2877,7 +3005,8 @@ int main(int argc, char **argv){
|
||||
}else if( strcmp(z,"-separator")==0 ){
|
||||
i++;
|
||||
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");
|
||||
return 1;
|
||||
}
|
||||
@ -2886,7 +3015,8 @@ int main(int argc, char **argv){
|
||||
}else if( strcmp(z,"-nullvalue")==0 ){
|
||||
i++;
|
||||
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");
|
||||
return 1;
|
||||
}
|
||||
@ -2921,8 +3051,26 @@ int main(int argc, char **argv){
|
||||
}else if( strcmp(z,"-multiplex")==0 ){
|
||||
i++;
|
||||
#endif
|
||||
}else if( strcmp(z,"-help")==0 || strcmp(z, "--help")==0 ){
|
||||
}else if( strcmp(z,"-help")==0 ){
|
||||
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{
|
||||
fprintf(stderr,"%s: Error: unknown option: %s\n", Argv0, z);
|
||||
fprintf(stderr,"Use -help for a list of options.\n");
|
||||
@ -2975,7 +3123,6 @@ int main(int argc, char **argv){
|
||||
write_history(zHistory);
|
||||
free(zHistory);
|
||||
}
|
||||
free(zHome);
|
||||
}else{
|
||||
rc = process_input(&data, stdin);
|
||||
}
|
||||
|
@ -453,9 +453,11 @@ int sqlite3_exec(
|
||||
#define SQLITE_LOCKED_SHAREDCACHE (SQLITE_LOCKED | (1<<8))
|
||||
#define SQLITE_BUSY_RECOVERY (SQLITE_BUSY | (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_READONLY_RECOVERY (SQLITE_READONLY | (1<<8))
|
||||
#define SQLITE_READONLY_CANTLOCK (SQLITE_READONLY | (2<<8))
|
||||
#define SQLITE_ABORT_ROLLBACK (SQLITE_ABORT | (2<<8))
|
||||
|
||||
/*
|
||||
** 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
|
||||
** is used during testing and only needs to be supported when SQLITE_TEST
|
||||
** is defined.
|
||||
**
|
||||
** <ul>
|
||||
** <li>[[SQLITE_FCNTL_SIZE_HINT]]
|
||||
** 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
|
||||
** 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 run faster.
|
||||
**
|
||||
** <li>[[SQLITE_FCNTL_CHUNK_SIZE]]
|
||||
** 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
|
||||
** 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
|
||||
** improve performance on some systems.
|
||||
**
|
||||
** <li>[[SQLITE_FCNTL_FILE_POINTER]]
|
||||
** The [SQLITE_FCNTL_FILE_POINTER] opcode is used to obtain a pointer
|
||||
** to the [sqlite3_file] object associated with a particular database
|
||||
** connection. See the [sqlite3_file_control()] documentation for
|
||||
** additional information.
|
||||
**
|
||||
** <li>[[SQLITE_FCNTL_SYNC_OMITTED]]
|
||||
** ^(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
|
||||
** 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
|
||||
** that do require it.
|
||||
**
|
||||
** <li>[[SQLITE_FCNTL_WIN32_AV_RETRY]]
|
||||
** ^The [SQLITE_FCNTL_WIN32_AV_RETRY] opcode is used to configure automatic
|
||||
** retry counts and intervals for certain disk I/O operations for the
|
||||
** 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
|
||||
** interrogated. The zDbName parameter is ignored.
|
||||
**
|
||||
** <li>[[SQLITE_FCNTL_PERSIST_WAL]]
|
||||
** ^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
|
||||
** are automatically deleted when the latest connection to the database
|
||||
** 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 persistence setting.
|
||||
**
|
||||
** <li>[[SQLITE_FCNTL_POWERSAFE_OVERWRITE]]
|
||||
** ^The [SQLITE_FCNTL_POWERSAFE_OVERWRITE] opcode is used to set or query the
|
||||
** persistent "powersafe-overwrite" or "PSOW" setting. The PSOW setting
|
||||
** 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
|
||||
** zero-damage mode setting.
|
||||
**
|
||||
** <li>[[SQLITE_FCNTL_OVERWRITE]]
|
||||
** ^The [SQLITE_FCNTL_OVERWRITE] opcode is invoked by SQLite after opening
|
||||
** a write transaction to indicate that, unless it is rolled back for some
|
||||
** reason, the entire database file will be overwritten by the current
|
||||
** transaction. This is used by VACUUM operations.
|
||||
**
|
||||
** <li>[[SQLITE_FCNTL_VFSNAME]]
|
||||
** ^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
|
||||
** 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
|
||||
** pointer in case this file-control is not implemented. This file-control
|
||||
** 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_GET_LOCKPROXYFILE 2
|
||||
@ -810,6 +845,7 @@ struct sqlite3_io_methods {
|
||||
#define SQLITE_FCNTL_OVERWRITE 11
|
||||
#define SQLITE_FCNTL_VFSNAME 12
|
||||
#define SQLITE_FCNTL_POWERSAFE_OVERWRITE 13
|
||||
#define SQLITE_FCNTL_PRAGMA 14
|
||||
|
||||
/*
|
||||
** CAPI3REF: Mutex Handle
|
||||
@ -1507,7 +1543,7 @@ struct sqlite3_mem_methods {
|
||||
** [SQLITE_USE_URI] symbol defined.
|
||||
**
|
||||
** [[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.
|
||||
** They are retained for backwards compatibility but are now no-ops.
|
||||
** </dl>
|
||||
@ -2639,9 +2675,14 @@ int sqlite3_open_v2(
|
||||
**
|
||||
** 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
|
||||
** of P. The value of P is true if it is "yes" or "true" or "on" or
|
||||
** a non-zero number and is false otherwise. If P is not a query parameter
|
||||
** on F then sqlite3_uri_boolean(F,P,B) returns (B!=0).
|
||||
** of P. The sqlite3_uri_boolean(F,P,B) routine returns true (1) if the
|
||||
** value of query parameter P is one of "yes", "true", or "on" in any
|
||||
** 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
|
||||
** 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);
|
||||
|
||||
/*
|
||||
** 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
|
||||
**
|
||||
@ -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
|
||||
** is always 0.
|
||||
** </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>
|
||||
*/
|
||||
#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_CACHE_HIT 7
|
||||
#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
|
||||
**
|
||||
** ^The [sqlite3_strnicmp()] API allows applications and extensions to
|
||||
** compare the contents of two buffers containing UTF-8 strings in a
|
||||
** case-independent fashion, using the same definition of case independence
|
||||
** that SQLite uses internally when comparing identifiers.
|
||||
** ^The [sqlite3_stricmp()] and [sqlite3_strnicmp()] APIs allow applications
|
||||
** and extensions to compare the contents of two buffers containing UTF-8
|
||||
** strings in a case-independent fashion, using the same definition of "case
|
||||
** independence" that SQLite uses internally when comparing identifiers.
|
||||
*/
|
||||
int sqlite3_stricmp(const char *, const char *);
|
||||
int sqlite3_strnicmp(const char *, const char *, int);
|
||||
|
||||
/*
|
||||
|
205
src/sqliteInt.h
205
src/sqliteInt.h
@ -561,9 +561,13 @@ struct BusyHandler {
|
||||
|
||||
/*
|
||||
** 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
|
||||
@ -793,35 +797,16 @@ struct FuncDefHash {
|
||||
|
||||
/*
|
||||
** 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 {
|
||||
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 */
|
||||
int nDb; /* Number of backends currently in use */
|
||||
int flags; /* Miscellaneous flags. See below */
|
||||
i64 lastRowid; /* ROWID of most recent insert (see above) */
|
||||
unsigned int openFlags; /* Flags passed to sqlite3_vfs.xOpen() */
|
||||
int errCode; /* Most recent error code (SQLITE_*) */
|
||||
int errMask; /* & result codes with this before returning */
|
||||
@ -832,27 +817,23 @@ struct sqlite3 {
|
||||
signed char nextAutovac; /* Autovac setting after VACUUM if >=0 */
|
||||
u8 suppressErr; /* Do not issue error messages if true */
|
||||
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 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 */
|
||||
int nChange; /* Value returned by sqlite3_changes() */
|
||||
int nTotalChange; /* Value returned by sqlite3_total_changes() */
|
||||
sqlite3_mutex *mutex; /* Connection mutex */
|
||||
int aLimit[SQLITE_N_LIMIT]; /* Limits */
|
||||
struct sqlite3InitInfo { /* Information used during initialization */
|
||||
int iDb; /* When back is being initialized */
|
||||
int newTnum; /* Rootpage of table being initialized */
|
||||
u8 iDb; /* Which db file is being initialized */
|
||||
u8 busy; /* TRUE if currently initializing */
|
||||
u8 orphanTrigger; /* Last statement is orphaned TEMP trigger */
|
||||
} 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 writeVdbeCnt; /* Number of active VDBEs that are writing */
|
||||
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 *pTraceArg; /* Argument to the trace function */
|
||||
void (*xProfile)(void*,const char*,u64); /* Profiling function */
|
||||
@ -889,21 +870,20 @@ struct sqlite3 {
|
||||
int nProgressOps; /* Number of opcodes for progress callback */
|
||||
#endif
|
||||
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||||
int nVTrans; /* Allocated size of aVTrans */
|
||||
Hash aModule; /* populated by sqlite3_create_module() */
|
||||
VtabCtx *pVtabCtx; /* Context for active vtab connect/create */
|
||||
VTable **aVTrans; /* Virtual tables with open transactions */
|
||||
int nVTrans; /* Allocated size of aVTrans */
|
||||
VTable *pDisconnect; /* Disconnect these in next sqlite3_prepare() */
|
||||
#endif
|
||||
FuncDefHash aFunc; /* Hash table of connection functions */
|
||||
Hash aCollSeq; /* All collating sequences */
|
||||
BusyHandler busyHandler; /* Busy callback */
|
||||
int busyTimeout; /* Busy handler timeout, in msec */
|
||||
Db aDbStatic[2]; /* Static space for the 2 default backends */
|
||||
Savepoint *pSavepoint; /* List of active savepoints */
|
||||
int busyTimeout; /* Busy handler timeout, in msec */
|
||||
int nSavepoint; /* Number of non-transaction savepoints */
|
||||
int nStatement; /* Number of nested statement-transactions */
|
||||
u8 isTransactionSavepoint; /* True if the outermost savepoint is a TS */
|
||||
i64 nDeferredCons; /* Net deferred constraints this transaction. */
|
||||
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_VdbeListing 0x00008000 /* Debug listings of VDBE programs */
|
||||
#define SQLITE_WriteSchema 0x00010000 /* OK to update SQLITE_MASTER */
|
||||
#define SQLITE_NoReadlock 0x00020000 /* Readlocks are omitted when
|
||||
** accessing read-only databases */
|
||||
/* 0x00020000 Unused */
|
||||
#define SQLITE_IgnoreChecks 0x00040000 /* Do not enforce check constraints */
|
||||
#define SQLITE_ReadUncommitted 0x0080000 /* For shared-cache mode */
|
||||
#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_CASE 0x02 /* Case-sensitive LIKE-type function */
|
||||
#define SQLITE_FUNC_EPHEM 0x04 /* Ephemeral. Delete with VDBE */
|
||||
#define SQLITE_FUNC_NEEDCOLL 0x08 /* sqlite3GetFuncCollSeq() might be called */
|
||||
#define SQLITE_FUNC_PRIVATE 0x10 /* Allowed for internal use only */
|
||||
#define SQLITE_FUNC_COUNT 0x20 /* Built-in count(*) aggregate */
|
||||
#define SQLITE_FUNC_COALESCE 0x40 /* Built-in coalesce() or ifnull() function */
|
||||
#define SQLITE_FUNC_COUNT 0x10 /* Built-in count(*) aggregate */
|
||||
#define SQLITE_FUNC_COALESCE 0x20 /* 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
|
||||
@ -1066,7 +1048,10 @@ struct FuncDestructor {
|
||||
** parameter.
|
||||
*/
|
||||
#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}
|
||||
#define STR_FUNCTION(zName, nArg, pArg, bNC, xFunc) \
|
||||
{nArg, SQLITE_UTF8, bNC*SQLITE_FUNC_NEEDCOLL, \
|
||||
@ -1296,7 +1281,7 @@ struct Table {
|
||||
FKey *pFKey; /* Linked list of all foreign keys in this table */
|
||||
char *zColAff; /* String defining the affinity of each column */
|
||||
#ifndef SQLITE_OMIT_CHECK
|
||||
Expr *pCheck; /* The AND of all CHECK constraints */
|
||||
ExprList *pCheck; /* All CHECK constraints */
|
||||
#endif
|
||||
#ifndef SQLITE_OMIT_ALTERTABLE
|
||||
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_Autoincrement 0x08 /* Integer primary key is autoincrement */
|
||||
#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 {
|
||||
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 */
|
||||
tRowcnt *aiRowEst; /* Result of ANALYZE: Est. rows selected by each column */
|
||||
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 */
|
||||
Index *pNext; /* The next index associated with the same table */
|
||||
Schema *pSchema; /* Schema containing this index */
|
||||
u8 *aSortOrder; /* Array of size Index.nColumn. True==DESC, False==ASC */
|
||||
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
|
||||
int nSample; /* Number of elements in aSample[] */
|
||||
tRowcnt avgEq; /* Average nEq value for key values not in aSample */
|
||||
@ -1553,8 +1536,8 @@ struct AggInfo {
|
||||
** than the source table */
|
||||
int sortingIdx; /* Cursor number of the sorting index */
|
||||
int sortingIdxPTab; /* Cursor number of pseudo-table */
|
||||
ExprList *pGroupBy; /* The group by clause */
|
||||
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 */
|
||||
Table *pTab; /* Source table */
|
||||
int iTable; /* Cursor number of the source table */
|
||||
@ -1564,7 +1547,6 @@ struct AggInfo {
|
||||
Expr *pExpr; /* The original expression */
|
||||
} *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.
|
||||
** Additional columns are used only as parameters to
|
||||
** aggregate functions */
|
||||
@ -1575,7 +1557,6 @@ struct AggInfo {
|
||||
int iDistinct; /* Ephemeral table used to enforce DISTINCT */
|
||||
} *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 */
|
||||
u8 flags2; /* Second set of flags. EP2_... */
|
||||
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 */
|
||||
Table *pTab; /* Table for TK_COLUMN expressions. */
|
||||
#if SQLITE_MAX_EXPR_DEPTH>0
|
||||
@ -1715,7 +1697,7 @@ struct Expr {
|
||||
#define EP_FixedDest 0x0200 /* Result needed in a specific register */
|
||||
#define EP_IntValue 0x0400 /* Integer value contained in u.iValue */
|
||||
#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_TokenOnly 0x4000 /* Expr struct is EXPR_TOKENONLYSIZE bytes only */
|
||||
#define EP_Static 0x8000 /* Held in memory not obtained from malloc() */
|
||||
@ -1772,9 +1754,8 @@ struct Expr {
|
||||
*/
|
||||
struct ExprList {
|
||||
int nExpr; /* Number of expressions on the list */
|
||||
int nAlloc; /* Number of entries allocated below */
|
||||
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 */
|
||||
char *zName; /* Token associated with this expression */
|
||||
char *zSpan; /* Original text of the expression */
|
||||
@ -1782,7 +1763,7 @@ struct ExprList {
|
||||
u8 done; /* A flag to indicate when processing is finished */
|
||||
u16 iOrderByCol; /* For ORDER BY, column number in result set */
|
||||
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 */
|
||||
} *a;
|
||||
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 hasAgg; /* True if aggregates are seen */
|
||||
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 */
|
||||
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 */
|
||||
char affinity; /* MakeRecord with this affinity for SRT_Set */
|
||||
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 */
|
||||
Expr *pWhere; /* The WHERE clause */
|
||||
ExprList *pGroupBy; /* The GROUP BY clause */
|
||||
@ -2071,9 +2053,6 @@ struct Select {
|
||||
Select *pRightmost; /* Right-most select in a compound select statement */
|
||||
Expr *pLimit; /* LIMIT 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_HasTypeInfo 0x20 /* FROM subqueries have Table metadata */
|
||||
#define SF_UseSorter 0x40 /* Sort using a sorter */
|
||||
#define SF_Values 0x80 /* Synthesized from VALUES clause */
|
||||
|
||||
|
||||
/*
|
||||
@ -2164,10 +2144,10 @@ struct AutoincInfo {
|
||||
*/
|
||||
struct TriggerPrg {
|
||||
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 */
|
||||
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 {
|
||||
sqlite3 *db; /* The main database structure */
|
||||
int rc; /* Return code from execution */
|
||||
char *zErrMsg; /* An error message */
|
||||
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 checkSchema; /* Causes schema cookie check after an error */
|
||||
u8 nested; /* Number of nested calls to the parser/code generator */
|
||||
u8 nTempReg; /* Number of temporary registers in aTempReg[] */
|
||||
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 nRangeReg; /* Size of the 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 iCacheLevel; /* ColCache valid when aColCache[].iLevel<=iCacheLevel */
|
||||
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 {
|
||||
int iTable; /* Table cursor number */
|
||||
int iColumn; /* Table column number */
|
||||
@ -2228,61 +2210,64 @@ struct Parse {
|
||||
} aColCache[SQLITE_N_COLCACHE]; /* One for each column cache entry */
|
||||
yDbMask writeMask; /* Start a write transaction on these 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 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
|
||||
int nTableLock; /* Number of locks in aTableLock */
|
||||
TableLock *aTableLock; /* Required table locks for shared-cache mode */
|
||||
#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 */
|
||||
int nMaxArg; /* Max args passed to user function by sub-program */
|
||||
|
||||
/* Information used while coding trigger programs. */
|
||||
Parse *pToplevel; /* Parse structure for main program (or NULL) */
|
||||
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 newmask; /* Mask of new.* columns referenced */
|
||||
u8 eTriggerOp; /* TK_UPDATE, TK_INSERT or TK_DELETE */
|
||||
u8 eOrconf; /* Default ON CONFLICT policy for trigger steps */
|
||||
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
|
||||
** each recursion */
|
||||
|
||||
int nVar; /* Number of '?' variables seen in the SQL so far */
|
||||
int nzVar; /* Number of available slots in azVar[] */
|
||||
char **azVar; /* Pointers to names of parameters */
|
||||
Vdbe *pReprepare; /* VM being reprepared (sqlite3Reprepare()) */
|
||||
int nAlias; /* Number of aliased result set columns */
|
||||
int *aAlias; /* Register used to hold aliased result */
|
||||
u8 explain; /* True if the EXPLAIN flag is found on the query */
|
||||
Token sNameToken; /* Token with unqualified schema object name */
|
||||
Token sLastToken; /* The last token parsed */
|
||||
const char *zTail; /* All SQL text past the last semicolon parsed */
|
||||
Table *pNewTable; /* A table being constructed by CREATE TABLE */
|
||||
int nVar; /* Number of '?' variables seen in the SQL so far */
|
||||
int nzVar; /* Number of available slots in azVar[] */
|
||||
u8 explain; /* True if the EXPLAIN flag is found on the query */
|
||||
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||||
u8 declareVtab; /* True if inside sqlite3_declare_vtab() */
|
||||
int nVtabLock; /* Number of virtual tables to lock */
|
||||
#endif
|
||||
int nAlias; /* Number of aliased result set columns */
|
||||
int nHeight; /* Expression tree height of current sub-select */
|
||||
#ifndef SQLITE_OMIT_EXPLAIN
|
||||
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 */
|
||||
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
|
||||
Token sArg; /* Complete text of a module argument */
|
||||
u8 declareVtab; /* True if inside sqlite3_declare_vtab() */
|
||||
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;
|
||||
Token sArg; /* Complete text of a module argument */
|
||||
Table **apVtabLock; /* Pointer to virtual tables needing locking */
|
||||
#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
|
||||
#define IN_DECLARE_VTAB 0
|
||||
#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_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_USESEEKRESULT 0x10 /* Try to avoid a seek in BtreeInsert() */
|
||||
#define OPFLAG_CLEARCACHE 0x20 /* Clear pseudo-table cache in OP_Column */
|
||||
#define OPFLAG_LENGTHARG 0x40 /* OP_Column only used for length() */
|
||||
#define OPFLAG_TYPEOFARG 0x80 /* OP_Column only used for typeof() */
|
||||
|
||||
/*
|
||||
* Each trigger present in the database schema is stored as an instance of
|
||||
@ -2433,8 +2420,8 @@ struct StrAccum {
|
||||
*/
|
||||
typedef struct {
|
||||
sqlite3 *db; /* The database being initialized */
|
||||
int iDb; /* 0 for main database. 1 for TEMP, 2.. for ATTACHed */
|
||||
char **pzErrMsg; /* Error message stored here */
|
||||
int iDb; /* 0 for main database. 1 for TEMP, 2.. for ATTACHed */
|
||||
int rc; /* Result code stored here */
|
||||
} InitData;
|
||||
|
||||
@ -2489,6 +2476,7 @@ struct Walker {
|
||||
union { /* Extra data for callback */
|
||||
NameContext *pNC; /* Naming context */
|
||||
int i; /* Integer value */
|
||||
SrcList *pSrcList; /* FROM clause */
|
||||
} u;
|
||||
};
|
||||
|
||||
@ -2576,7 +2564,7 @@ int sqlite3CantopenError(int);
|
||||
/*
|
||||
** Internal function prototypes
|
||||
*/
|
||||
int sqlite3StrICmp(const char *, const char *);
|
||||
#define sqlite3StrICmp sqlite3_stricmp
|
||||
int sqlite3Strlen30(const char*);
|
||||
#define sqlite3StrNICmp sqlite3_strnicmp
|
||||
|
||||
@ -2724,6 +2712,7 @@ void sqlite3AddCollateType(Parse*, Token*);
|
||||
void sqlite3EndTable(Parse*,Token*,Token*,Select*);
|
||||
int sqlite3ParseUri(const char*,const char*,unsigned int*,
|
||||
sqlite3_vfs**,char**,char **);
|
||||
Btree *sqlite3DbNameToBtree(sqlite3*,const char*);
|
||||
int sqlite3CodeOnce(Parse *);
|
||||
|
||||
Bitvec *sqlite3BitvecCreate(u32);
|
||||
@ -2759,7 +2748,7 @@ void sqlite3DeleteTable(sqlite3*, Table*);
|
||||
# define sqlite3AutoincrementEnd(X)
|
||||
#endif
|
||||
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*);
|
||||
int sqlite3IdListIndex(IdList*,const char*);
|
||||
SrcList *sqlite3SrcListEnlarge(sqlite3*, SrcList*, int, int);
|
||||
@ -2789,7 +2778,7 @@ void sqlite3DeleteFrom(Parse*, SrcList*, Expr*);
|
||||
void sqlite3Update(Parse*, SrcList*, ExprList*, Expr*, int);
|
||||
WhereInfo *sqlite3WhereBegin(Parse*, SrcList*, Expr*, ExprList**,ExprList*,u16);
|
||||
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 sqlite3ExprCodeMove(Parse*, int, int, int);
|
||||
void sqlite3ExprCodeCopy(Parse*, int, int, int);
|
||||
@ -2823,7 +2812,7 @@ Vdbe *sqlite3GetVdbe(Parse*);
|
||||
void sqlite3PrngSaveState(void);
|
||||
void sqlite3PrngRestoreState(void);
|
||||
void sqlite3PrngResetState(void);
|
||||
void sqlite3RollbackAll(sqlite3*);
|
||||
void sqlite3RollbackAll(sqlite3*,int);
|
||||
void sqlite3CodeVerifySchema(Parse*, int);
|
||||
void sqlite3CodeVerifyNamedSchema(Parse*, const char *zDb);
|
||||
void sqlite3BeginTransaction(Parse*, int);
|
||||
@ -2856,7 +2845,7 @@ SrcList *sqlite3SrcListDup(sqlite3*,SrcList*,int);
|
||||
IdList *sqlite3IdListDup(sqlite3*,IdList*);
|
||||
Select *sqlite3SelectDup(sqlite3*,Select*,int);
|
||||
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 sqlite3RegisterDateTimeFunctions(void);
|
||||
void sqlite3RegisterGlobalFunctions(void);
|
||||
@ -2997,7 +2986,7 @@ void sqlite3FileSuffix3(const char*, char*);
|
||||
#else
|
||||
# define sqlite3FileSuffix3(X,Y)
|
||||
#endif
|
||||
u8 sqlite3GetBoolean(const char *z);
|
||||
u8 sqlite3GetBoolean(const char *z,int);
|
||||
|
||||
const void *sqlite3ValueText(sqlite3_value*, u8);
|
||||
int sqlite3ValueBytes(sqlite3_value*, u8);
|
||||
@ -3123,7 +3112,7 @@ void sqlite3AutoLoadExtensions(sqlite3*);
|
||||
# define sqlite3VtabInSync(db) ((db)->nVTrans>0 && (db)->aVTrans==0)
|
||||
#endif
|
||||
void sqlite3VtabMakeWritable(Parse*,Table*);
|
||||
void sqlite3VtabBeginParse(Parse*, Token*, Token*, Token*);
|
||||
void sqlite3VtabBeginParse(Parse*, Token*, Token*, Token*, int);
|
||||
void sqlite3VtabFinishParse(Parse*, Token*);
|
||||
void sqlite3VtabArgInit(Parse*);
|
||||
void sqlite3VtabArgExtend(Parse*, Token*);
|
||||
|
@ -224,10 +224,12 @@ int sqlite3_db_status(
|
||||
** to zero.
|
||||
*/
|
||||
case SQLITE_DBSTATUS_CACHE_HIT:
|
||||
case SQLITE_DBSTATUS_CACHE_MISS: {
|
||||
case SQLITE_DBSTATUS_CACHE_MISS:
|
||||
case SQLITE_DBSTATUS_CACHE_WRITE:{
|
||||
int i;
|
||||
int nRet = 0;
|
||||
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++){
|
||||
if( db->aDb[i].pBt ){
|
||||
|
@ -1163,7 +1163,7 @@ static int dbPrepareAndBind(
|
||||
memset(pPreStmt, 0, nByte);
|
||||
|
||||
pPreStmt->pStmt = pStmt;
|
||||
pPreStmt->nSql = (*pzOut - zSql);
|
||||
pPreStmt->nSql = (int)(*pzOut - zSql);
|
||||
pPreStmt->zSql = sqlite3_sql(pStmt);
|
||||
pPreStmt->apParm = (Tcl_Obj **)&pPreStmt[1];
|
||||
#ifdef SQLITE_TEST
|
||||
@ -3108,23 +3108,19 @@ EXTERN int Sqlite3_Init(Tcl_Interp *interp){
|
||||
return TCL_OK;
|
||||
}
|
||||
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 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
|
||||
int Sqlite_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 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
|
||||
|
||||
#ifdef TCLSH
|
||||
@ -3474,7 +3470,7 @@ static int md5file_cmd(void*cd, Tcl_Interp*interp, int argc, const char **argv){
|
||||
MD5Init(&ctx);
|
||||
for(;;){
|
||||
int n;
|
||||
n = fread(zBuf, 1, sizeof(zBuf), in);
|
||||
n = (int)fread(zBuf, 1, sizeof(zBuf), in);
|
||||
if( n<=0 ) break;
|
||||
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++){
|
||||
const char *zData = (char*)sqlite3_value_text(argv[i]);
|
||||
if( zData ){
|
||||
MD5Update(p, (unsigned char*)zData, strlen(zData));
|
||||
MD5Update(p, (unsigned char*)zData, (int)strlen(zData));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
63
src/test1.c
63
src/test1.c
@ -668,6 +668,7 @@ static int test_key(
|
||||
int argc, /* Number of arguments */
|
||||
char **argv /* Text of each argument */
|
||||
){
|
||||
#ifdef SQLITE_HAS_CODEC
|
||||
sqlite3 *db;
|
||||
const char *zKey;
|
||||
int nKey;
|
||||
@ -679,7 +680,6 @@ static int test_key(
|
||||
if( getDbPointer(interp, argv[1], &db) ) return TCL_ERROR;
|
||||
zKey = argv[2];
|
||||
nKey = strlen(zKey);
|
||||
#ifdef SQLITE_HAS_CODEC
|
||||
sqlite3_key(db, zKey, nKey);
|
||||
#endif
|
||||
return TCL_OK;
|
||||
@ -696,6 +696,7 @@ static int test_rekey(
|
||||
int argc, /* Number of arguments */
|
||||
char **argv /* Text of each argument */
|
||||
){
|
||||
#ifdef SQLITE_HAS_CODEC
|
||||
sqlite3 *db;
|
||||
const char *zKey;
|
||||
int nKey;
|
||||
@ -707,7 +708,6 @@ static int test_rekey(
|
||||
if( getDbPointer(interp, argv[1], &db) ) return TCL_ERROR;
|
||||
zKey = argv[2];
|
||||
nKey = strlen(zKey);
|
||||
#ifdef SQLITE_HAS_CODEC
|
||||
sqlite3_rekey(db, zKey, nKey);
|
||||
#endif
|
||||
return TCL_OK;
|
||||
@ -800,7 +800,7 @@ struct dstr {
|
||||
** Append text to a dstr
|
||||
*/
|
||||
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 ){
|
||||
char *zNew;
|
||||
p->nAlloc = p->nAlloc*2 + n + 200;
|
||||
@ -2369,7 +2369,6 @@ static int uses_stmt_journal(
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
sqlite3_stmt *pStmt;
|
||||
int rc;
|
||||
|
||||
if( objc!=2 ){
|
||||
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;
|
||||
rc = sqlite3_stmt_readonly(pStmt);
|
||||
sqlite3_stmt_readonly(pStmt);
|
||||
Tcl_SetObjResult(interp, Tcl_NewBooleanObj(((Vdbe *)pStmt)->usesStmtJournal));
|
||||
return TCL_OK;
|
||||
}
|
||||
@ -3264,7 +3263,7 @@ static int test_bind_text16(
|
||||
char *value;
|
||||
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 *oN = objv[objc-3];
|
||||
Tcl_Obj *oString = objv[objc-2];
|
||||
@ -3610,10 +3609,10 @@ static int test_prepare(
|
||||
if( sqlite3TestErrCode(interp, db, rc) ) return TCL_ERROR;
|
||||
if( zTail && objc>=5 ){
|
||||
if( bytes>=0 ){
|
||||
bytes = bytes - (zTail-zSql);
|
||||
bytes = bytes - (int)(zTail-zSql);
|
||||
}
|
||||
if( strlen(zTail)<bytes ){
|
||||
bytes = strlen(zTail);
|
||||
if( (int)strlen(zTail)<bytes ){
|
||||
bytes = (int)strlen(zTail);
|
||||
}
|
||||
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( zTail && objc>=5 ){
|
||||
if( bytes>=0 ){
|
||||
bytes = bytes - (zTail-zSql);
|
||||
bytes = bytes - (int)(zTail-zSql);
|
||||
}
|
||||
Tcl_ObjSetVar2(interp, objv[4], 0, Tcl_NewStringObj(zTail, bytes), 0);
|
||||
}
|
||||
@ -3769,7 +3768,7 @@ static int test_prepare16(
|
||||
|
||||
if( objc>=5 ){
|
||||
if( zTail ){
|
||||
objlen = objlen - ((u8 *)zTail-(u8 *)zSql);
|
||||
objlen = objlen - (int)((u8 *)zTail-(u8 *)zSql);
|
||||
}else{
|
||||
objlen = 0;
|
||||
}
|
||||
@ -3829,7 +3828,7 @@ static int test_prepare16_v2(
|
||||
|
||||
if( objc>=5 ){
|
||||
if( zTail ){
|
||||
objlen = objlen - ((u8 *)zTail-(u8 *)zSql);
|
||||
objlen = objlen - (int)((u8 *)zTail-(u8 *)zSql);
|
||||
}else{
|
||||
objlen = 0;
|
||||
}
|
||||
@ -3858,7 +3857,6 @@ static int test_open(
|
||||
){
|
||||
const char *zFilename;
|
||||
sqlite3 *db;
|
||||
int rc;
|
||||
char zBuf[100];
|
||||
|
||||
if( objc!=3 && objc!=2 && objc!=1 ){
|
||||
@ -3868,7 +3866,7 @@ static int test_open(
|
||||
}
|
||||
|
||||
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;
|
||||
Tcl_AppendResult(interp, zBuf, 0);
|
||||
@ -3957,7 +3955,6 @@ static int test_open16(
|
||||
#ifndef SQLITE_OMIT_UTF16
|
||||
const void *zFilename;
|
||||
sqlite3 *db;
|
||||
int rc;
|
||||
char zBuf[100];
|
||||
|
||||
if( objc!=3 ){
|
||||
@ -3967,7 +3964,7 @@ static int test_open16(
|
||||
}
|
||||
|
||||
zFilename = Tcl_GetByteArrayFromObj(objv[1], 0);
|
||||
rc = sqlite3_open16(zFilename, &db);
|
||||
sqlite3_open16(zFilename, &db);
|
||||
|
||||
if( sqlite3TestMakePointerStr(interp, zBuf, db) ) return TCL_ERROR;
|
||||
Tcl_AppendResult(interp, zBuf, 0);
|
||||
@ -4668,6 +4665,30 @@ static int test_db_filename(
|
||||
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?
|
||||
**
|
||||
@ -5114,8 +5135,6 @@ static int file_control_lockproxy_test(
|
||||
Tcl_Obj *CONST objv[] /* Command arguments */
|
||||
){
|
||||
sqlite3 *db;
|
||||
const char *zPwd;
|
||||
int nPwd;
|
||||
|
||||
if( objc!=3 ){
|
||||
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) ){
|
||||
return TCL_ERROR;
|
||||
}
|
||||
zPwd = Tcl_GetStringFromObj(objv[2], &nPwd);
|
||||
|
||||
#if !defined(SQLITE_ENABLE_LOCKING_STYLE)
|
||||
# if defined(__APPLE__)
|
||||
@ -5138,8 +5156,11 @@ static int file_control_lockproxy_test(
|
||||
{
|
||||
char *testPath;
|
||||
int rc;
|
||||
int nPwd;
|
||||
const char *zPwd;
|
||||
char proxyPath[400];
|
||||
|
||||
zPwd = Tcl_GetStringFromObj(objv[2], &nPwd);
|
||||
if( sizeof(proxyPath)<nPwd+20 ){
|
||||
Tcl_AppendResult(interp, "PWD too big", (void*)0);
|
||||
return TCL_ERROR;
|
||||
@ -5795,6 +5816,7 @@ struct win32FileLocker {
|
||||
|
||||
|
||||
#if SQLITE_OS_WIN
|
||||
#include <process.h>
|
||||
/*
|
||||
** 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_db_release_memory", test_db_release_memory, 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_thread_cleanup", test_thread_cleanup, 0},
|
||||
{ "sqlite3_pager_refcounts", test_pager_refcounts, 0},
|
||||
@ -6106,7 +6129,7 @@ int Sqlitetest1_Init(Tcl_Interp *interp){
|
||||
#endif
|
||||
#ifdef SQLITE_ENABLE_COLUMN_METADATA
|
||||
{"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_origin_name16", test_stmt_utf16, (void*)sqlite3_column_origin_name16},
|
||||
#endif
|
||||
|
@ -547,7 +547,7 @@ static int fake_big_file(
|
||||
if( Tcl_GetInt(interp, argv[1], &n) ) return TCL_ERROR;
|
||||
|
||||
pVfs = sqlite3_vfs_find(0);
|
||||
nFile = strlen(argv[2]);
|
||||
nFile = (int)strlen(argv[2]);
|
||||
zFile = sqlite3_malloc( nFile+2 );
|
||||
if( zFile==0 ) return TCL_ERROR;
|
||||
memcpy(zFile, argv[2], nFile+1);
|
||||
|
@ -80,7 +80,7 @@ static int btree_open(
|
||||
sDb.mutex = sqlite3MutexAlloc(SQLITE_MUTEX_RECURSIVE);
|
||||
sqlite3_mutex_enter(sDb.mutex);
|
||||
}
|
||||
n = strlen(argv[1]);
|
||||
n = (int)strlen(argv[1]);
|
||||
zFilename = sqlite3_malloc( n+2 );
|
||||
if( zFilename==0 ) return TCL_ERROR;
|
||||
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;
|
||||
in = start;
|
||||
in *= mult;
|
||||
for(i=0; i<count; i++){
|
||||
for(i=0; i<(int)count; i++){
|
||||
char zErr[200];
|
||||
n1 = putVarint(zBuf, in);
|
||||
if( n1>9 || n1<1 ){
|
||||
|
@ -64,7 +64,6 @@ static int test_value_overhead(
|
||||
int repeat_count;
|
||||
int i;
|
||||
Mem val;
|
||||
const char *zVal;
|
||||
|
||||
if( objc!=3 ){
|
||||
Tcl_AppendResult(interp, "wrong # args: should be \"",
|
||||
@ -82,7 +81,7 @@ static int test_value_overhead(
|
||||
|
||||
for(i=0; i<repeat_count; i++){
|
||||
if( do_calls ){
|
||||
zVal = (char*)sqlite3_value_text(&val);
|
||||
sqlite3_value_text(&val);
|
||||
}
|
||||
}
|
||||
|
||||
|
28
src/test6.c
28
src/test6.c
@ -177,7 +177,7 @@ static int writeDbFile(CrashFile *p, u8 *z, i64 iAmt, i64 iOff){
|
||||
iSkip = 512;
|
||||
}
|
||||
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;
|
||||
}
|
||||
@ -306,8 +306,8 @@ static int writeListSync(CrashFile *pFile, int isCrash){
|
||||
}
|
||||
case 3: { /* Trash sectors */
|
||||
u8 *zGarbage;
|
||||
int iFirst = (pWrite->iOffset/g.iSectorSize);
|
||||
int iLast = (pWrite->iOffset+pWrite->nBuf-1)/g.iSectorSize;
|
||||
int iFirst = (int)(pWrite->iOffset/g.iSectorSize);
|
||||
int iLast = (int)((pWrite->iOffset+pWrite->nBuf-1)/g.iSectorSize);
|
||||
|
||||
assert(pWrite->zBuf);
|
||||
|
||||
@ -430,7 +430,7 @@ static int cfWrite(
|
||||
){
|
||||
CrashFile *pCrash = (CrashFile *)pFile;
|
||||
if( iAmt+iOfst>pCrash->iSize ){
|
||||
pCrash->iSize = iAmt+iOfst;
|
||||
pCrash->iSize = (int)(iAmt+iOfst);
|
||||
}
|
||||
while( pCrash->iSize>pCrash->nData ){
|
||||
u8 *zNew;
|
||||
@ -454,7 +454,7 @@ static int cfTruncate(sqlite3_file *pFile, sqlite_int64 size){
|
||||
CrashFile *pCrash = (CrashFile *)pFile;
|
||||
assert(size>=0);
|
||||
if( pCrash->iSize>size ){
|
||||
pCrash->iSize = size;
|
||||
pCrash->iSize = (int)size;
|
||||
}
|
||||
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 *zCrashFile = g.zCrashFile;
|
||||
int nName = strlen(zName);
|
||||
int nCrashFile = strlen(zCrashFile);
|
||||
int nName = (int)strlen(zName);
|
||||
int nCrashFile = (int)strlen(zCrashFile);
|
||||
|
||||
if( nCrashFile>0 && zCrashFile[nCrashFile-1]=='*' ){
|
||||
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) ){
|
||||
#ifdef TRACE_CRASHTEST
|
||||
printf("cfSync(): name matched, g.iCrash = %d\n", g.iCrash);
|
||||
#endif
|
||||
if( (--g.iCrash)==0 ) isCrash = 1;
|
||||
}
|
||||
|
||||
@ -510,7 +518,7 @@ static int cfFileControl(sqlite3_file *pFile, int op, void *pArg){
|
||||
i64 nByte = *(i64 *)pArg;
|
||||
if( nByte>pCrash->iSize ){
|
||||
if( SQLITE_OK==writeListAppend(pFile, nByte, 0, 0) ){
|
||||
pCrash->iSize = nByte;
|
||||
pCrash->iSize = (int)nByte;
|
||||
}
|
||||
}
|
||||
return SQLITE_OK;
|
||||
@ -627,11 +635,11 @@ static int cfOpen(
|
||||
iChunk = PENDING_BYTE;
|
||||
}
|
||||
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 ){
|
||||
i64 iOff = PENDING_BYTE+512;
|
||||
iChunk = pWrapper->iSize - iOff;
|
||||
rc = sqlite3OsRead(pReal, &pWrapper->zData[iOff], iChunk, iOff);
|
||||
rc = sqlite3OsRead(pReal, &pWrapper->zData[iOff], (int)iChunk, iOff);
|
||||
}
|
||||
}else{
|
||||
rc = SQLITE_NOMEM;
|
||||
|
20
src/test8.c
20
src/test8.c
@ -192,7 +192,7 @@ static int getColumnNames(
|
||||
rc = SQLITE_NOMEM;
|
||||
goto out;
|
||||
}
|
||||
nBytes += strlen(zName)+1;
|
||||
nBytes += (int)strlen(zName)+1;
|
||||
}
|
||||
aCol = (char **)sqlite3MallocZero(nBytes);
|
||||
if( !aCol ){
|
||||
@ -831,13 +831,10 @@ static int echoBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
|
||||
if( !isIgnoreUsable && !pConstraint->usable ) continue;
|
||||
|
||||
iCol = pConstraint->iColumn;
|
||||
if( pVtab->aIndex[iCol] || iCol<0 ){
|
||||
char *zCol = pVtab->aCol[iCol];
|
||||
if( iCol<0 || pVtab->aIndex[iCol] ){
|
||||
char *zCol = iCol>=0 ? pVtab->aCol[iCol] : "rowid";
|
||||
char *zOp = 0;
|
||||
useIdx = 1;
|
||||
if( iCol<0 ){
|
||||
zCol = "rowid";
|
||||
}
|
||||
switch( pConstraint->op ){
|
||||
case SQLITE_INDEX_CONSTRAINT_EQ:
|
||||
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
|
||||
** 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;
|
||||
char *zCol = pVtab->aCol[iCol];
|
||||
char *zCol = iCol>=0 ? pVtab->aCol[iCol] : "rowid";
|
||||
char *zDir = pIdxInfo->aOrderBy->desc?"DESC":"ASC";
|
||||
if( iCol<0 ){
|
||||
zCol = "rowid";
|
||||
}
|
||||
zNew = sqlite3_mprintf(" ORDER BY %s %s", zCol, zDir);
|
||||
string_concat(&zQuery, zNew, 1, &rc);
|
||||
pIdxInfo->orderByConsumed = 1;
|
||||
@ -1221,7 +1217,7 @@ static int echoRename(sqlite3_vtab *vtab, const char *zNewName){
|
||||
}
|
||||
|
||||
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",
|
||||
p->zTableName, zNewName, &p->zTableName[nThis]
|
||||
);
|
||||
|
@ -420,6 +420,12 @@ Tcl_SetVar2(interp, "sqlite_options", "long_double",
|
||||
Tcl_SetVar2(interp, "sqlite_options", "rtree", "0", TCL_GLOBAL_ONLY);
|
||||
#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
|
||||
Tcl_SetVar2(interp, "sqlite_options", "schema_pragmas", "0", TCL_GLOBAL_ONLY);
|
||||
#else
|
||||
|
@ -202,7 +202,7 @@ static void test_auxdata(
|
||||
}else {
|
||||
zRet[i*2] = '0';
|
||||
}
|
||||
n = strlen(z) + 1;
|
||||
n = (int)strlen(z) + 1;
|
||||
zAux = testContextMalloc(pCtx, n);
|
||||
if( zAux ){
|
||||
memcpy(zAux, z, n);
|
||||
|
@ -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.
|
||||
**
|
||||
** 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".
|
||||
** Note that all fuzzer virtual tables must be TEMP tables. The
|
||||
** "temp." prefix in front of the table name is required when the
|
||||
** table is being created. The "temp." prefix can be omitted when
|
||||
** using the table as long as the name is unambiguous.
|
||||
** When it is created, the new fuzzer table must be supplied with the
|
||||
** name of a "fuzzer data table", which must reside in the same database
|
||||
** file as the new fuzzer table. The fuzzer data table contains the various
|
||||
** transformations and their costs that the fuzzer logic uses to generate
|
||||
** variations.
|
||||
**
|
||||
** Before being used, the fuzzer needs to be programmed by giving it
|
||||
** character transformations and a cost associated with each transformation.
|
||||
** Examples:
|
||||
** The fuzzer data table must contain exactly four columns (more precisely,
|
||||
** the statement "SELECT * FROM <fuzzer_data_table>" must return records
|
||||
** 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
|
||||
** 100. (All costs are integers. We recommend that costs be scaled so
|
||||
** that the average cost is around 100.)
|
||||
** CREATE TABLE f_data(ruleset, cFrom, cTo, Cost);
|
||||
** INSERT INTO f_data(ruleset, cFrom, cTo, Cost) VALUES(0, '', 'a', 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 above statement says that the cost of deleting a single letter
|
||||
** 'b' is 87.
|
||||
**
|
||||
** INSERT INTO f(cFrom,cTo,Cost) VALUES('o','oe',38);
|
||||
** INSERT INTO f(cFrom,cTo,Cost) VALUES('oe','o',40);
|
||||
**
|
||||
** This third example says that the cost of transforming the single
|
||||
** letter "o" into the two-letter sequence "oe" is 38 and that the
|
||||
** 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
|
||||
** costs are integers. We recommend that costs be scaled so that the
|
||||
** 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
|
||||
** and fourth INSERT statements mean that the cost of transforming a
|
||||
** single letter "o" into the two-letter sequence "oe" is 38 and that the
|
||||
** cost of transforming "oe" back into "o" is 40.
|
||||
**
|
||||
** After all the transformation costs have been set, the fuzzer table
|
||||
** can be queried as follows:
|
||||
** The contents of the fuzzer data table are loaded into main memory when
|
||||
** 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
|
||||
** WHERE word MATCH 'abcdefg'
|
||||
@ -61,6 +76,9 @@
|
||||
** the one that is returned. In the example, the search is limited to
|
||||
** 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
|
||||
** 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
|
||||
@ -93,7 +111,42 @@
|
||||
**
|
||||
** This last query will show up to 50 words out of the vocabulary that
|
||||
** 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 <stdlib.h>
|
||||
#include <string.h>
|
||||
@ -112,10 +165,25 @@ typedef struct fuzzer_seen fuzzer_seen;
|
||||
typedef struct fuzzer_stem fuzzer_stem;
|
||||
|
||||
/*
|
||||
** Type of the "cost" of an edit operation. Might be changed to
|
||||
** "float" or "double" or "sqlite3_int64" in the future.
|
||||
** Various types.
|
||||
**
|
||||
** 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 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.
|
||||
*/
|
||||
struct fuzzer_rule {
|
||||
fuzzer_rule *pNext; /* Next rule in order of increasing rCost */
|
||||
fuzzer_cost rCost; /* Cost of this transformation */
|
||||
int nFrom, nTo; /* Length of the zFrom and zTo strings */
|
||||
char *zFrom; /* Transform from */
|
||||
char zTo[4]; /* Transform to (extra space appended) */
|
||||
fuzzer_rule *pNext; /* Next rule in order of increasing rCost */
|
||||
char *zFrom; /* Transform from */
|
||||
fuzzer_cost rCost; /* Cost of this transformation */
|
||||
fuzzer_len nFrom, nTo; /* Length of the zFrom and zTo strings */
|
||||
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 {
|
||||
char *zBasis; /* Word being fuzzed */
|
||||
int nBasis; /* Length of the zBasis string */
|
||||
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 *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 */
|
||||
char *zClassName; /* Name of this class. Default: "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 */
|
||||
};
|
||||
|
||||
@ -179,54 +247,11 @@ struct fuzzer_cursor {
|
||||
char *zBuf; /* Temporary use buffer */
|
||||
int nBuf; /* Bytes allocated for zBuf */
|
||||
int nStem; /* Number of stems allocated */
|
||||
int iRuleset; /* Only process rules from this ruleset */
|
||||
fuzzer_rule nullRule; /* Null rule used first */
|
||||
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
|
||||
** 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;
|
||||
}
|
||||
|
||||
/*
|
||||
** 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.
|
||||
@ -268,25 +557,6 @@ static int fuzzerOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){
|
||||
memset(pCur, 0, sizeof(*pCur));
|
||||
pCur->pVtab = p;
|
||||
*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++;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
@ -343,8 +613,8 @@ static int fuzzerRender(
|
||||
int *pnBuf /* Size of the buffer */
|
||||
){
|
||||
const fuzzer_rule *pRule = pStem->pRule;
|
||||
int n;
|
||||
char *z;
|
||||
int n; /* Size of output term without nul-term */
|
||||
char *z; /* Buffer to assemble output term in */
|
||||
|
||||
n = pStem->nBasis + pRule->nTo - pRule->nFrom;
|
||||
if( (*pnBuf)<n+1 ){
|
||||
@ -362,6 +632,8 @@ static int fuzzerRender(
|
||||
memcpy(&z[n+pRule->nTo], &pStem->zBasis[n+pRule->nFrom],
|
||||
pStem->nBasis-n-pRule->nFrom+1);
|
||||
}
|
||||
|
||||
assert( z[pStem->nBasis + pRule->nTo - pRule->nFrom]==0 );
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
@ -424,12 +696,31 @@ static int fuzzerSeen(fuzzer_cursor *pCur, fuzzer_stem *pStem){
|
||||
}
|
||||
h = fuzzerHash(pCur->zBuf);
|
||||
pLookup = pCur->apHash[h];
|
||||
while( pLookup && strcmp(pLookup->zBasis, pCur->zBuf)!=0 ){
|
||||
while( pLookup && strcmp(pLookup->zBasis, pCur->zBuf)!=0 ){
|
||||
pLookup = pLookup->pHash;
|
||||
}
|
||||
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
|
||||
** 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){
|
||||
const fuzzer_rule *pRule;
|
||||
while( (pRule = pStem->pRule)!=0 ){
|
||||
assert( pRule==&pCur->nullRule || pRule->iRuleset==pCur->iRuleset );
|
||||
while( pStem->n < pStem->nBasis - pRule->nFrom ){
|
||||
pStem->n++;
|
||||
if( pRule->nFrom==0
|
||||
@ -453,8 +745,11 @@ static int fuzzerAdvance(fuzzer_cursor *pCur, fuzzer_stem *pStem){
|
||||
}
|
||||
}
|
||||
pStem->n = -1;
|
||||
pStem->pRule = pRule->pNext;
|
||||
if( pStem->pRule && fuzzerCost(pStem)>pCur->rLimit ) pStem->pRule = 0;
|
||||
do{
|
||||
pRule = pRule->pNext;
|
||||
}while( fuzzerSkipRule(pRule, pStem, pCur->iRuleset) );
|
||||
pStem->pRule = pRule;
|
||||
if( pRule && fuzzerCost(pStem)>pCur->rLimit ) pStem->pRule = 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@ -572,15 +867,20 @@ static fuzzer_stem *fuzzerNewStem(
|
||||
fuzzer_cost rBaseCost
|
||||
){
|
||||
fuzzer_stem *pNew;
|
||||
fuzzer_rule *pRule;
|
||||
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;
|
||||
memset(pNew, 0, sizeof(*pNew));
|
||||
pNew->zBasis = (char*)&pNew[1];
|
||||
pNew->nBasis = strlen(zWord);
|
||||
pNew->nBasis = (int)strlen(zWord);
|
||||
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->rBaseCost = pNew->rCostX = rBaseCost;
|
||||
h = fuzzerHash(pNew->zBasis);
|
||||
@ -627,7 +927,10 @@ static int fuzzerNext(sqlite3_vtab_cursor *cur){
|
||||
** stem list is the next lowest cost word.
|
||||
*/
|
||||
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;
|
||||
pStem = fuzzerInsert(pCur, pStem);
|
||||
if( (rc = fuzzerSeen(pCur, pStem))!=0 ){
|
||||
@ -665,30 +968,44 @@ static int fuzzerFilter(
|
||||
int argc, sqlite3_value **argv
|
||||
){
|
||||
fuzzer_cursor *pCur = (fuzzer_cursor *)pVtabCursor;
|
||||
const char *zWord = 0;
|
||||
const char *zWord = "";
|
||||
fuzzer_stem *pStem;
|
||||
int idx;
|
||||
|
||||
fuzzerClearCursor(pCur, 1);
|
||||
pCur->rLimit = 2147483647;
|
||||
if( idxNum==1 ){
|
||||
idx = 0;
|
||||
if( idxNum & 1 ){
|
||||
zWord = (const char*)sqlite3_value_text(argv[0]);
|
||||
}else if( idxNum==2 ){
|
||||
pCur->rLimit = (fuzzer_cost)sqlite3_value_int(argv[0]);
|
||||
}else if( idxNum==3 ){
|
||||
zWord = (const char*)sqlite3_value_text(argv[0]);
|
||||
pCur->rLimit = (fuzzer_cost)sqlite3_value_int(argv[1]);
|
||||
idx++;
|
||||
}
|
||||
if( idxNum & 2 ){
|
||||
pCur->rLimit = (fuzzer_cost)sqlite3_value_int(argv[idx]);
|
||||
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.rCost = 0;
|
||||
pCur->nullRule.nFrom = 0;
|
||||
pCur->nullRule.nTo = 0;
|
||||
pCur->nullRule.zFrom = "";
|
||||
pStem->pRule = &pCur->nullRule;
|
||||
pStem->n = pStem->nBasis;
|
||||
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;
|
||||
}
|
||||
|
||||
@ -735,22 +1052,29 @@ static int fuzzerEof(sqlite3_vtab_cursor *cur){
|
||||
/*
|
||||
** Search for terms of these forms:
|
||||
**
|
||||
** word MATCH $str
|
||||
** distance < $value
|
||||
** distance <= $value
|
||||
** (A) word MATCH $str
|
||||
** (B1) distance < $value
|
||||
** (B2) distance <= $value
|
||||
** (C) ruleid == $ruleid
|
||||
**
|
||||
** 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
|
||||
** 1: There is a "word MATCH" term with $str in filter.argv[0].
|
||||
** 2: There is a "distance<" term with $value in filter.argv[0].
|
||||
** 3: Both "word MATCH" and "distance<" with $str in argv[0] and
|
||||
** $value in argv[1].
|
||||
** bit 1: Term of the form (A) found
|
||||
** bit 2: Term like (B1) or (B2) found
|
||||
** bit 3: Term like (C) found
|
||||
**
|
||||
** 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){
|
||||
int iPlan = 0;
|
||||
int iDistTerm = -1;
|
||||
int iRulesetTerm = -1;
|
||||
int i;
|
||||
const struct sqlite3_index_constraint *pConstraint;
|
||||
pConstraint = pIdxInfo->aConstraint;
|
||||
@ -772,11 +1096,23 @@ static int fuzzerBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
|
||||
iPlan |= 2;
|
||||
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 ){
|
||||
pIdxInfo->aConstraintUsage[iDistTerm].argvIndex = 1;
|
||||
}else if( iPlan==3 ){
|
||||
pIdxInfo->aConstraintUsage[iDistTerm].argvIndex = 2;
|
||||
if( iPlan & 2 ){
|
||||
pIdxInfo->aConstraintUsage[iDistTerm].argvIndex = 1+((iPlan&1)!=0);
|
||||
}
|
||||
if( iPlan & 4 ){
|
||||
int idx = 1;
|
||||
if( iPlan & 1 ) idx++;
|
||||
if( iPlan & 2 ) idx++;
|
||||
pIdxInfo->aConstraintUsage[iRulesetTerm].argvIndex = idx;
|
||||
}
|
||||
pIdxInfo->idxNum = iPlan;
|
||||
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.
|
||||
**
|
||||
** 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.
|
||||
** A virtual table module that implements the "fuzzer".
|
||||
*/
|
||||
static sqlite3_module fuzzerModule = {
|
||||
0, /* iVersion */
|
||||
@ -872,7 +1143,7 @@ static sqlite3_module fuzzerModule = {
|
||||
fuzzerEof, /* xEof - check for end of scan */
|
||||
fuzzerColumn, /* xColumn - read data */
|
||||
fuzzerRowid, /* xRowid - read data */
|
||||
fuzzerUpdate, /* xUpdate - INSERT */
|
||||
0, /* xUpdate */
|
||||
0, /* xBegin */
|
||||
0, /* xSync */
|
||||
0, /* xCommit */
|
||||
@ -916,7 +1187,7 @@ static int register_fuzzer_module(
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "DB");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
|
||||
getDbPointer(interp, Tcl_GetString(objv[1]), &db);
|
||||
fuzzer_register(db);
|
||||
return TCL_OK;
|
||||
}
|
||||
|
@ -126,7 +126,7 @@ static int hexio_read(
|
||||
return TCL_ERROR;
|
||||
}
|
||||
fseek(in, offset, SEEK_SET);
|
||||
got = fread(zBuf, 1, amt, in);
|
||||
got = (int)fread(zBuf, 1, amt, in);
|
||||
fclose(in);
|
||||
if( got<0 ){
|
||||
got = 0;
|
||||
@ -178,7 +178,7 @@ static int hexio_write(
|
||||
return TCL_ERROR;
|
||||
}
|
||||
fseek(out, offset, SEEK_SET);
|
||||
written = fwrite(aOut, 1, nOut, out);
|
||||
written = (int)fwrite(aOut, 1, nOut, out);
|
||||
sqlite3_free(aOut);
|
||||
fclose(out);
|
||||
Tcl_SetObjResult(interp, Tcl_NewIntObj(written));
|
||||
|
@ -290,9 +290,9 @@ static jt_file *locateDatabaseHandle(const char *zJournal){
|
||||
jt_file *pMain = 0;
|
||||
enterJtMutex();
|
||||
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)
|
||||
&& (strlen(pMain->zName)==nName)
|
||||
&& ((int)strlen(pMain->zName)==nName)
|
||||
&& 0==memcmp(pMain->zName, zJournal, nName)
|
||||
&& (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. */
|
||||
if( rc==SQLITE_OK ){
|
||||
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;
|
||||
if( iOff==PENDING_BYTE ) continue;
|
||||
rc = sqlite3OsRead(pMain->pReal, aData, pMain->nPagesize, iOff);
|
||||
@ -466,7 +466,7 @@ static int readJournalFile(jt_file *p, jt_file *pMain){
|
||||
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. */
|
||||
@ -538,7 +538,7 @@ static int jtWrite(
|
||||
}
|
||||
|
||||
if( p->flags&SQLITE_OPEN_MAIN_DB && p->pWritable ){
|
||||
if( iAmt<p->nPagesize
|
||||
if( iAmt<(int)p->nPagesize
|
||||
&& p->nPagesize%iAmt==0
|
||||
&& iOfst>=(PENDING_BYTE+512)
|
||||
&& iOfst+iAmt<=PENDING_BYTE+p->nPagesize
|
||||
@ -549,7 +549,7 @@ static int jtWrite(
|
||||
** pending-byte page.
|
||||
*/
|
||||
}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( pgno<=p->nPage || p->nSync>0 );
|
||||
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 ){
|
||||
u32 pgno;
|
||||
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) );
|
||||
}
|
||||
}
|
||||
@ -723,7 +723,7 @@ static int jtOpen(
|
||||
** returning.
|
||||
*/
|
||||
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]) ){
|
||||
/* Deleting a journal file. The end of a transaction. */
|
||||
jt_file *pMain = locateDatabaseHandle(zPath);
|
||||
|
@ -713,14 +713,14 @@ static int test_memdebug_settitle(
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
const char *zTitle;
|
||||
if( objc!=2 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "TITLE");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
zTitle = Tcl_GetString(objv[1]);
|
||||
#ifdef SQLITE_MEMDEBUG
|
||||
{
|
||||
const char *zTitle;
|
||||
zTitle = Tcl_GetString(objv[1]);
|
||||
extern int sqlite3MemdebugSettitle(const char*);
|
||||
sqlite3MemdebugSettitle(zTitle);
|
||||
}
|
||||
@ -1033,7 +1033,6 @@ static int test_config_lookaside(
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
int rc;
|
||||
int sz, cnt;
|
||||
Tcl_Obj *pRet;
|
||||
if( objc!=3 ){
|
||||
@ -1049,7 +1048,7 @@ static int test_config_lookaside(
|
||||
Tcl_ListObjAppendElement(
|
||||
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);
|
||||
return TCL_OK;
|
||||
}
|
||||
@ -1106,7 +1105,6 @@ static int test_config_heap(
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
static char *zBuf; /* Use this memory */
|
||||
static int szBuf; /* Bytes allocated for zBuf */
|
||||
int nByte; /* Size of buffer to pass to sqlite3_config() */
|
||||
int nMinAlloc; /* Size of minimum allocation */
|
||||
int rc; /* Return code of sqlite3_config() */
|
||||
@ -1124,11 +1122,9 @@ static int test_config_heap(
|
||||
if( nByte==0 ){
|
||||
free( zBuf );
|
||||
zBuf = 0;
|
||||
szBuf = 0;
|
||||
rc = sqlite3_config(SQLITE_CONFIG_HEAP, (void*)0, 0, 0);
|
||||
}else{
|
||||
zBuf = realloc(zBuf, nByte);
|
||||
szBuf = nByte;
|
||||
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_FULL", SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL },
|
||||
{ "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;
|
||||
if( objc!=4 ){
|
||||
|
@ -81,8 +81,12 @@
|
||||
#define sqlite3_mutex_notheld(X) ((void)(X),1)
|
||||
#endif /* SQLITE_THREADSAFE==0 */
|
||||
|
||||
/* Maximum chunk number */
|
||||
#define MX_CHUNK_NUMBER 299
|
||||
|
||||
/* First chunk for rollback journal files */
|
||||
#define SQLITE_MULTIPLEX_JOURNAL_8_3_OFFSET 400
|
||||
#define SQLITE_MULTIPLEX_WAL_8_3_OFFSET 700
|
||||
|
||||
|
||||
/************************ Shim Definitions ******************************/
|
||||
@ -251,17 +255,22 @@ static void multiplexFilename(
|
||||
){
|
||||
int n = nBase;
|
||||
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
|
||||
int i;
|
||||
for(i=n-1; i>0 && i>=n-4 && zOut[i]!='.'; i--){}
|
||||
if( i>=n-4 ) n = i+1;
|
||||
if( flags & SQLITE_OPEN_MAIN_JOURNAL ){
|
||||
/* The extensions on overflow files for main databases are 001, 002,
|
||||
** 003 and so forth. To avoid name collisions, add 400 to the
|
||||
** extensions of journal files so that they are 401, 402, 403, ....
|
||||
*/
|
||||
** 003 and so forth. To avoid name collisions, add 400 to the
|
||||
** extensions of journal files so that they are 401, 402, 403, ....
|
||||
*/
|
||||
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
|
||||
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
|
||||
** to open chunk 401 indicates the database is full. */
|
||||
if( iChunk>=SQLITE_MULTIPLEX_JOURNAL_8_3_OFFSET ){
|
||||
sqlite3_log(SQLITE_FULL, "multiplexed chunk overflow: %s", pGroup->zName);
|
||||
*rc = SQLITE_FULL;
|
||||
return 0;
|
||||
}
|
||||
@ -338,7 +348,13 @@ static sqlite3_file *multiplexSubOpen(
|
||||
}else{
|
||||
*rc = pOrigVfs->xAccess(pOrigVfs, pGroup->aReal[iChunk].z,
|
||||
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;
|
||||
}
|
||||
pSubOpen = sqlite3_malloc( pOrigVfs->szOsFile );
|
||||
@ -350,6 +366,8 @@ static sqlite3_file *multiplexSubOpen(
|
||||
*rc = pOrigVfs->xOpen(pOrigVfs, pGroup->aReal[iChunk].z, pSubOpen,
|
||||
flags, pOutFlags);
|
||||
if( (*rc)!=SQLITE_OK ){
|
||||
sqlite3_log(*rc, "multiplexor.xOpen failure on %s",
|
||||
pGroup->aReal[iChunk].z);
|
||||
sqlite3_free(pSubOpen);
|
||||
pGroup->aReal[iChunk].p = 0;
|
||||
return 0;
|
||||
@ -520,7 +538,7 @@ static int multiplexOpen(
|
||||
pGroup->bEnabled = -1;
|
||||
pGroup->bTruncate = sqlite3_uri_boolean(zUri, "truncate",
|
||||
(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);
|
||||
pGroup->szChunk = (pGroup->szChunk+0xffff)&~0xffff;
|
||||
if( zName ){
|
||||
@ -588,7 +606,7 @@ static int multiplexOpen(
|
||||
bExists = multiplexSubSize(pGroup, 1, &rc)>0;
|
||||
if( rc==SQLITE_OK && bExists && sz==(sz&0xffff0000) && sz>0
|
||||
&& sz!=pGroup->szChunk ){
|
||||
pGroup->szChunk = sz;
|
||||
pGroup->szChunk = (int)sz;
|
||||
}else if( rc==SQLITE_OK && !bExists && sz>pGroup->szChunk ){
|
||||
pGroup->bEnabled = 0;
|
||||
}
|
||||
@ -632,7 +650,7 @@ static int multiplexDelete(
|
||||
/* If the main chunk was deleted successfully, also delete any subsequent
|
||||
** chunks - starting with the last (highest numbered).
|
||||
*/
|
||||
int nName = strlen(zName);
|
||||
int nName = (int)strlen(zName);
|
||||
char *z;
|
||||
z = sqlite3_malloc(nName + 5);
|
||||
if( z==0 ){
|
||||
@ -648,6 +666,17 @@ static int multiplexDelete(
|
||||
multiplexFilename(zName, nName, SQLITE_OPEN_MAIN_JOURNAL, --iChunk, z);
|
||||
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);
|
||||
}
|
||||
|
@ -288,7 +288,7 @@ static int tmpWrite(
|
||||
){
|
||||
tmp_file *pTmp = (tmp_file *)pFile;
|
||||
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);
|
||||
if( !zNew ){
|
||||
return SQLITE_NOMEM;
|
||||
@ -297,7 +297,7 @@ static int tmpWrite(
|
||||
pTmp->nAlloc = nNew;
|
||||
}
|
||||
memcpy(&pTmp->zAlloc[iOfst], zBuf, iAmt);
|
||||
pTmp->nSize = MAX(pTmp->nSize, iOfst+iAmt);
|
||||
pTmp->nSize = (int)MAX(pTmp->nSize, iOfst+iAmt);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
@ -306,7 +306,7 @@ static int tmpWrite(
|
||||
*/
|
||||
static int tmpTruncate(sqlite3_file *pFile, sqlite_int64 size){
|
||||
tmp_file *pTmp = (tmp_file *)pFile;
|
||||
pTmp->nSize = MIN(pTmp->nSize, size);
|
||||
pTmp->nSize = (int)MIN(pTmp->nSize, size);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
@ -418,7 +418,7 @@ static int fsRead(
|
||||
/* Journal file. */
|
||||
int iRem = iAmt;
|
||||
int iBuf = 0;
|
||||
int ii = iOfst;
|
||||
int ii = (int)iOfst;
|
||||
while( iRem>0 && rc==SQLITE_OK ){
|
||||
int iRealOff = pReal->nBlob - BLOCKSIZE*((ii/BLOCKSIZE)+1) + ii%BLOCKSIZE;
|
||||
int iRealAmt = MIN(iRem, BLOCKSIZE - (iRealOff%BLOCKSIZE));
|
||||
@ -453,14 +453,14 @@ static int fsWrite(
|
||||
}else{
|
||||
rc = pF->pMethods->xWrite(pF, zBuf, iAmt, iOfst+BLOCKSIZE);
|
||||
if( rc==SQLITE_OK ){
|
||||
pReal->nDatabase = MAX(pReal->nDatabase, iAmt+iOfst);
|
||||
pReal->nDatabase = (int)MAX(pReal->nDatabase, iAmt+iOfst);
|
||||
}
|
||||
}
|
||||
}else{
|
||||
/* Journal file. */
|
||||
int iRem = iAmt;
|
||||
int iBuf = 0;
|
||||
int ii = iOfst;
|
||||
int ii = (int)iOfst;
|
||||
while( iRem>0 && rc==SQLITE_OK ){
|
||||
int iRealOff = pReal->nBlob - BLOCKSIZE*((ii/BLOCKSIZE)+1) + ii%BLOCKSIZE;
|
||||
int iRealAmt = MIN(iRem, BLOCKSIZE - (iRealOff%BLOCKSIZE));
|
||||
@ -475,7 +475,7 @@ static int fsWrite(
|
||||
}
|
||||
}
|
||||
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_real_file *pReal = p->pReal;
|
||||
if( p->eType==DATABASE_FILE ){
|
||||
pReal->nDatabase = MIN(pReal->nDatabase, size);
|
||||
pReal->nDatabase = (int)MIN(pReal->nDatabase, size);
|
||||
}else{
|
||||
pReal->nJournal = MIN(pReal->nJournal, size);
|
||||
pReal->nJournal = (int)MIN(pReal->nJournal, size);
|
||||
}
|
||||
return SQLITE_OK;
|
||||
}
|
||||
@ -606,7 +606,7 @@ static int fsOpen(
|
||||
p->eType = eType;
|
||||
|
||||
assert(strlen("-journal")==8);
|
||||
nName = strlen(zName)-((eType==JOURNAL_FILE)?8:0);
|
||||
nName = (int)strlen(zName)-((eType==JOURNAL_FILE)?8:0);
|
||||
pReal=pFsVfs->pFileList;
|
||||
for(; pReal && strncmp(pReal->zName, zName, nName); pReal=pReal->pNext);
|
||||
|
||||
@ -641,7 +641,7 @@ static int fsOpen(
|
||||
pReal->nBlob = BLOBSIZE;
|
||||
}else{
|
||||
unsigned char zS[4];
|
||||
pReal->nBlob = size;
|
||||
pReal->nBlob = (int)size;
|
||||
rc = pRealFile->pMethods->xRead(pRealFile, zS, 4, 0);
|
||||
pReal->nDatabase = (zS[0]<<24)+(zS[1]<<16)+(zS[2]<<8)+zS[3];
|
||||
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_real_file *pReal;
|
||||
sqlite3_file *pF;
|
||||
int nName = strlen(zPath) - 8;
|
||||
int nName = (int)strlen(zPath) - 8;
|
||||
|
||||
assert(strlen("-journal")==8);
|
||||
assert(strcmp("-journal", &zPath[nName])==0);
|
||||
@ -717,7 +717,7 @@ static int fsAccess(
|
||||
fs_vfs_t *pFsVfs = (fs_vfs_t *)pVfs;
|
||||
fs_real_file *pReal;
|
||||
int isJournal = 0;
|
||||
int nName = strlen(zPath);
|
||||
int nName = (int)strlen(zPath);
|
||||
|
||||
if( flags!=SQLITE_ACCESS_EXISTS ){
|
||||
sqlite3_vfs *pParent = ((fs_vfs_t *)pVfs)->pParent;
|
||||
|
@ -242,7 +242,7 @@ static sqlite3_uint64 vfslog_time(){
|
||||
}
|
||||
#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 *);
|
||||
|
||||
/*
|
||||
@ -648,7 +648,7 @@ static void vfslog_call(
|
||||
sqlite3_vfs *pVfs,
|
||||
int eEvent,
|
||||
int iFileid,
|
||||
int nClick,
|
||||
sqlite3_int64 nClick,
|
||||
int return_code,
|
||||
int size,
|
||||
int offset
|
||||
@ -661,7 +661,7 @@ static void vfslog_call(
|
||||
zRec = (unsigned char *)&p->aBuf[p->nBuf];
|
||||
put32bits(&zRec[0], eEvent);
|
||||
put32bits(&zRec[4], iFileid);
|
||||
put32bits(&zRec[8], nClick);
|
||||
put32bits(&zRec[8], (unsigned int)(nClick&0xffff));
|
||||
put32bits(&zRec[12], return_code);
|
||||
put32bits(&zRec[16], size);
|
||||
put32bits(&zRec[20], offset);
|
||||
@ -671,7 +671,7 @@ static void vfslog_call(
|
||||
static void vfslog_string(sqlite3_vfs *pVfs, const char *zStr){
|
||||
VfslogVfs *p = (VfslogVfs *)pVfs;
|
||||
unsigned char *zRec;
|
||||
int nStr = zStr ? strlen(zStr) : 0;
|
||||
int nStr = zStr ? (int)strlen(zStr) : 0;
|
||||
if( (4+nStr+p->nBuf)>sizeof(p->aBuf) ){
|
||||
vfslog_flush(p);
|
||||
}
|
||||
@ -720,7 +720,7 @@ int sqlite3_vfslog_new(
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
|
||||
nVfs = strlen(zVfs);
|
||||
nVfs = (int)strlen(zVfs);
|
||||
nByte = sizeof(VfslogVfs) + pParent->szOsFile + nVfs+1+pParent->mxPathname+1;
|
||||
p = (VfslogVfs *)sqlite3_malloc(nByte);
|
||||
memset(p, 0, nByte);
|
||||
@ -1043,7 +1043,7 @@ static int vlogColumn(
|
||||
}
|
||||
case 1: {
|
||||
char *zStr = pCsr->zTransient;
|
||||
if( val!=0 && val<pCsr->nFile ){
|
||||
if( val!=0 && val<(unsigned)pCsr->nFile ){
|
||||
zStr = pCsr->azFile[val];
|
||||
}
|
||||
sqlite3_result_text(ctx, zStr, -1, SQLITE_TRANSIENT);
|
||||
|
248
src/test_quota.c
248
src/test_quota.c
@ -120,6 +120,9 @@ struct quota_FILE {
|
||||
FILE *f; /* Open stdio file pointer */
|
||||
sqlite3_int64 iOfst; /* Current offset into the file */
|
||||
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;
|
||||
}
|
||||
if( pFile==0 && createFlag ){
|
||||
int nName = strlen(zName);
|
||||
int nName = (int)(strlen(zName) & 0x3fffffff);
|
||||
pFile = (quotaFile *)sqlite3_malloc( sizeof(*pFile) + nName + 1 );
|
||||
if( pFile ){
|
||||
memset(pFile, 0, sizeof(*pFile));
|
||||
@ -419,7 +422,7 @@ static quotaFile *quotaFindFile(
|
||||
*/
|
||||
static char *quota_utf8_to_mbcs(const char *zUtf8){
|
||||
#if SQLITE_OS_WIN
|
||||
int n; /* Bytes in zUtf8 */
|
||||
size_t n; /* Bytes in zUtf8 */
|
||||
int nWide; /* number of UTF-16 characters */
|
||||
int nMbcs; /* Bytes of MBCS */
|
||||
LPWSTR zTmpWide; /* The UTF16 text */
|
||||
@ -897,7 +900,7 @@ int sqlite3_quota_set(
|
||||
pGroup = pGroup->pNext;
|
||||
}
|
||||
if( pGroup==0 ){
|
||||
int nPattern = strlen(zPattern);
|
||||
int nPattern = (int)(strlen(zPattern) & 0x3fffffff);
|
||||
if( iLimit<=0 ){
|
||||
quotaLeave();
|
||||
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 *p = 0;
|
||||
char *zFull = 0;
|
||||
char *zFullTranslated;
|
||||
char *zFullTranslated = 0;
|
||||
int rc;
|
||||
quotaGroup *pGroup;
|
||||
quotaFile *pFile;
|
||||
@ -995,7 +998,6 @@ quota_FILE *sqlite3_quota_fopen(const char *zFilename, const char *zMode){
|
||||
zFullTranslated = quota_utf8_to_mbcs(zFull);
|
||||
if( zFullTranslated==0 ) goto quota_fopen_error;
|
||||
p->f = fopen(zFullTranslated, zMode);
|
||||
quota_mbcs_free(zFullTranslated);
|
||||
if( p->f==0 ) goto quota_fopen_error;
|
||||
quotaEnter();
|
||||
pGroup = quotaGroupFind(zFull);
|
||||
@ -1010,9 +1012,13 @@ quota_FILE *sqlite3_quota_fopen(const char *zFilename, const char *zMode){
|
||||
}
|
||||
quotaLeave();
|
||||
sqlite3_free(zFull);
|
||||
#if SQLITE_OS_WIN
|
||||
p->zMbcsName = zFullTranslated;
|
||||
#endif
|
||||
return p;
|
||||
|
||||
quota_fopen_error:
|
||||
quota_mbcs_free(zFullTranslated);
|
||||
sqlite3_free(zFull);
|
||||
if( p && p->f ) fclose(p->f);
|
||||
sqlite3_free(p);
|
||||
@ -1045,6 +1051,7 @@ size_t sqlite3_quota_fwrite(
|
||||
sqlite3_int64 iEnd;
|
||||
sqlite3_int64 szNew;
|
||||
quotaFile *pFile;
|
||||
size_t rc;
|
||||
|
||||
iOfst = ftell(p->f);
|
||||
iEnd = iOfst + size*nmemb;
|
||||
@ -1060,7 +1067,7 @@ size_t sqlite3_quota_fwrite(
|
||||
}
|
||||
if( szNew>pGroup->iLimit && pGroup->iLimit>0 ){
|
||||
iEnd = pGroup->iLimit - pGroup->iSize + pFile->iSize;
|
||||
nmemb = (iEnd - iOfst)/size;
|
||||
nmemb = (size_t)((iEnd - iOfst)/size);
|
||||
iEnd = iOfst + size*nmemb;
|
||||
szNew = pGroup->iSize - pFile->iSize + iEnd;
|
||||
}
|
||||
@ -1068,8 +1075,23 @@ size_t sqlite3_quota_fwrite(
|
||||
pGroup->iSize = szNew;
|
||||
pFile->iSize = iEnd;
|
||||
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();
|
||||
}
|
||||
#if SQLITE_OS_WIN
|
||||
quota_mbcs_free(p->zMbcsName);
|
||||
#endif
|
||||
sqlite3_free(p);
|
||||
return rc;
|
||||
}
|
||||
@ -1135,12 +1160,89 @@ long sqlite3_quota_ftell(quota_FILE *p){
|
||||
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.
|
||||
*/
|
||||
int sqlite3_quota_remove(const char *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 */
|
||||
quotaGroup *pGroup; /* Group containing zFilename */
|
||||
quotaFile *pFile; /* A file in the group */
|
||||
@ -1480,7 +1582,7 @@ static int test_quota_fread(
|
||||
char *zBuf;
|
||||
int sz;
|
||||
int nElem;
|
||||
int got;
|
||||
size_t got;
|
||||
|
||||
if( objc!=4 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "HANDLE SIZE NELEM");
|
||||
@ -1515,7 +1617,7 @@ static int test_quota_fwrite(
|
||||
char *zBuf;
|
||||
int sz;
|
||||
int nElem;
|
||||
int got;
|
||||
size_t got;
|
||||
|
||||
if( objc!=5 ){
|
||||
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;
|
||||
zBuf = Tcl_GetString(objv[4]);
|
||||
got = sqlite3_quota_fwrite(zBuf, sz, nElem, p);
|
||||
Tcl_SetObjResult(interp, Tcl_NewIntObj(got));
|
||||
Tcl_SetObjResult(interp, Tcl_NewWideIntObj(got));
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
@ -1656,6 +1758,96 @@ static int test_quota_ftell(
|
||||
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
|
||||
*/
|
||||
@ -1713,21 +1905,25 @@ int Sqlitequota_Init(Tcl_Interp *interp){
|
||||
char *zName;
|
||||
Tcl_ObjCmdProc *xProc;
|
||||
} aCmd[] = {
|
||||
{ "sqlite3_quota_initialize", test_quota_initialize },
|
||||
{ "sqlite3_quota_shutdown", test_quota_shutdown },
|
||||
{ "sqlite3_quota_set", test_quota_set },
|
||||
{ "sqlite3_quota_file", test_quota_file },
|
||||
{ "sqlite3_quota_dump", test_quota_dump },
|
||||
{ "sqlite3_quota_fopen", test_quota_fopen },
|
||||
{ "sqlite3_quota_fread", test_quota_fread },
|
||||
{ "sqlite3_quota_fwrite", test_quota_fwrite },
|
||||
{ "sqlite3_quota_fclose", test_quota_fclose },
|
||||
{ "sqlite3_quota_fflush", test_quota_fflush },
|
||||
{ "sqlite3_quota_fseek", test_quota_fseek },
|
||||
{ "sqlite3_quota_rewind", test_quota_rewind },
|
||||
{ "sqlite3_quota_ftell", test_quota_ftell },
|
||||
{ "sqlite3_quota_remove", test_quota_remove },
|
||||
{ "sqlite3_quota_glob", test_quota_glob },
|
||||
{ "sqlite3_quota_initialize", test_quota_initialize },
|
||||
{ "sqlite3_quota_shutdown", test_quota_shutdown },
|
||||
{ "sqlite3_quota_set", test_quota_set },
|
||||
{ "sqlite3_quota_file", test_quota_file },
|
||||
{ "sqlite3_quota_dump", test_quota_dump },
|
||||
{ "sqlite3_quota_fopen", test_quota_fopen },
|
||||
{ "sqlite3_quota_fread", test_quota_fread },
|
||||
{ "sqlite3_quota_fwrite", test_quota_fwrite },
|
||||
{ "sqlite3_quota_fclose", test_quota_fclose },
|
||||
{ "sqlite3_quota_fflush", test_quota_fflush },
|
||||
{ "sqlite3_quota_fseek", test_quota_fseek },
|
||||
{ "sqlite3_quota_rewind", test_quota_rewind },
|
||||
{ "sqlite3_quota_ftell", test_quota_ftell },
|
||||
{ "sqlite3_quota_ftruncate", test_quota_ftruncate },
|
||||
{ "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;
|
||||
|
||||
|
@ -29,6 +29,14 @@
|
||||
#ifndef _QUOTA_H_
|
||||
#include "sqlite3.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++ */
|
||||
#ifdef __cplusplus
|
||||
@ -182,6 +190,48 @@ int sqlite3_quota_fseek(quota_FILE*, long, int);
|
||||
void sqlite3_quota_rewind(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.
|
||||
** Adjust quotas accordingly.
|
||||
|
@ -49,7 +49,11 @@ static void circle_del(void *p){
|
||||
static int circle_geom(
|
||||
sqlite3_rtree_geometry *p,
|
||||
int nCoord,
|
||||
#ifdef SQLITE_RTREE_INT_ONLY
|
||||
sqlite3_int64 *aCoord,
|
||||
#else
|
||||
double *aCoord,
|
||||
#endif
|
||||
int *pRes
|
||||
){
|
||||
int i; /* Iterator variable */
|
||||
@ -188,8 +192,12 @@ static int gHere = 42;
|
||||
*/
|
||||
static int cube_geom(
|
||||
sqlite3_rtree_geometry *p,
|
||||
int nCoord,
|
||||
int nCoord,
|
||||
#ifdef SQLITE_RTREE_INT_ONLY
|
||||
sqlite3_int64 *aCoord,
|
||||
#else
|
||||
double *aCoord,
|
||||
#endif
|
||||
int *piRes
|
||||
){
|
||||
Cube *pCube = (Cube *)p->pUser;
|
||||
|
1951
src/test_spellfix.c
Normal file
1951
src/test_spellfix.c
Normal file
File diff suppressed because it is too large
Load Diff
@ -324,12 +324,13 @@ static int statDecodePage(Btree *pBt, StatPage *p){
|
||||
u64 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);
|
||||
pCell->nLocal = nLocal;
|
||||
assert( nPayload>=nLocal );
|
||||
assert( nLocal>=0 );
|
||||
assert( nPayload>=(u32)nLocal );
|
||||
assert( nLocal<=(nUsable-35) );
|
||||
if( nPayload>nLocal ){
|
||||
if( nPayload>(u32)nLocal ){
|
||||
int j;
|
||||
int nOvfl = ((nPayload - nLocal) + nUsable-4 - 1) / (nUsable - 4);
|
||||
pCell->nLastOvfl = (nPayload-nLocal) - (nOvfl-1) * (nUsable-4);
|
||||
@ -378,7 +379,7 @@ static void statSizeAndOffset(StatCursor *pCsr){
|
||||
x[0] = pCsr->iPageno;
|
||||
if( sqlite3OsFileControl(fd, 230440, &x)==SQLITE_OK ){
|
||||
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);
|
||||
if( rc==SQLITE_ROW ){
|
||||
int nPage;
|
||||
u32 iRoot = sqlite3_column_int64(pCsr->pStmt, 1);
|
||||
u32 iRoot = (u32)sqlite3_column_int64(pCsr->pStmt, 1);
|
||||
sqlite3PagerPagecount(pPager, &nPage);
|
||||
if( nPage==0 ){
|
||||
pCsr->isEof = 1;
|
||||
|
@ -273,7 +273,6 @@ static int sqlthread_open(
|
||||
|
||||
const char *zFilename;
|
||||
sqlite3 *db;
|
||||
int rc;
|
||||
char zBuf[100];
|
||||
extern void Md5_Register(sqlite3*);
|
||||
|
||||
@ -281,11 +280,12 @@ static int sqlthread_open(
|
||||
UNUSED_PARAMETER(objc);
|
||||
|
||||
zFilename = Tcl_GetString(objv[2]);
|
||||
rc = sqlite3_open(zFilename, &db);
|
||||
sqlite3_open(zFilename, &db);
|
||||
#ifdef SQLITE_HAS_CODEC
|
||||
if( db && objc>=4 ){
|
||||
const char *zKey;
|
||||
int nKey;
|
||||
int rc;
|
||||
zKey = Tcl_GetStringFromObj(objv[3], &nKey);
|
||||
rc = sqlite3_key(db, zKey, nKey);
|
||||
if( rc!=SQLITE_OK ){
|
||||
|
@ -480,6 +480,27 @@ static int tvfsCheckReservedLock(sqlite3_file *pFile, int *pResOut){
|
||||
*/
|
||||
static int tvfsFileControl(sqlite3_file *pFile, int op, void *pArg){
|
||||
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);
|
||||
}
|
||||
|
||||
@ -763,7 +784,7 @@ static int tvfsShmOpen(sqlite3_file *pFile){
|
||||
if( 0==strcmp(pFd->zFilename, pBuffer->zFile) ) break;
|
||||
}
|
||||
if( !pBuffer ){
|
||||
int nByte = sizeof(TestvfsBuffer) + strlen(pFd->zFilename) + 1;
|
||||
int nByte = sizeof(TestvfsBuffer) + (int)strlen(pFd->zFilename) + 1;
|
||||
pBuffer = (TestvfsBuffer *)ckalloc(nByte);
|
||||
memset(pBuffer, 0, nByte);
|
||||
pBuffer->zFile = (char *)&pBuffer[1];
|
||||
@ -845,13 +866,13 @@ static int tvfsShmLock(
|
||||
|
||||
if( p->pScript && p->mask&TESTVFS_SHMLOCK_MASK ){
|
||||
sqlite3_snprintf(sizeof(zLock), zLock, "%d %d", ofst, n);
|
||||
nLock = strlen(zLock);
|
||||
nLock = (int)strlen(zLock);
|
||||
if( flags & SQLITE_SHM_LOCK ){
|
||||
strcpy(&zLock[nLock], " lock");
|
||||
}else{
|
||||
strcpy(&zLock[nLock], " unlock");
|
||||
}
|
||||
nLock += strlen(&zLock[nLock]);
|
||||
nLock += (int)strlen(&zLock[nLock]);
|
||||
if( flags & SQLITE_SHM_SHARED ){
|
||||
strcpy(&zLock[nLock], " shared");
|
||||
}else{
|
||||
@ -1375,7 +1396,7 @@ static int testvfs_cmd(
|
||||
}
|
||||
|
||||
zVfs = Tcl_GetString(objv[1]);
|
||||
nByte = sizeof(Testvfs) + strlen(zVfs)+1;
|
||||
nByte = sizeof(Testvfs) + (int)strlen(zVfs)+1;
|
||||
p = (Testvfs *)ckalloc(nByte);
|
||||
memset(p, 0, nByte);
|
||||
p->iDevchar = -1;
|
||||
|
@ -476,6 +476,12 @@ static int vfstraceFileControl(sqlite3_file *pFile, int op, void *pArg){
|
||||
case SQLITE_FCNTL_OVERWRITE: zOp = "OVERWRITE"; break;
|
||||
case SQLITE_FCNTL_VFSNAME: zOp = "VFSNAME"; 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: {
|
||||
sqlite3_snprintf(sizeof zBuf, zBuf, "%d", op);
|
||||
zOp = zBuf;
|
||||
@ -490,6 +496,10 @@ static int vfstraceFileControl(sqlite3_file *pFile, int op, void *pArg){
|
||||
*(char**)pArg = sqlite3_mprintf("vfstrace.%s/%z",
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -33,8 +33,8 @@
|
||||
typedef struct wholenumber_cursor wholenumber_cursor;
|
||||
struct wholenumber_cursor {
|
||||
sqlite3_vtab_cursor base; /* Base class - must be first */
|
||||
unsigned iValue; /* Current value */
|
||||
unsigned mxValue; /* Maximum value */
|
||||
sqlite3_int64 iValue; /* Current value */
|
||||
sqlite3_int64 mxValue; /* Maximum value */
|
||||
};
|
||||
|
||||
/* Methods for the wholenumber module */
|
||||
|
12
src/util.c
12
src/util.c
@ -216,13 +216,13 @@ int sqlite3Dequote(char *z){
|
||||
** Some systems have stricmp(). Others have strcasecmp(). Because
|
||||
** there is no consistency, we will define our own.
|
||||
**
|
||||
** IMPLEMENTATION-OF: R-20522-24639 The sqlite3_strnicmp() API allows
|
||||
** applications and extensions to compare the contents of two buffers
|
||||
** containing UTF-8 strings in a case-independent fashion, using the same
|
||||
** definition of case independence that SQLite uses internally when
|
||||
** comparing identifiers.
|
||||
** IMPLEMENTATION-OF: R-30243-02494 The sqlite3_stricmp() and
|
||||
** sqlite3_strnicmp() APIs allow applications and extensions to compare
|
||||
** the contents of two buffers containing UTF-8 strings in a
|
||||
** case-independent fashion, using the same definition of "case
|
||||
** 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;
|
||||
a = (unsigned char *)zLeft;
|
||||
b = (unsigned char *)zRight;
|
||||
|
20
src/vacuum.c
20
src/vacuum.c
@ -176,6 +176,18 @@ int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db){
|
||||
}
|
||||
#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 */
|
||||
if( sqlite3PagerGetJournalMode(sqlite3BtreePager(pMain))
|
||||
==PAGER_JOURNALMODE_WAL ){
|
||||
@ -189,20 +201,12 @@ int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db){
|
||||
rc = SQLITE_NOMEM;
|
||||
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
|
||||
sqlite3BtreeSetAutoVacuum(pTemp, db->nextAutovac>=0 ? db->nextAutovac :
|
||||
sqlite3BtreeGetAutoVacuum(pMain));
|
||||
#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
|
||||
** in the temporary database.
|
||||
*/
|
||||
|
85
src/vdbe.c
85
src/vdbe.c
@ -1331,19 +1331,26 @@ arithmetic_result_is_null:
|
||||
break;
|
||||
}
|
||||
|
||||
/* Opcode: CollSeq * * P4
|
||||
/* Opcode: CollSeq P1 * * P4
|
||||
**
|
||||
** P4 is a pointer to a CollSeq struct. If the next call to a user function
|
||||
** or aggregate calls sqlite3GetFuncCollSeq(), this collation sequence will
|
||||
** be returned. This is used by the built-in min(), max() and nullif()
|
||||
** 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
|
||||
** to retrieve the collation sequence set by this opcode is not available
|
||||
** publicly, only to user functions defined in func.c.
|
||||
*/
|
||||
case OP_CollSeq: {
|
||||
assert( pOp->p4type==P4_COLLSEQ );
|
||||
if( pOp->p1 ){
|
||||
sqlite3VdbeMemSetInt64(&aMem[pOp->p1], 0);
|
||||
}
|
||||
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.
|
||||
** The first OP_Column against a pseudo-table after the value of the content
|
||||
** 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: {
|
||||
u32 payloadSize; /* Number of bytes in the record */
|
||||
@ -2260,7 +2272,7 @@ case OP_Column: {
|
||||
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.
|
||||
** assert( zRec!=0 || avail>=payloadSize || avail>=9 ); */
|
||||
szHdr = getVarint32((u8*)zData, offset);
|
||||
@ -2335,11 +2347,11 @@ case OP_Column: {
|
||||
break;
|
||||
}
|
||||
}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
|
||||
** table. Set the offset for any extra columns not present in
|
||||
** the record to 0. This tells code below to store a NULL
|
||||
** instead of deserializing a value from the record.
|
||||
** the record to 0. This tells code below to store the default value
|
||||
** for the column instead of deserializing a value from the record.
|
||||
*/
|
||||
aOffset[i] = 0;
|
||||
}
|
||||
@ -2369,17 +2381,32 @@ case OP_Column: {
|
||||
if( aOffset[p2] ){
|
||||
assert( rc==SQLITE_OK );
|
||||
if( zRec ){
|
||||
/* This is the common case where the whole row fits on a single page */
|
||||
VdbeMemRelease(pDest);
|
||||
sqlite3VdbeSerialGet((u8 *)&zRec[aOffset[p2]], aType[p2], pDest);
|
||||
}else{
|
||||
len = sqlite3VdbeSerialTypeLen(aType[p2]);
|
||||
sqlite3VdbeMemMove(&sMem, pDest);
|
||||
rc = sqlite3VdbeMemFromBtree(pCrsr, aOffset[p2], len, pC->isIndex, &sMem);
|
||||
if( rc!=SQLITE_OK ){
|
||||
goto op_column_out;
|
||||
/* This branch happens only when the row overflows onto multiple pages */
|
||||
t = aType[p2];
|
||||
if( (pOp->p5 & (OPFLAG_LENGTHARG|OPFLAG_TYPEOFARG))!=0
|
||||
&& ((t>=12 && (t&1)==0) || (pOp->p5 & OPFLAG_TYPEOFARG)!=0)
|
||||
){
|
||||
/* 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, aType[p2], pDest);
|
||||
sqlite3VdbeSerialGet((u8*)zData, t, pDest);
|
||||
}
|
||||
pDest->enc = encoding;
|
||||
}else{
|
||||
@ -2677,16 +2704,12 @@ case OP_Savepoint: {
|
||||
if( !pSavepoint ){
|
||||
sqlite3SetString(&p->zErrMsg, db, "no such savepoint: %s", zName);
|
||||
rc = SQLITE_ERROR;
|
||||
}else if(
|
||||
db->writeVdbeCnt>0 || (p1==SAVEPOINT_ROLLBACK && db->activeVdbeCnt>1)
|
||||
){
|
||||
}else if( db->writeVdbeCnt>0 && p1==SAVEPOINT_RELEASE ){
|
||||
/* It is not possible to release (commit) a savepoint if there are
|
||||
** active write statements. It is not possible to rollback a savepoint
|
||||
** if there are any active statements at all.
|
||||
** active write statements.
|
||||
*/
|
||||
sqlite3SetString(&p->zErrMsg, db,
|
||||
"cannot %s savepoint - SQL statements in progress",
|
||||
(p1==SAVEPOINT_ROLLBACK ? "rollback": "release")
|
||||
"cannot release savepoint - SQL statements in progress"
|
||||
);
|
||||
rc = SQLITE_BUSY;
|
||||
}else{
|
||||
@ -2711,6 +2734,11 @@ case OP_Savepoint: {
|
||||
rc = p->rc;
|
||||
}else{
|
||||
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++){
|
||||
rc = sqlite3BtreeSavepoint(db->aDb[ii].pBt, p1, iSavepoint);
|
||||
if( rc!=SQLITE_OK ){
|
||||
@ -2779,6 +2807,7 @@ case OP_AutoCommit: {
|
||||
assert( desiredAutoCommit==1 || iRollback==0 );
|
||||
assert( db->activeVdbeCnt>0 ); /* At least this one VM is active */
|
||||
|
||||
#if 0
|
||||
if( turnOnAC && iRollback && db->activeVdbeCnt>1 ){
|
||||
/* If this instruction implements a ROLLBACK and other VMs are
|
||||
** 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 - "
|
||||
"SQL statements in progress");
|
||||
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
|
||||
** return an error indicating that the other VMs must complete first.
|
||||
*/
|
||||
@ -2797,7 +2828,7 @@ case OP_AutoCommit: {
|
||||
}else if( desiredAutoCommit!=db->autoCommit ){
|
||||
if( iRollback ){
|
||||
assert( desiredAutoCommit==1 );
|
||||
sqlite3RollbackAll(db);
|
||||
sqlite3RollbackAll(db, SQLITE_ABORT_ROLLBACK);
|
||||
db->autoCommit = 1;
|
||||
}else if( (rc = sqlite3VdbeCheckFk(p, 1))!=SQLITE_OK ){
|
||||
goto vdbe_return;
|
||||
@ -2853,7 +2884,7 @@ case OP_AutoCommit: {
|
||||
** throw an ABORT exception), a statement transaction may also be opened.
|
||||
** More specifically, a statement transaction is opened iff the database
|
||||
** 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
|
||||
** entire transaction. If no error is encountered, the statement transaction
|
||||
** will automatically commit when the VDBE halts.
|
||||
@ -3835,7 +3866,7 @@ case OP_NewRowid: { /* out2-prerelease */
|
||||
assert( sqlite3BtreeCursorIsValid(pC->pCursor) );
|
||||
rc = sqlite3BtreeKeySize(pC->pCursor, &v);
|
||||
assert( rc==SQLITE_OK ); /* Cannot fail following BtreeLast() */
|
||||
if( v==MAX_ROWID ){
|
||||
if( v>=MAX_ROWID ){
|
||||
pC->useRandomRowid = 1;
|
||||
}else{
|
||||
v++; /* IMP: R-29538-34987 */
|
||||
@ -4833,6 +4864,7 @@ case OP_ParseSchema: {
|
||||
db->init.busy = 0;
|
||||
}
|
||||
}
|
||||
if( rc ) sqlite3ResetInternalSchema(db, -1);
|
||||
if( rc==SQLITE_NOMEM ){
|
||||
goto no_mem;
|
||||
}
|
||||
@ -5167,7 +5199,6 @@ case OP_Program: { /* jump */
|
||||
p->nOp = pProgram->nOp;
|
||||
p->aOnceFlag = (u8 *)&p->apCsr[p->nCursor];
|
||||
p->nOnceFlag = pProgram->nOnce;
|
||||
p->nOp = pProgram->nOp;
|
||||
pc = -1;
|
||||
memset(p->aOnceFlag, 0, p->nOnceFlag);
|
||||
|
||||
@ -5356,6 +5387,7 @@ case OP_AggStep: {
|
||||
ctx.s.db = db;
|
||||
ctx.isError = 0;
|
||||
ctx.pColl = 0;
|
||||
ctx.skipFlag = 0;
|
||||
if( ctx.pFunc->flags & SQLITE_FUNC_NEEDCOLL ){
|
||||
assert( pOp>p->aOp );
|
||||
assert( pOp[-1].p4type==P4_COLLSEQ );
|
||||
@ -5367,6 +5399,11 @@ case OP_AggStep: {
|
||||
sqlite3SetString(&p->zErrMsg, db, "%s", sqlite3_value_text(&ctx.s));
|
||||
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);
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user