1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-07-24 22:22:08 +03:00

Merge the latest trunk changes into the sessions branch.

FossilOrigin-Name: 6883580e6c8973010a42d1d2c5bde04c6b2f4eb7
This commit is contained in:
drh
2011-05-05 15:46:16 +00:00
33 changed files with 1721 additions and 228 deletions

View File

@ -1 +1 @@
3.7.6.1 3.7.7

18
configure vendored
View File

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

View File

@ -419,6 +419,34 @@ static void fts3GetDeltaVarint(char **pp, sqlite3_int64 *pVal){
*pVal += iVal; *pVal += iVal;
} }
/*
** When this function is called, *pp points to the first byte following a
** varint that is part of a doclist (or position-list, or any other list
** of varints). This function moves *pp to point to the start of that varint,
** and decrements the value stored in *pVal by the varint value.
**
** Argument pStart points to the first byte of the doclist that the
** varint is part of.
*/
static void fts3GetReverseDeltaVarint(
char **pp,
char *pStart,
sqlite3_int64 *pVal
){
sqlite3_int64 iVal;
char *p = *pp;
/* Pointer p now points at the first byte past the varint we are
** interested in. So, unless the doclist is corrupt, the 0x80 bit is
** clear on character p[-1]. */
for(p = (*pp)-2; p>=pStart && *p&0x80; p--);
p++;
*pp = p;
sqlite3Fts3GetVarint(p, &iVal);
*pVal -= iVal;
}
/* /*
** As long as *pp has not reached its end (pEnd), then do the same ** As long as *pp has not reached its end (pEnd), then do the same
** as fts3GetDeltaVarint(): read a single varint and add it to *pVal. ** as fts3GetDeltaVarint(): read a single varint and add it to *pVal.
@ -524,6 +552,8 @@ static void fts3DeclareVtab(int *pRc, Fts3Table *p){
char *zSql; /* SQL statement passed to declare_vtab() */ char *zSql; /* SQL statement passed to declare_vtab() */
char *zCols; /* List of user defined columns */ char *zCols; /* List of user defined columns */
sqlite3_vtab_config(p->db, SQLITE_VTAB_CONSTRAINT_SUPPORT, 1);
/* Create a list of user columns for the virtual table */ /* Create a list of user columns for the virtual table */
zCols = sqlite3_mprintf("%Q, ", p->azColumn[0]); zCols = sqlite3_mprintf("%Q, ", p->azColumn[0]);
for(i=1; zCols && i<p->nColumn; i++){ for(i=1; zCols && i<p->nColumn; i++){
@ -1092,6 +1122,22 @@ static int fts3BestIndexMethod(sqlite3_vtab *pVTab, sqlite3_index_info *pInfo){
pInfo->aConstraintUsage[iCons].argvIndex = 1; pInfo->aConstraintUsage[iCons].argvIndex = 1;
pInfo->aConstraintUsage[iCons].omit = 1; pInfo->aConstraintUsage[iCons].omit = 1;
} }
/* Regardless of the strategy selected, FTS can deliver rows in rowid (or
** docid) order. Both ascending and descending are possible.
*/
if( pInfo->nOrderBy==1 ){
struct sqlite3_index_orderby *pOrder = &pInfo->aOrderBy[0];
if( pOrder->iColumn<0 || pOrder->iColumn==p->nColumn+1 ){
if( pOrder->desc ){
pInfo->idxStr = "DESC";
}else{
pInfo->idxStr = "ASC";
}
}
pInfo->orderByConsumed = 1;
}
return SQLITE_OK; return SQLITE_OK;
} }
@ -2996,12 +3042,20 @@ static int fts3NextMethod(sqlite3_vtab_cursor *pCursor){
} }
pCsr->iPrevId = sqlite3_column_int64(pCsr->pStmt, 0); pCsr->iPrevId = sqlite3_column_int64(pCsr->pStmt, 0);
}else{ }else{
if( pCsr->pNextId>=&pCsr->aDoclist[pCsr->nDoclist] ){ if( pCsr->desc==0 ){
pCsr->isEof = 1; if( pCsr->pNextId>=&pCsr->aDoclist[pCsr->nDoclist] ){
break; pCsr->isEof = 1;
break;
}
fts3GetDeltaVarint(&pCsr->pNextId, &pCsr->iPrevId);
}else{
fts3GetReverseDeltaVarint(&pCsr->pNextId,pCsr->aDoclist,&pCsr->iPrevId);
if( pCsr->pNextId<=pCsr->aDoclist ){
pCsr->isEof = 1;
break;
}
} }
sqlite3_reset(pCsr->pStmt); sqlite3_reset(pCsr->pStmt);
fts3GetDeltaVarint(&pCsr->pNextId, &pCsr->iPrevId);
pCsr->isRequireSeek = 1; pCsr->isRequireSeek = 1;
pCsr->isMatchinfoNeeded = 1; pCsr->isMatchinfoNeeded = 1;
} }
@ -3034,8 +3088,8 @@ static int fts3FilterMethod(
sqlite3_value **apVal /* Arguments for the indexing scheme */ sqlite3_value **apVal /* Arguments for the indexing scheme */
){ ){
const char *azSql[] = { const char *azSql[] = {
"SELECT %s FROM %Q.'%q_content' AS x WHERE docid = ?", /* non-full-scan */ "SELECT %s FROM %Q.'%q_content' AS x WHERE docid = ?", /* non-full-scan */
"SELECT %s FROM %Q.'%q_content' AS x ", /* full-scan */ "SELECT %s FROM %Q.'%q_content' AS x ORDER BY docid %s", /* full-scan */
}; };
int rc; /* Return code */ int rc; /* Return code */
char *zSql; /* SQL statement used to access %_content */ char *zSql; /* SQL statement used to access %_content */
@ -3091,7 +3145,9 @@ static int fts3FilterMethod(
** row by docid. ** row by docid.
*/ */
zSql = (char *)azSql[idxNum==FTS3_FULLSCAN_SEARCH]; zSql = (char *)azSql[idxNum==FTS3_FULLSCAN_SEARCH];
zSql = sqlite3_mprintf(zSql, p->zReadExprlist, p->zDb, p->zName); zSql = sqlite3_mprintf(
zSql, p->zReadExprlist, p->zDb, p->zName, (idxStr ? idxStr : "ASC")
);
if( !zSql ){ if( !zSql ){
rc = SQLITE_NOMEM; rc = SQLITE_NOMEM;
}else{ }else{
@ -3103,7 +3159,22 @@ static int fts3FilterMethod(
} }
pCsr->eSearch = (i16)idxNum; pCsr->eSearch = (i16)idxNum;
assert( pCsr->desc==0 );
if( rc!=SQLITE_OK ) return rc; if( rc!=SQLITE_OK ) return rc;
if( rc==SQLITE_OK && pCsr->nDoclist>0 && idxStr && idxStr[0]=='D' ){
sqlite3_int64 iDocid = 0;
char *csr = pCsr->aDoclist;
while( csr<&pCsr->aDoclist[pCsr->nDoclist] ){
fts3GetDeltaVarint(&csr, &iDocid);
}
pCsr->pNextId = csr;
pCsr->iPrevId = iDocid;
pCsr->desc = 1;
pCsr->isRequireSeek = 1;
pCsr->isMatchinfoNeeded = 1;
pCsr->eEvalmode = FTS3_EVAL_NEXT;
return SQLITE_OK;
}
return fts3NextMethod(pCursor); return fts3NextMethod(pCursor);
} }
@ -3256,12 +3327,32 @@ int sqlite3Fts3ExprLoadFtDoclist(
return rc; return rc;
} }
/*
** When called, *ppPoslist must point to the byte immediately following the
** end of a position-list. i.e. ( (*ppPoslist)[-1]==POS_END ). This function
** moves *ppPoslist so that it instead points to the first byte of the
** same position list.
*/
static void fts3ReversePoslist(char *pStart, char **ppPoslist){
char *p = &(*ppPoslist)[-3];
char c = p[1];
while( p>pStart && (*p & 0x80) | c ){
c = *p--;
}
if( p>pStart ){ p = &p[2]; }
while( *p++&0x80 );
*ppPoslist = p;
}
/* /*
** After ExprLoadDoclist() (see above) has been called, this function is ** After ExprLoadDoclist() (see above) has been called, this function is
** used to iterate/search through the position lists that make up the doclist ** used to iterate/search through the position lists that make up the doclist
** stored in pExpr->aDoclist. ** stored in pExpr->aDoclist.
*/ */
char *sqlite3Fts3FindPositions( char *sqlite3Fts3FindPositions(
Fts3Cursor *pCursor, /* Associate FTS3 cursor */
Fts3Expr *pExpr, /* Access this expressions doclist */ Fts3Expr *pExpr, /* Access this expressions doclist */
sqlite3_int64 iDocid, /* Docid associated with requested pos-list */ sqlite3_int64 iDocid, /* Docid associated with requested pos-list */
int iCol /* Column of requested pos-list */ int iCol /* Column of requested pos-list */
@ -3272,20 +3363,36 @@ char *sqlite3Fts3FindPositions(
char *pCsr; char *pCsr;
if( pExpr->pCurrent==0 ){ if( pExpr->pCurrent==0 ){
pExpr->pCurrent = pExpr->aDoclist; if( pCursor->desc==0 ){
pExpr->iCurrent = 0; pExpr->pCurrent = pExpr->aDoclist;
pExpr->pCurrent += sqlite3Fts3GetVarint(pExpr->pCurrent,&pExpr->iCurrent); pExpr->iCurrent = 0;
fts3GetDeltaVarint(&pExpr->pCurrent, &pExpr->iCurrent);
}else{
pCsr = pExpr->aDoclist;
while( pCsr<pEnd ){
fts3GetDeltaVarint(&pCsr, &pExpr->iCurrent);
fts3PoslistCopy(0, &pCsr);
}
fts3ReversePoslist(pExpr->aDoclist, &pCsr);
pExpr->pCurrent = pCsr;
}
} }
pCsr = pExpr->pCurrent; pCsr = pExpr->pCurrent;
assert( pCsr ); assert( pCsr );
while( pCsr<pEnd ){ while( (pCursor->desc==0 && pCsr<pEnd)
if( pExpr->iCurrent<iDocid ){ || (pCursor->desc && pCsr>pExpr->aDoclist)
){
if( pCursor->desc==0 && pExpr->iCurrent<iDocid ){
fts3PoslistCopy(0, &pCsr); fts3PoslistCopy(0, &pCsr);
if( pCsr<pEnd ){ if( pCsr<pEnd ){
fts3GetDeltaVarint(&pCsr, &pExpr->iCurrent); fts3GetDeltaVarint(&pCsr, &pExpr->iCurrent);
} }
pExpr->pCurrent = pCsr; pExpr->pCurrent = pCsr;
}else if( pCursor->desc && pExpr->iCurrent>iDocid ){
fts3GetReverseDeltaVarint(&pCsr, pExpr->aDoclist, &pExpr->iCurrent);
fts3ReversePoslist(pExpr->aDoclist, &pCsr);
pExpr->pCurrent = pCsr;
}else{ }else{
if( pExpr->iCurrent==iDocid ){ if( pExpr->iCurrent==iDocid ){
int iThis = 0; int iThis = 0;
@ -3542,8 +3649,19 @@ static int fts3RenameMethod(
return rc; return rc;
} }
static int fts3SavepointMethod(sqlite3_vtab *pVtab, int iSavepoint){
return sqlite3Fts3PendingTermsFlush((Fts3Table *)pVtab);
}
static int fts3ReleaseMethod(sqlite3_vtab *pVtab, int iSavepoint){
return SQLITE_OK;
}
static int fts3RollbackToMethod(sqlite3_vtab *pVtab, int iSavepoint){
sqlite3Fts3PendingTermsClear((Fts3Table *)pVtab);
return SQLITE_OK;
}
static const sqlite3_module fts3Module = { static const sqlite3_module fts3Module = {
/* iVersion */ 0, /* iVersion */ 1,
/* xCreate */ fts3CreateMethod, /* xCreate */ fts3CreateMethod,
/* xConnect */ fts3ConnectMethod, /* xConnect */ fts3ConnectMethod,
/* xBestIndex */ fts3BestIndexMethod, /* xBestIndex */ fts3BestIndexMethod,
@ -3563,6 +3681,9 @@ static const sqlite3_module fts3Module = {
/* xRollback */ fts3RollbackMethod, /* xRollback */ fts3RollbackMethod,
/* xFindFunction */ fts3FindFunctionMethod, /* xFindFunction */ fts3FindFunctionMethod,
/* xRename */ fts3RenameMethod, /* xRename */ fts3RenameMethod,
/* xSavepoint */ fts3SavepointMethod,
/* xRelease */ fts3ReleaseMethod,
/* xRollbackTo */ fts3RollbackToMethod,
}; };
/* /*
@ -3609,6 +3730,11 @@ int sqlite3Fts3Init(sqlite3 *db){
sqlite3Fts3IcuTokenizerModule(&pIcu); sqlite3Fts3IcuTokenizerModule(&pIcu);
#endif #endif
#ifdef SQLITE_TEST
rc = sqlite3Fts3InitTerm(db);
if( rc!=SQLITE_OK ) return rc;
#endif
rc = sqlite3Fts3InitAux(db); rc = sqlite3Fts3InitAux(db);
if( rc!=SQLITE_OK ) return rc; if( rc!=SQLITE_OK ) return rc;

View File

@ -171,6 +171,7 @@ struct Fts3Cursor {
char *pNextId; /* Pointer into the body of aDoclist */ char *pNextId; /* Pointer into the body of aDoclist */
char *aDoclist; /* List of docids for full-text queries */ char *aDoclist; /* List of docids for full-text queries */
int nDoclist; /* Size of buffer at aDoclist */ int nDoclist; /* Size of buffer at aDoclist */
int desc; /* True to sort in descending order */
int eEvalmode; /* An FTS3_EVAL_XX constant */ int eEvalmode; /* An FTS3_EVAL_XX constant */
int nRowAvg; /* Average size of database rows, in pages */ int nRowAvg; /* Average size of database rows, in pages */
@ -353,7 +354,7 @@ int sqlite3Fts3GetVarint32(const char *, int *);
int sqlite3Fts3VarintLen(sqlite3_uint64); int sqlite3Fts3VarintLen(sqlite3_uint64);
void sqlite3Fts3Dequote(char *); void sqlite3Fts3Dequote(char *);
char *sqlite3Fts3FindPositions(Fts3Expr *, sqlite3_int64, int); char *sqlite3Fts3FindPositions(Fts3Cursor *, Fts3Expr *, sqlite3_int64, int);
int sqlite3Fts3ExprLoadDoclist(Fts3Cursor *, Fts3Expr *); int sqlite3Fts3ExprLoadDoclist(Fts3Cursor *, Fts3Expr *);
int sqlite3Fts3ExprLoadFtDoclist(Fts3Cursor *, Fts3Expr *, char **, int *); int sqlite3Fts3ExprLoadFtDoclist(Fts3Cursor *, Fts3Expr *, char **, int *);
int sqlite3Fts3ExprNearTrim(Fts3Expr *, Fts3Expr *, int); int sqlite3Fts3ExprNearTrim(Fts3Expr *, Fts3Expr *, int);

View File

@ -415,7 +415,7 @@ static int fts3SnippetFindPositions(Fts3Expr *pExpr, int iPhrase, void *ctx){
pPhrase->nToken = pExpr->pPhrase->nToken; pPhrase->nToken = pExpr->pPhrase->nToken;
pCsr = sqlite3Fts3FindPositions(pExpr, p->pCsr->iPrevId, p->iCol); pCsr = sqlite3Fts3FindPositions(p->pCsr, pExpr, p->pCsr->iPrevId, p->iCol);
if( pCsr ){ if( pCsr ){
int iFirst = 0; int iFirst = 0;
pPhrase->pList = pCsr; pPhrase->pList = pCsr;
@ -888,7 +888,7 @@ static int fts3ExprLocalHitsCb(
if( pExpr->aDoclist ){ if( pExpr->aDoclist ){
char *pCsr; char *pCsr;
pCsr = sqlite3Fts3FindPositions(pExpr, p->pCursor->iPrevId, -1); pCsr = sqlite3Fts3FindPositions(p->pCursor, pExpr, p->pCursor->iPrevId, -1);
if( pCsr ){ if( pCsr ){
fts3LoadColumnlistCounts(&pCsr, &p->aMatchinfo[iStart], 0); fts3LoadColumnlistCounts(&pCsr, &p->aMatchinfo[iStart], 0);
} }
@ -1055,7 +1055,7 @@ static int fts3MatchinfoLcs(Fts3Cursor *pCsr, MatchInfo *pInfo){
LcsIterator *pIter = &aIter[i]; LcsIterator *pIter = &aIter[i];
nToken -= pIter->pExpr->pPhrase->nToken; nToken -= pIter->pExpr->pPhrase->nToken;
pIter->iPosOffset = nToken; pIter->iPosOffset = nToken;
pIter->pRead = sqlite3Fts3FindPositions(pIter->pExpr, pCsr->iPrevId, -1); pIter->pRead = sqlite3Fts3FindPositions(pCsr,pIter->pExpr,pCsr->iPrevId,-1);
if( pIter->pRead ){ if( pIter->pRead ){
pIter->iPos = pIter->iPosOffset; pIter->iPos = pIter->iPosOffset;
fts3LcsIteratorAdvance(&aIter[i]); fts3LcsIteratorAdvance(&aIter[i]);
@ -1408,6 +1408,7 @@ struct TermOffset {
}; };
struct TermOffsetCtx { struct TermOffsetCtx {
Fts3Cursor *pCsr;
int iCol; /* Column of table to populate aTerm for */ int iCol; /* Column of table to populate aTerm for */
int iTerm; int iTerm;
sqlite3_int64 iDocid; sqlite3_int64 iDocid;
@ -1425,7 +1426,7 @@ static int fts3ExprTermOffsetInit(Fts3Expr *pExpr, int iPhrase, void *ctx){
int iPos = 0; /* First position in position-list */ int iPos = 0; /* First position in position-list */
UNUSED_PARAMETER(iPhrase); UNUSED_PARAMETER(iPhrase);
pList = sqlite3Fts3FindPositions(pExpr, p->iDocid, p->iCol); pList = sqlite3Fts3FindPositions(p->pCsr, pExpr, p->iDocid, p->iCol);
nTerm = pExpr->pPhrase->nToken; nTerm = pExpr->pPhrase->nToken;
if( pList ){ if( pList ){
fts3GetDeltaPosition(&pList, &iPos); fts3GetDeltaPosition(&pList, &iPos);
@ -1478,6 +1479,7 @@ void sqlite3Fts3Offsets(
goto offsets_out; goto offsets_out;
} }
sCtx.iDocid = pCsr->iPrevId; sCtx.iDocid = pCsr->iPrevId;
sCtx.pCsr = pCsr;
/* Loop through the table columns, appending offset information to /* Loop through the table columns, appending offset information to
** string-buffer res for each column. ** string-buffer res for each column.

360
ext/fts3/fts3_term.c Normal file
View File

@ -0,0 +1,360 @@
/*
** 2011 Jan 27
**
** The author disclaims copyright to this source code. In place of
** a legal notice, here is a blessing:
**
** May you do good and not evil.
** May you find forgiveness for yourself and forgive others.
** May you share freely, never taking more than you give.
**
******************************************************************************
**
** This file is not part of the production FTS code. It is only used for
** testing. It contains a virtual table implementation that provides direct
** access to the full-text index of an FTS table.
*/
#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3)
#ifdef SQLITE_TEST
#include "fts3Int.h"
#include <string.h>
#include <assert.h>
typedef struct Fts3termTable Fts3termTable;
typedef struct Fts3termCursor Fts3termCursor;
struct Fts3termTable {
sqlite3_vtab base; /* Base class used by SQLite core */
Fts3Table *pFts3Tab;
};
struct Fts3termCursor {
sqlite3_vtab_cursor base; /* Base class used by SQLite core */
Fts3SegReaderCursor csr; /* Must be right after "base" */
Fts3SegFilter filter;
int isEof; /* True if cursor is at EOF */
char *pNext;
sqlite3_int64 iRowid; /* Current 'rowid' value */
sqlite3_int64 iDocid; /* Current 'docid' value */
int iCol; /* Current 'col' value */
int iPos; /* Current 'pos' value */
};
/*
** Schema of the terms table.
*/
#define FTS3_TERMS_SCHEMA "CREATE TABLE x(term, docid, col, pos)"
/*
** This function does all the work for both the xConnect and xCreate methods.
** These tables have no persistent representation of their own, so xConnect
** and xCreate are identical operations.
*/
static int fts3termConnectMethod(
sqlite3 *db, /* Database connection */
void *pUnused, /* Unused */
int argc, /* Number of elements in argv array */
const char * const *argv, /* xCreate/xConnect argument array */
sqlite3_vtab **ppVtab, /* OUT: New sqlite3_vtab object */
char **pzErr /* OUT: sqlite3_malloc'd error message */
){
char const *zDb; /* Name of database (e.g. "main") */
char const *zFts3; /* Name of fts3 table */
int nDb; /* Result of strlen(zDb) */
int nFts3; /* Result of strlen(zFts3) */
int nByte; /* Bytes of space to allocate here */
int rc; /* value returned by declare_vtab() */
Fts3termTable *p; /* Virtual table object to return */
UNUSED_PARAMETER(pUnused);
/* The user should specify a single argument - the name of an fts3 table. */
if( argc!=4 ){
*pzErr = sqlite3_mprintf(
"wrong number of arguments to fts4term constructor"
);
return SQLITE_ERROR;
}
zDb = argv[1];
nDb = strlen(zDb);
zFts3 = argv[3];
nFts3 = strlen(zFts3);
rc = sqlite3_declare_vtab(db, FTS3_TERMS_SCHEMA);
if( rc!=SQLITE_OK ) return rc;
nByte = sizeof(Fts3termTable) + sizeof(Fts3Table) + nDb + nFts3 + 2;
p = (Fts3termTable *)sqlite3_malloc(nByte);
if( !p ) return SQLITE_NOMEM;
memset(p, 0, nByte);
p->pFts3Tab = (Fts3Table *)&p[1];
p->pFts3Tab->zDb = (char *)&p->pFts3Tab[1];
p->pFts3Tab->zName = &p->pFts3Tab->zDb[nDb+1];
p->pFts3Tab->db = db;
memcpy((char *)p->pFts3Tab->zDb, zDb, nDb);
memcpy((char *)p->pFts3Tab->zName, zFts3, nFts3);
sqlite3Fts3Dequote((char *)p->pFts3Tab->zName);
*ppVtab = (sqlite3_vtab *)p;
return SQLITE_OK;
}
/*
** This function does the work for both the xDisconnect and xDestroy methods.
** These tables have no persistent representation of their own, so xDisconnect
** and xDestroy are identical operations.
*/
static int fts3termDisconnectMethod(sqlite3_vtab *pVtab){
Fts3termTable *p = (Fts3termTable *)pVtab;
Fts3Table *pFts3 = p->pFts3Tab;
int i;
/* Free any prepared statements held */
for(i=0; i<SizeofArray(pFts3->aStmt); i++){
sqlite3_finalize(pFts3->aStmt[i]);
}
sqlite3_free(pFts3->zSegmentsTbl);
sqlite3_free(p);
return SQLITE_OK;
}
#define FTS4AUX_EQ_CONSTRAINT 1
#define FTS4AUX_GE_CONSTRAINT 2
#define FTS4AUX_LE_CONSTRAINT 4
/*
** xBestIndex - Analyze a WHERE and ORDER BY clause.
*/
static int fts3termBestIndexMethod(
sqlite3_vtab *pVTab,
sqlite3_index_info *pInfo
){
UNUSED_PARAMETER(pVTab);
/* This vtab naturally does "ORDER BY term, docid, col, pos". */
if( pInfo->nOrderBy ){
int i;
for(i=0; i<pInfo->nOrderBy; i++){
if( pInfo->aOrderBy[i].iColumn!=i || pInfo->aOrderBy[i].desc ) break;
}
if( i==pInfo->nOrderBy ){
pInfo->orderByConsumed = 1;
}
}
return SQLITE_OK;
}
/*
** xOpen - Open a cursor.
*/
static int fts3termOpenMethod(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCsr){
Fts3termCursor *pCsr; /* Pointer to cursor object to return */
UNUSED_PARAMETER(pVTab);
pCsr = (Fts3termCursor *)sqlite3_malloc(sizeof(Fts3termCursor));
if( !pCsr ) return SQLITE_NOMEM;
memset(pCsr, 0, sizeof(Fts3termCursor));
*ppCsr = (sqlite3_vtab_cursor *)pCsr;
return SQLITE_OK;
}
/*
** xClose - Close a cursor.
*/
static int fts3termCloseMethod(sqlite3_vtab_cursor *pCursor){
Fts3Table *pFts3 = ((Fts3termTable *)pCursor->pVtab)->pFts3Tab;
Fts3termCursor *pCsr = (Fts3termCursor *)pCursor;
sqlite3Fts3SegmentsClose(pFts3);
sqlite3Fts3SegReaderFinish(&pCsr->csr);
sqlite3_free(pCsr);
return SQLITE_OK;
}
/*
** xNext - Advance the cursor to the next row, if any.
*/
static int fts3termNextMethod(sqlite3_vtab_cursor *pCursor){
Fts3termCursor *pCsr = (Fts3termCursor *)pCursor;
Fts3Table *pFts3 = ((Fts3termTable *)pCursor->pVtab)->pFts3Tab;
int rc;
sqlite3_int64 v;
/* Increment our pretend rowid value. */
pCsr->iRowid++;
/* Advance to the next term in the full-text index. */
if( pCsr->csr.aDoclist==0
|| pCsr->pNext>=&pCsr->csr.aDoclist[pCsr->csr.nDoclist-1]
){
rc = sqlite3Fts3SegReaderStep(pFts3, &pCsr->csr);
if( rc!=SQLITE_ROW ){
pCsr->isEof = 1;
return rc;
}
pCsr->iCol = 0;
pCsr->iPos = 0;
pCsr->iDocid = 0;
pCsr->pNext = pCsr->csr.aDoclist;
/* Read docid */
pCsr->pNext += sqlite3Fts3GetVarint(pCsr->pNext, &pCsr->iDocid);
}
pCsr->pNext += sqlite3Fts3GetVarint(pCsr->pNext, &v);
if( v==0 ){
pCsr->pNext += sqlite3Fts3GetVarint(pCsr->pNext, &v);
pCsr->iDocid += v;
pCsr->pNext += sqlite3Fts3GetVarint(pCsr->pNext, &v);
pCsr->iCol = 0;
pCsr->iPos = 0;
}
if( v==1 ){
pCsr->pNext += sqlite3Fts3GetVarint(pCsr->pNext, &v);
pCsr->iCol += v;
pCsr->iPos = 0;
pCsr->pNext += sqlite3Fts3GetVarint(pCsr->pNext, &v);
}
pCsr->iPos += (v - 2);
return SQLITE_OK;
}
/*
** xFilter - Initialize a cursor to point at the start of its data.
*/
static int fts3termFilterMethod(
sqlite3_vtab_cursor *pCursor, /* The cursor used for this query */
int idxNum, /* Strategy index */
const char *idxStr, /* Unused */
int nVal, /* Number of elements in apVal */
sqlite3_value **apVal /* Arguments for the indexing scheme */
){
Fts3termCursor *pCsr = (Fts3termCursor *)pCursor;
Fts3Table *pFts3 = ((Fts3termTable *)pCursor->pVtab)->pFts3Tab;
int rc;
UNUSED_PARAMETER(nVal);
UNUSED_PARAMETER(idxNum);
UNUSED_PARAMETER(idxStr);
UNUSED_PARAMETER(apVal);
assert( idxStr==0 && idxNum==0 );
/* In case this cursor is being reused, close and zero it. */
testcase(pCsr->filter.zTerm);
sqlite3Fts3SegReaderFinish(&pCsr->csr);
memset(&pCsr->csr, 0, ((u8*)&pCsr[1]) - (u8*)&pCsr->csr);
pCsr->filter.flags = FTS3_SEGMENT_REQUIRE_POS|FTS3_SEGMENT_IGNORE_EMPTY;
pCsr->filter.flags |= FTS3_SEGMENT_SCAN;
rc = sqlite3Fts3SegReaderCursor(pFts3, FTS3_SEGCURSOR_ALL,
pCsr->filter.zTerm, pCsr->filter.nTerm, 0, 1, &pCsr->csr
);
if( rc==SQLITE_OK ){
rc = sqlite3Fts3SegReaderStart(pFts3, &pCsr->csr, &pCsr->filter);
}
if( rc==SQLITE_OK ){
rc = fts3termNextMethod(pCursor);
}
return rc;
}
/*
** xEof - Return true if the cursor is at EOF, or false otherwise.
*/
static int fts3termEofMethod(sqlite3_vtab_cursor *pCursor){
Fts3termCursor *pCsr = (Fts3termCursor *)pCursor;
return pCsr->isEof;
}
/*
** xColumn - Return a column value.
*/
static int fts3termColumnMethod(
sqlite3_vtab_cursor *pCursor, /* Cursor to retrieve value from */
sqlite3_context *pCtx, /* Context for sqlite3_result_xxx() calls */
int iCol /* Index of column to read value from */
){
Fts3termCursor *p = (Fts3termCursor *)pCursor;
assert( iCol>=0 && iCol<=3 );
switch( iCol ){
case 0:
sqlite3_result_text(pCtx, p->csr.zTerm, p->csr.nTerm, SQLITE_TRANSIENT);
break;
case 1:
sqlite3_result_int64(pCtx, p->iDocid);
break;
case 2:
sqlite3_result_int64(pCtx, p->iCol);
break;
default:
sqlite3_result_int64(pCtx, p->iPos);
break;
}
return SQLITE_OK;
}
/*
** xRowid - Return the current rowid for the cursor.
*/
static int fts3termRowidMethod(
sqlite3_vtab_cursor *pCursor, /* Cursor to retrieve value from */
sqlite_int64 *pRowid /* OUT: Rowid value */
){
Fts3termCursor *pCsr = (Fts3termCursor *)pCursor;
*pRowid = pCsr->iRowid;
return SQLITE_OK;
}
/*
** Register the fts3term module with database connection db. Return SQLITE_OK
** if successful or an error code if sqlite3_create_module() fails.
*/
int sqlite3Fts3InitTerm(sqlite3 *db){
static const sqlite3_module fts3term_module = {
0, /* iVersion */
fts3termConnectMethod, /* xCreate */
fts3termConnectMethod, /* xConnect */
fts3termBestIndexMethod, /* xBestIndex */
fts3termDisconnectMethod, /* xDisconnect */
fts3termDisconnectMethod, /* xDestroy */
fts3termOpenMethod, /* xOpen */
fts3termCloseMethod, /* xClose */
fts3termFilterMethod, /* xFilter */
fts3termNextMethod, /* xNext */
fts3termEofMethod, /* xEof */
fts3termColumnMethod, /* xColumn */
fts3termRowidMethod, /* xRowid */
0, /* xUpdate */
0, /* xBegin */
0, /* xSync */
0, /* xCommit */
0, /* xRollback */
0, /* xFindFunction */
0 /* xRename */
};
int rc; /* Return code */
rc = sqlite3_create_module(db, "fts4term", &fts3term_module, 0);
return rc;
}
#endif
#endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3) */

View File

@ -542,6 +542,14 @@ static int fts3PendingTermsAdd(
assert( pTokenizer && pModule ); assert( pTokenizer && pModule );
/* If the user has inserted a NULL value, this function may be called with
** zText==0. In this case, add zero token entries to the hash table and
** return early. */
if( zText==0 ){
*pnWord = 0;
return SQLITE_OK;
}
rc = pModule->xOpen(pTokenizer, zText, -1, &pCsr); rc = pModule->xOpen(pTokenizer, zText, -1, &pCsr);
if( rc!=SQLITE_OK ){ if( rc!=SQLITE_OK ){
return rc; return rc;
@ -632,11 +640,9 @@ static int fts3InsertTerms(Fts3Table *p, sqlite3_value **apVal, u32 *aSz){
int i; /* Iterator variable */ int i; /* Iterator variable */
for(i=2; i<p->nColumn+2; i++){ for(i=2; i<p->nColumn+2; i++){
const char *zText = (const char *)sqlite3_value_text(apVal[i]); const char *zText = (const char *)sqlite3_value_text(apVal[i]);
if( zText ){ int rc = fts3PendingTermsAdd(p, zText, i-2, &aSz[i-2]);
int rc = fts3PendingTermsAdd(p, zText, i-2, &aSz[i-2]); if( rc!=SQLITE_OK ){
if( rc!=SQLITE_OK ){ return rc;
return rc;
}
} }
aSz[p->nColumn] += sqlite3_value_bytes(apVal[i]); aSz[p->nColumn] += sqlite3_value_bytes(apVal[i]);
} }
@ -741,14 +747,14 @@ static int fts3DeleteAll(Fts3Table *p){
static void fts3DeleteTerms( static void fts3DeleteTerms(
int *pRC, /* Result code */ int *pRC, /* Result code */
Fts3Table *p, /* The FTS table to delete from */ Fts3Table *p, /* The FTS table to delete from */
sqlite3_value **apVal, /* apVal[] contains the docid to be deleted */ sqlite3_value *pRowid, /* The docid to be deleted */
u32 *aSz /* Sizes of deleted document written here */ u32 *aSz /* Sizes of deleted document written here */
){ ){
int rc; int rc;
sqlite3_stmt *pSelect; sqlite3_stmt *pSelect;
if( *pRC ) return; if( *pRC ) return;
rc = fts3SqlStmt(p, SQL_SELECT_CONTENT_BY_ROWID, &pSelect, apVal); rc = fts3SqlStmt(p, SQL_SELECT_CONTENT_BY_ROWID, &pSelect, &pRowid);
if( rc==SQLITE_OK ){ if( rc==SQLITE_OK ){
if( SQLITE_ROW==sqlite3_step(pSelect) ){ if( SQLITE_ROW==sqlite3_step(pSelect) ){
int i; int i;
@ -1888,16 +1894,16 @@ static void fts3SegWriterFree(SegmentWriter *pWriter){
** The first value in the apVal[] array is assumed to contain an integer. ** The first value in the apVal[] array is assumed to contain an integer.
** This function tests if there exist any documents with docid values that ** This function tests if there exist any documents with docid values that
** are different from that integer. i.e. if deleting the document with docid ** are different from that integer. i.e. if deleting the document with docid
** apVal[0] would mean the FTS3 table were empty. ** pRowid would mean the FTS3 table were empty.
** **
** If successful, *pisEmpty is set to true if the table is empty except for ** If successful, *pisEmpty is set to true if the table is empty except for
** document apVal[0], or false otherwise, and SQLITE_OK is returned. If an ** document pRowid, or false otherwise, and SQLITE_OK is returned. If an
** error occurs, an SQLite error code is returned. ** error occurs, an SQLite error code is returned.
*/ */
static int fts3IsEmpty(Fts3Table *p, sqlite3_value **apVal, int *pisEmpty){ static int fts3IsEmpty(Fts3Table *p, sqlite3_value *pRowid, int *pisEmpty){
sqlite3_stmt *pStmt; sqlite3_stmt *pStmt;
int rc; int rc;
rc = fts3SqlStmt(p, SQL_IS_EMPTY, &pStmt, apVal); rc = fts3SqlStmt(p, SQL_IS_EMPTY, &pStmt, &pRowid);
if( rc==SQLITE_OK ){ if( rc==SQLITE_OK ){
if( SQLITE_ROW==sqlite3_step(pStmt) ){ if( SQLITE_ROW==sqlite3_step(pStmt) ){
*pisEmpty = sqlite3_column_int(pStmt, 0); *pisEmpty = sqlite3_column_int(pStmt, 0);
@ -2621,6 +2627,40 @@ int sqlite3Fts3DeferToken(
return SQLITE_OK; return SQLITE_OK;
} }
/*
** SQLite value pRowid contains the rowid of a row that may or may not be
** present in the FTS3 table. If it is, delete it and adjust the contents
** of subsiduary data structures accordingly.
*/
static int fts3DeleteByRowid(
Fts3Table *p,
sqlite3_value *pRowid,
int *pnDoc,
u32 *aSzDel
){
int isEmpty = 0;
int rc = fts3IsEmpty(p, pRowid, &isEmpty);
if( rc==SQLITE_OK ){
if( isEmpty ){
/* Deleting this row means the whole table is empty. In this case
** delete the contents of all three tables and throw away any
** data in the pendingTerms hash table. */
rc = fts3DeleteAll(p);
*pnDoc = *pnDoc - 1;
}else{
sqlite3_int64 iRemove = sqlite3_value_int64(pRowid);
rc = fts3PendingTermsDocid(p, iRemove);
fts3DeleteTerms(&rc, p, pRowid, aSzDel);
fts3SqlExec(&rc, p, SQL_DELETE_CONTENT, &pRowid);
if( sqlite3_changes(p->db) ) *pnDoc = *pnDoc - 1;
if( p->bHasDocsize ){
fts3SqlExec(&rc, p, SQL_DELETE_DOCSIZE, &pRowid);
}
}
}
return rc;
}
/* /*
** This function does the work for the xUpdate method of FTS3 virtual ** This function does the work for the xUpdate method of FTS3 virtual
@ -2639,46 +2679,91 @@ int sqlite3Fts3UpdateMethod(
u32 *aSzIns; /* Sizes of inserted documents */ u32 *aSzIns; /* Sizes of inserted documents */
u32 *aSzDel; /* Sizes of deleted documents */ u32 *aSzDel; /* Sizes of deleted documents */
int nChng = 0; /* Net change in number of documents */ int nChng = 0; /* Net change in number of documents */
int bInsertDone = 0;
assert( p->pSegments==0 ); assert( p->pSegments==0 );
/* Check for a "special" INSERT operation. One of the form:
**
** INSERT INTO xyz(xyz) VALUES('command');
*/
if( nArg>1
&& sqlite3_value_type(apVal[0])==SQLITE_NULL
&& sqlite3_value_type(apVal[p->nColumn+2])!=SQLITE_NULL
){
return fts3SpecialInsert(p, apVal[p->nColumn+2]);
}
/* Allocate space to hold the change in document sizes */ /* Allocate space to hold the change in document sizes */
aSzIns = sqlite3_malloc( sizeof(aSzIns[0])*(p->nColumn+1)*2 ); aSzIns = sqlite3_malloc( sizeof(aSzIns[0])*(p->nColumn+1)*2 );
if( aSzIns==0 ) return SQLITE_NOMEM; if( aSzIns==0 ) return SQLITE_NOMEM;
aSzDel = &aSzIns[p->nColumn+1]; aSzDel = &aSzIns[p->nColumn+1];
memset(aSzIns, 0, sizeof(aSzIns[0])*(p->nColumn+1)*2); memset(aSzIns, 0, sizeof(aSzIns[0])*(p->nColumn+1)*2);
/* If this is a DELETE or UPDATE operation, remove the old record. */ /* If this is an INSERT operation, or an UPDATE that modifies the rowid
if( sqlite3_value_type(apVal[0])!=SQLITE_NULL ){ ** value, then this operation requires constraint handling.
int isEmpty = 0; **
rc = fts3IsEmpty(p, apVal, &isEmpty); ** If the on-conflict mode is REPLACE, this means that the existing row
if( rc==SQLITE_OK ){ ** should be deleted from the database before inserting the new row. Or,
if( isEmpty ){ ** if the on-conflict mode is other than REPLACE, then this method must
/* Deleting this row means the whole table is empty. In this case ** detect the conflict and return SQLITE_CONSTRAINT before beginning to
** delete the contents of all three tables and throw away any ** modify the database file.
** data in the pendingTerms hash table. */
*/ if( nArg>1 ){
rc = fts3DeleteAll(p); /* Find the value object that holds the new rowid value. */
sqlite3_value *pNewRowid = apVal[3+p->nColumn];
if( sqlite3_value_type(pNewRowid)==SQLITE_NULL ){
pNewRowid = apVal[1];
}
if( sqlite3_value_type(pNewRowid)!=SQLITE_NULL && (
sqlite3_value_type(apVal[0])==SQLITE_NULL
|| sqlite3_value_int64(apVal[0])!=sqlite3_value_int64(pNewRowid)
)){
/* The new rowid is not NULL (in this case the rowid will be
** automatically assigned and there is no chance of a conflict), and
** the statement is either an INSERT or an UPDATE that modifies the
** rowid column. So if the conflict mode is REPLACE, then delete any
** existing row with rowid=pNewRowid.
**
** Or, if the conflict mode is not REPLACE, insert the new record into
** the %_content table. If we hit the duplicate rowid constraint (or any
** other error) while doing so, return immediately.
**
** This branch may also run if pNewRowid contains a value that cannot
** be losslessly converted to an integer. In this case, the eventual
** call to fts3InsertData() (either just below or further on in this
** function) will return SQLITE_MISMATCH. If fts3DeleteByRowid is
** invoked, it will delete zero rows (since no row will have
** docid=$pNewRowid if $pNewRowid is not an integer value).
*/
if( sqlite3_vtab_on_conflict(p->db)==SQLITE_REPLACE ){
rc = fts3DeleteByRowid(p, pNewRowid, &nChng, aSzDel);
}else{ }else{
isRemove = 1; rc = fts3InsertData(p, apVal, pRowid);
iRemove = sqlite3_value_int64(apVal[0]); bInsertDone = 1;
rc = fts3PendingTermsDocid(p, iRemove);
fts3DeleteTerms(&rc, p, apVal, aSzDel);
fts3SqlExec(&rc, p, SQL_DELETE_CONTENT, apVal);
if( p->bHasDocsize ){
fts3SqlExec(&rc, p, SQL_DELETE_DOCSIZE, apVal);
}
nChng--;
} }
} }
}else if( sqlite3_value_type(apVal[p->nColumn+2])!=SQLITE_NULL ){ }
if( rc!=SQLITE_OK ){
sqlite3_free(aSzIns); sqlite3_free(aSzIns);
return fts3SpecialInsert(p, apVal[p->nColumn+2]); return rc;
}
/* If this is a DELETE or UPDATE operation, remove the old record. */
if( sqlite3_value_type(apVal[0])!=SQLITE_NULL ){
assert( sqlite3_value_type(apVal[0])==SQLITE_INTEGER );
rc = fts3DeleteByRowid(p, apVal[0], &nChng, aSzDel);
isRemove = 1;
iRemove = sqlite3_value_int64(apVal[0]);
} }
/* If this is an INSERT or UPDATE operation, insert the new record. */ /* If this is an INSERT or UPDATE operation, insert the new record. */
if( nArg>1 && rc==SQLITE_OK ){ if( nArg>1 && rc==SQLITE_OK ){
rc = fts3InsertData(p, apVal, pRowid); if( bInsertDone==0 ){
rc = fts3InsertData(p, apVal, pRowid);
if( rc==SQLITE_CONSTRAINT ) rc = SQLITE_CORRUPT;
}
if( rc==SQLITE_OK && (!isRemove || *pRowid!=iRemove) ){ if( rc==SQLITE_OK && (!isRemove || *pRowid!=iRemove) ){
rc = fts3PendingTermsDocid(p, *pRowid); rc = fts3PendingTermsDocid(p, *pRowid);
} }

View File

@ -2625,6 +2625,90 @@ static int newRowid(Rtree *pRtree, i64 *piRowid){
return rc; return rc;
} }
/*
** Remove the entry with rowid=iDelete from the r-tree structure.
*/
static int rtreeDeleteRowid(Rtree *pRtree, sqlite3_int64 iDelete){
int rc; /* Return code */
RtreeNode *pLeaf; /* Leaf node containing record iDelete */
int iCell; /* Index of iDelete cell in pLeaf */
RtreeNode *pRoot; /* Root node of rtree structure */
/* Obtain a reference to the root node to initialise Rtree.iDepth */
rc = nodeAcquire(pRtree, 1, 0, &pRoot);
/* Obtain a reference to the leaf node that contains the entry
** about to be deleted.
*/
if( rc==SQLITE_OK ){
rc = findLeafNode(pRtree, iDelete, &pLeaf);
}
/* Delete the cell in question from the leaf node. */
if( rc==SQLITE_OK ){
int rc2;
rc = nodeRowidIndex(pRtree, pLeaf, iDelete, &iCell);
if( rc==SQLITE_OK ){
rc = deleteCell(pRtree, pLeaf, iCell, 0);
}
rc2 = nodeRelease(pRtree, pLeaf);
if( rc==SQLITE_OK ){
rc = rc2;
}
}
/* Delete the corresponding entry in the <rtree>_rowid table. */
if( rc==SQLITE_OK ){
sqlite3_bind_int64(pRtree->pDeleteRowid, 1, iDelete);
sqlite3_step(pRtree->pDeleteRowid);
rc = sqlite3_reset(pRtree->pDeleteRowid);
}
/* Check if the root node now has exactly one child. If so, remove
** it, schedule the contents of the child for reinsertion and
** reduce the tree height by one.
**
** This is equivalent to copying the contents of the child into
** the root node (the operation that Gutman's paper says to perform
** in this scenario).
*/
if( rc==SQLITE_OK && pRtree->iDepth>0 && NCELL(pRoot)==1 ){
int rc2;
RtreeNode *pChild;
i64 iChild = nodeGetRowid(pRtree, pRoot, 0);
rc = nodeAcquire(pRtree, iChild, pRoot, &pChild);
if( rc==SQLITE_OK ){
rc = removeNode(pRtree, pChild, pRtree->iDepth-1);
}
rc2 = nodeRelease(pRtree, pChild);
if( rc==SQLITE_OK ) rc = rc2;
if( rc==SQLITE_OK ){
pRtree->iDepth--;
writeInt16(pRoot->zData, pRtree->iDepth);
pRoot->isDirty = 1;
}
}
/* Re-insert the contents of any underfull nodes removed from the tree. */
for(pLeaf=pRtree->pDeleted; pLeaf; pLeaf=pRtree->pDeleted){
if( rc==SQLITE_OK ){
rc = reinsertNodeContent(pRtree, pLeaf);
}
pRtree->pDeleted = pLeaf->pNext;
sqlite3_free(pLeaf);
}
/* Release the reference to the root node. */
if( rc==SQLITE_OK ){
rc = nodeRelease(pRtree, pRoot);
}else{
nodeRelease(pRtree, pRoot);
}
return rc;
}
/* /*
** The xUpdate method for rtree module virtual tables. ** The xUpdate method for rtree module virtual tables.
*/ */
@ -2636,103 +2720,25 @@ static int rtreeUpdate(
){ ){
Rtree *pRtree = (Rtree *)pVtab; Rtree *pRtree = (Rtree *)pVtab;
int rc = SQLITE_OK; int rc = SQLITE_OK;
RtreeCell cell; /* New cell to insert if nData>1 */
int bHaveRowid = 0; /* Set to 1 after new rowid is determined */
rtreeReference(pRtree); rtreeReference(pRtree);
assert(nData>=1); assert(nData>=1);
/* If azData[0] is not an SQL NULL value, it is the rowid of a /* Constraint handling. A write operation on an r-tree table may return
** record to delete from the r-tree table. The following block does ** SQLITE_CONSTRAINT for two reasons:
** just that. **
** 1. A duplicate rowid value, or
** 2. The supplied data violates the "x2>=x1" constraint.
**
** In the first case, if the conflict-handling mode is REPLACE, then
** the conflicting row can be removed before proceeding. In the second
** case, SQLITE_CONSTRAINT must be returned regardless of the
** conflict-handling mode specified by the user.
*/ */
if( sqlite3_value_type(azData[0])!=SQLITE_NULL ){ if( nData>1 ){
i64 iDelete; /* The rowid to delete */
RtreeNode *pLeaf; /* Leaf node containing record iDelete */
int iCell; /* Index of iDelete cell in pLeaf */
RtreeNode *pRoot;
/* Obtain a reference to the root node to initialise Rtree.iDepth */
rc = nodeAcquire(pRtree, 1, 0, &pRoot);
/* Obtain a reference to the leaf node that contains the entry
** about to be deleted.
*/
if( rc==SQLITE_OK ){
iDelete = sqlite3_value_int64(azData[0]);
rc = findLeafNode(pRtree, iDelete, &pLeaf);
}
/* Delete the cell in question from the leaf node. */
if( rc==SQLITE_OK ){
int rc2;
rc = nodeRowidIndex(pRtree, pLeaf, iDelete, &iCell);
if( rc==SQLITE_OK ){
rc = deleteCell(pRtree, pLeaf, iCell, 0);
}
rc2 = nodeRelease(pRtree, pLeaf);
if( rc==SQLITE_OK ){
rc = rc2;
}
}
/* Delete the corresponding entry in the <rtree>_rowid table. */
if( rc==SQLITE_OK ){
sqlite3_bind_int64(pRtree->pDeleteRowid, 1, iDelete);
sqlite3_step(pRtree->pDeleteRowid);
rc = sqlite3_reset(pRtree->pDeleteRowid);
}
/* Check if the root node now has exactly one child. If so, remove
** it, schedule the contents of the child for reinsertion and
** reduce the tree height by one.
**
** This is equivalent to copying the contents of the child into
** the root node (the operation that Gutman's paper says to perform
** in this scenario).
*/
if( rc==SQLITE_OK && pRtree->iDepth>0 && NCELL(pRoot)==1 ){
int rc2;
RtreeNode *pChild;
i64 iChild = nodeGetRowid(pRtree, pRoot, 0);
rc = nodeAcquire(pRtree, iChild, pRoot, &pChild);
if( rc==SQLITE_OK ){
rc = removeNode(pRtree, pChild, pRtree->iDepth-1);
}
rc2 = nodeRelease(pRtree, pChild);
if( rc==SQLITE_OK ) rc = rc2;
if( rc==SQLITE_OK ){
pRtree->iDepth--;
writeInt16(pRoot->zData, pRtree->iDepth);
pRoot->isDirty = 1;
}
}
/* Re-insert the contents of any underfull nodes removed from the tree. */
for(pLeaf=pRtree->pDeleted; pLeaf; pLeaf=pRtree->pDeleted){
if( rc==SQLITE_OK ){
rc = reinsertNodeContent(pRtree, pLeaf);
}
pRtree->pDeleted = pLeaf->pNext;
sqlite3_free(pLeaf);
}
/* Release the reference to the root node. */
if( rc==SQLITE_OK ){
rc = nodeRelease(pRtree, pRoot);
}else{
nodeRelease(pRtree, pRoot);
}
}
/* If the azData[] array contains more than one element, elements
** (azData[2]..azData[argc-1]) contain a new record to insert into
** the r-tree structure.
*/
if( rc==SQLITE_OK && nData>1 ){
/* Insert a new record into the r-tree */
RtreeCell cell;
int ii; int ii;
RtreeNode *pLeaf;
/* Populate the cell.aCoord[] array. The first coordinate is azData[3]. */ /* Populate the cell.aCoord[] array. The first coordinate is azData[3]. */
assert( nData==(pRtree->nDim*2 + 3) ); assert( nData==(pRtree->nDim*2 + 3) );
@ -2756,18 +2762,49 @@ static int rtreeUpdate(
} }
} }
/* Figure out the rowid of the new row. */ /* If a rowid value was supplied, check if it is already present in
if( sqlite3_value_type(azData[2])==SQLITE_NULL ){ ** the table. If so, the constraint has failed. */
rc = newRowid(pRtree, &cell.iRowid); if( sqlite3_value_type(azData[2])!=SQLITE_NULL ){
}else{
cell.iRowid = sqlite3_value_int64(azData[2]); cell.iRowid = sqlite3_value_int64(azData[2]);
sqlite3_bind_int64(pRtree->pReadRowid, 1, cell.iRowid); if( sqlite3_value_type(azData[0])==SQLITE_NULL
if( SQLITE_ROW==sqlite3_step(pRtree->pReadRowid) ){ || sqlite3_value_int64(azData[0])!=cell.iRowid
sqlite3_reset(pRtree->pReadRowid); ){
rc = SQLITE_CONSTRAINT; int steprc;
goto constraint; sqlite3_bind_int64(pRtree->pReadRowid, 1, cell.iRowid);
steprc = sqlite3_step(pRtree->pReadRowid);
rc = sqlite3_reset(pRtree->pReadRowid);
if( SQLITE_ROW==steprc ){
if( sqlite3_vtab_on_conflict(pRtree->db)==SQLITE_REPLACE ){
rc = rtreeDeleteRowid(pRtree, cell.iRowid);
}else{
rc = SQLITE_CONSTRAINT;
goto constraint;
}
}
} }
rc = sqlite3_reset(pRtree->pReadRowid); bHaveRowid = 1;
}
}
/* If azData[0] is not an SQL NULL value, it is the rowid of a
** record to delete from the r-tree table. The following block does
** just that.
*/
if( sqlite3_value_type(azData[0])!=SQLITE_NULL ){
rc = rtreeDeleteRowid(pRtree, sqlite3_value_int64(azData[0]));
}
/* If the azData[] array contains more than one element, elements
** (azData[2]..azData[argc-1]) contain a new record to insert into
** the r-tree structure.
*/
if( rc==SQLITE_OK && nData>1 ){
/* Insert the new record into the r-tree */
RtreeNode *pLeaf;
/* Figure out the rowid of the new row. */
if( bHaveRowid==0 ){
rc = newRowid(pRtree, &cell.iRowid);
} }
*pRowid = cell.iRowid; *pRowid = cell.iRowid;
@ -3008,6 +3045,8 @@ static int rtreeInit(
return SQLITE_ERROR; return SQLITE_ERROR;
} }
sqlite3_vtab_config(db, SQLITE_VTAB_CONSTRAINT_SUPPORT, 1);
/* Allocate the sqlite3_vtab structure */ /* Allocate the sqlite3_vtab structure */
nDb = strlen(argv[1]); nDb = strlen(argv[1]);
nName = strlen(argv[2]); nName = strlen(argv[2]);

View File

@ -31,6 +31,8 @@ source $testdir/tester.tcl
# rtree-7.*: Test renaming an r-tree table. # rtree-7.*: Test renaming an r-tree table.
# rtree-8.*: Test constrained scans of r-tree data. # rtree-8.*: Test constrained scans of r-tree data.
# #
# rtree-12.*: Test that on-conflict clauses are supported.
#
ifcapable !rtree { ifcapable !rtree {
finish_test finish_test
@ -416,4 +418,83 @@ do_test rtree-11.2 {
} }
} {2} } {2}
#-------------------------------------------------------------------------
# Test on-conflict clause handling.
#
db_delete_and_reopen
do_execsql_test 12.0 {
CREATE VIRTUAL TABLE t1 USING rtree_i32(idx, x1, x2, y1, y2);
INSERT INTO t1 VALUES(1, 1, 2, 3, 4);
INSERT INTO t1 VALUES(2, 2, 3, 4, 5);
INSERT INTO t1 VALUES(3, 3, 4, 5, 6);
CREATE TABLE source(idx, x1, x2, y1, y2);
INSERT INTO source VALUES(5, 8, 8, 8, 8);
INSERT INTO source VALUES(2, 7, 7, 7, 7);
}
db_save_and_close
foreach {tn sql_template testdata} {
1 "INSERT %CONF% INTO t1 VALUES(2, 7, 7, 7, 7)" {
ROLLBACK 0 1 {1 1 2 3 4 2 2 3 4 5 3 3 4 5 6}
ABORT 0 1 {1 1 2 3 4 2 2 3 4 5 3 3 4 5 6 4 4 5 6 7}
IGNORE 0 0 {1 1 2 3 4 2 2 3 4 5 3 3 4 5 6 4 4 5 6 7}
FAIL 0 1 {1 1 2 3 4 2 2 3 4 5 3 3 4 5 6 4 4 5 6 7}
REPLACE 0 0 {1 1 2 3 4 2 7 7 7 7 3 3 4 5 6 4 4 5 6 7}
}
2 "INSERT %CONF% INTO t1 SELECT * FROM source" {
ROLLBACK 1 1 {1 1 2 3 4 2 2 3 4 5 3 3 4 5 6}
ABORT 1 1 {1 1 2 3 4 2 2 3 4 5 3 3 4 5 6 4 4 5 6 7}
IGNORE 1 0 {1 1 2 3 4 2 2 3 4 5 3 3 4 5 6 4 4 5 6 7 5 8 8 8 8}
FAIL 1 1 {1 1 2 3 4 2 2 3 4 5 3 3 4 5 6 4 4 5 6 7 5 8 8 8 8}
REPLACE 1 0 {1 1 2 3 4 2 7 7 7 7 3 3 4 5 6 4 4 5 6 7 5 8 8 8 8}
}
3 "UPDATE %CONF% t1 SET idx = 2 WHERE idx = 4" {
ROLLBACK 1 1 {1 1 2 3 4 2 2 3 4 5 3 3 4 5 6}
ABORT 1 1 {1 1 2 3 4 2 2 3 4 5 3 3 4 5 6 4 4 5 6 7}
IGNORE 1 0 {1 1 2 3 4 2 2 3 4 5 3 3 4 5 6 4 4 5 6 7}
FAIL 1 1 {1 1 2 3 4 2 2 3 4 5 3 3 4 5 6 4 4 5 6 7}
REPLACE 1 0 {1 1 2 3 4 2 4 5 6 7 3 3 4 5 6}
}
3 "UPDATE %CONF% t1 SET idx = ((idx+1)%5)+1 WHERE idx > 2" {
ROLLBACK 1 1 {1 1 2 3 4 2 2 3 4 5 3 3 4 5 6}
ABORT 1 1 {1 1 2 3 4 2 2 3 4 5 3 3 4 5 6 4 4 5 6 7}
IGNORE 1 0 {1 1 2 3 4 2 2 3 4 5 4 4 5 6 7 5 3 4 5 6}
FAIL 1 1 {1 1 2 3 4 2 2 3 4 5 4 4 5 6 7 5 3 4 5 6}
REPLACE 1 0 {1 4 5 6 7 2 2 3 4 5 5 3 4 5 6}
}
4 "INSERT %CONF% INTO t1 VALUES(2, 7, 6, 7, 7)" {
ROLLBACK 0 1 {1 1 2 3 4 2 2 3 4 5 3 3 4 5 6}
ABORT 0 1 {1 1 2 3 4 2 2 3 4 5 3 3 4 5 6 4 4 5 6 7}
IGNORE 0 0 {1 1 2 3 4 2 2 3 4 5 3 3 4 5 6 4 4 5 6 7}
FAIL 0 1 {1 1 2 3 4 2 2 3 4 5 3 3 4 5 6 4 4 5 6 7}
REPLACE 0 1 {1 1 2 3 4 2 2 3 4 5 3 3 4 5 6 4 4 5 6 7}
}
} {
foreach {mode uses error data} $testdata {
db_restore_and_reopen
set sql [string map [list %CONF% "OR $mode"] $sql_template]
set testname "12.$tn.[string tolower $mode]"
execsql {
BEGIN;
INSERT INTO t1 VALUES(4, 4, 5, 6, 7);
}
set res(0) {0 {}}
set res(1) {1 {constraint failed}}
do_catchsql_test $testname.1 $sql $res($error)
do_test $testname.2 [list sql_uses_stmt db $sql] $uses
do_execsql_test $testname.3 { SELECT * FROM t1 ORDER BY idx } $data
do_test $testname.4 { rtree_check db t1 } 0
db close
}
}
finish_test finish_test

View File

@ -300,6 +300,7 @@ TESTSRC2 = \
$(TOP)/ext/fts3/fts3.c \ $(TOP)/ext/fts3/fts3.c \
$(TOP)/ext/fts3/fts3_aux.c \ $(TOP)/ext/fts3/fts3_aux.c \
$(TOP)/ext/fts3/fts3_expr.c \ $(TOP)/ext/fts3/fts3_expr.c \
$(TOP)/ext/fts3/fts3_term.c \
$(TOP)/ext/fts3/fts3_tokenizer.c \ $(TOP)/ext/fts3/fts3_tokenizer.c \
$(TOP)/ext/fts3/fts3_write.c \ $(TOP)/ext/fts3/fts3_write.c \
$(TOP)/ext/async/sqlite3async.c \ $(TOP)/ext/async/sqlite3async.c \

View File

@ -1,11 +1,11 @@
C Merge\strunk\schanges\sinto\ssessions\sbranch. C Merge\sthe\slatest\strunk\schanges\sinto\sthe\ssessions\sbranch.
D 2011-04-18T17:30:56.521 D 2011-05-05T15:46:16.843
F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
F Makefile.in 7a4d9524721d40ef9ee26f93f9bd6a51dba106f2 F Makefile.in 7a4d9524721d40ef9ee26f93f9bd6a51dba106f2
F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
F Makefile.vxworks c85ec1d8597fe2f7bc225af12ac1666e21379151 F Makefile.vxworks c85ec1d8597fe2f7bc225af12ac1666e21379151
F README cd04a36fbc7ea56932a4052d7d0b7f09f27c33d6 F README cd04a36fbc7ea56932a4052d7d0b7f09f27c33d6
F VERSION c97e5dcdea2407f4a94f9740294cdf39ce9e88c4 F VERSION 3fcdd7fbe3eb282df3978fe77288544543767961
F aclocal.m4 a5c22d164aff7ed549d53a90fa56d56955281f50 F aclocal.m4 a5c22d164aff7ed549d53a90fa56d56955281f50
F addopcodes.awk 17dc593f791f874d2c23a0f9360850ded0286531 F addopcodes.awk 17dc593f791f874d2c23a0f9360850ded0286531
F art/2005osaward.gif 0d1851b2a7c1c9d0ccce545f3e14bca42d7fd248 F art/2005osaward.gif 0d1851b2a7c1c9d0ccce545f3e14bca42d7fd248
@ -22,7 +22,7 @@ F art/src_logo.gif 9341ef09f0e53cd44c0c9b6fc3c16f7f3d6c2ad9
F config.guess 226d9a188c6196f3033ffc651cbc9dcee1a42977 F config.guess 226d9a188c6196f3033ffc651cbc9dcee1a42977
F config.h.in 868fdb48c028421a203470e15c69ada15b9ba673 F config.h.in 868fdb48c028421a203470e15c69ada15b9ba673
F config.sub 9ebe4c3b3dab6431ece34f16828b594fb420da55 F config.sub 9ebe4c3b3dab6431ece34f16828b594fb420da55
F configure 61dbf78cdc4d6a871333dc599c130be6cce865c5 x F configure 1c31f231ba59b71ff81dbf5c7c7594fdc83803d5 x
F configure.ac 87a3c71bbe9c925381c154413eea7f3cdc397244 F configure.ac 87a3c71bbe9c925381c154413eea7f3cdc397244
F contrib/sqlitecon.tcl 210a913ad63f9f991070821e599d600bd913e0ad F contrib/sqlitecon.tcl 210a913ad63f9f991070821e599d600bd913e0ad
F doc/lemon.html f0f682f50210928c07e562621c3b7e8ab912a538 F doc/lemon.html f0f682f50210928c07e562621c3b7e8ab912a538
@ -61,29 +61,30 @@ F ext/fts2/mkfts2amal.tcl 974d5d438cb3f7c4a652639262f82418c1e4cff0
F ext/fts3/README.syntax a19711dc5458c20734b8e485e75fb1981ec2427a F ext/fts3/README.syntax a19711dc5458c20734b8e485e75fb1981ec2427a
F ext/fts3/README.tokenizers 998756696647400de63d5ba60e9655036cb966e9 F ext/fts3/README.tokenizers 998756696647400de63d5ba60e9655036cb966e9
F ext/fts3/README.txt 8c18f41574404623b76917b9da66fcb0ab38328d F ext/fts3/README.txt 8c18f41574404623b76917b9da66fcb0ab38328d
F ext/fts3/fts3.c 5653c5654ac9b65bf3646af7e1d695c7e9b991a0 F ext/fts3/fts3.c 73d6718dba1e4ef621ff8a4ac1c22019460c84cc
F ext/fts3/fts3.h 3a10a0af180d502cecc50df77b1b22df142817fe F ext/fts3/fts3.h 3a10a0af180d502cecc50df77b1b22df142817fe
F ext/fts3/fts3Int.h 945926ea4b6a686c3e9834640a252d9870b7191e F ext/fts3/fts3Int.h 8c2ac39ee17362571c58ab2c4f0667324c31f738
F ext/fts3/fts3_aux.c 9e931f55eed8498dafe7bc1160f10cbb1a652fdf F ext/fts3/fts3_aux.c 9e931f55eed8498dafe7bc1160f10cbb1a652fdf
F ext/fts3/fts3_expr.c 5f49e0deaf723724b08100bb3ff40aab02ad0c93 F ext/fts3/fts3_expr.c 5f49e0deaf723724b08100bb3ff40aab02ad0c93
F ext/fts3/fts3_hash.c 3c8f6387a4a7f5305588b203fa7c887d753e1f1c F ext/fts3/fts3_hash.c 3c8f6387a4a7f5305588b203fa7c887d753e1f1c
F ext/fts3/fts3_hash.h 8331fb2206c609f9fc4c4735b9ab5ad6137c88ec F ext/fts3/fts3_hash.h 8331fb2206c609f9fc4c4735b9ab5ad6137c88ec
F ext/fts3/fts3_icu.c ac494aed69835008185299315403044664bda295 F ext/fts3/fts3_icu.c ac494aed69835008185299315403044664bda295
F ext/fts3/fts3_porter.c d61cfd81fb0fd8fbcb25adcaee0ba671aefaa5c2 F ext/fts3/fts3_porter.c d61cfd81fb0fd8fbcb25adcaee0ba671aefaa5c2
F ext/fts3/fts3_snippet.c e857c6a89d81d3b89df59f3b44b35c68d8ed5c62 F ext/fts3/fts3_snippet.c a4a3c7d2ab15ca9188e2d9b51a5e3927bf76580d
F ext/fts3/fts3_term.c f115f5a5f4298303d3b22fc6c524b8d565c7b950
F ext/fts3/fts3_tokenizer.c 055f3dc7369585350b28db1ee0f3b214dca6724d F ext/fts3/fts3_tokenizer.c 055f3dc7369585350b28db1ee0f3b214dca6724d
F ext/fts3/fts3_tokenizer.h 13ffd9fcb397fec32a05ef5cd9e0fa659bf3dbd3 F ext/fts3/fts3_tokenizer.h 13ffd9fcb397fec32a05ef5cd9e0fa659bf3dbd3
F ext/fts3/fts3_tokenizer1.c 6e5cbaa588924ac578263a598e4fb9f5c9bb179d F ext/fts3/fts3_tokenizer1.c 6e5cbaa588924ac578263a598e4fb9f5c9bb179d
F ext/fts3/fts3_write.c 813495ed106eb9461044e3c0374f4db69b37eb09 F ext/fts3/fts3_write.c 7d6d904b89333448eb968fc82470a74985d0b61e
F ext/fts3/fts3speed.tcl b54caf6a18d38174f1a6e84219950d85e98bb1e9 F ext/fts3/fts3speed.tcl b54caf6a18d38174f1a6e84219950d85e98bb1e9
F ext/fts3/mkfts3amal.tcl 252ecb7fe6467854f2aa237bf2c390b74e71f100 F ext/fts3/mkfts3amal.tcl 252ecb7fe6467854f2aa237bf2c390b74e71f100
F ext/icu/README.txt bf8461d8cdc6b8f514c080e4e10dc3b2bbdfefa9 F ext/icu/README.txt bf8461d8cdc6b8f514c080e4e10dc3b2bbdfefa9
F ext/icu/icu.c eb9ae1d79046bd7871aa97ee6da51eb770134b5a F ext/icu/icu.c eb9ae1d79046bd7871aa97ee6da51eb770134b5a
F ext/icu/sqliteicu.h 728867a802baa5a96de7495e9689a8e01715ef37 F ext/icu/sqliteicu.h 728867a802baa5a96de7495e9689a8e01715ef37
F ext/rtree/README 6315c0d73ebf0ec40dedb5aa0e942bc8b54e3761 F ext/rtree/README 6315c0d73ebf0ec40dedb5aa0e942bc8b54e3761
F ext/rtree/rtree.c f5fa951eba03c41d292958064604a033021acdee F ext/rtree/rtree.c 829c6901a2b065ff93a68d431f9eaba8de7128e0
F ext/rtree/rtree.h 834dbcb82dc85b2481cde6a07cdadfddc99e9b9e F ext/rtree/rtree.h 834dbcb82dc85b2481cde6a07cdadfddc99e9b9e
F ext/rtree/rtree1.test dbd4250ac0ad367a262eb9676f7e3080b0368206 F ext/rtree/rtree1.test 28e1b8da4da98093ce3210187434dd760a8d89d8
F ext/rtree/rtree2.test acbb3a4ce0f4fbc2c304d2b4b784cfa161856bba F ext/rtree/rtree2.test acbb3a4ce0f4fbc2c304d2b4b784cfa161856bba
F ext/rtree/rtree3.test a494da55c30ee0bc9b01a91c80c81b387b22d2dc F ext/rtree/rtree3.test a494da55c30ee0bc9b01a91c80c81b387b22d2dc
F ext/rtree/rtree4.test 0061e6f464fd3dc6a79f82454c5a1c3dadbe42af F ext/rtree/rtree4.test 0061e6f464fd3dc6a79f82454c5a1c3dadbe42af
@ -111,7 +112,7 @@ F ext/session/sqlite3session.h 665f5591562e3c71eb3d0da26f1a1efae26f7bcf
F ext/session/test_session.c 311e5b9228374d0b5780448f289847ff1cf7d388 F ext/session/test_session.c 311e5b9228374d0b5780448f289847ff1cf7d388
F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x
F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8 F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8
F main.mk f942406cb7df55d1aec40a88a7ae399b730cd94f F main.mk 352002cedf9e754ec96cfe0a8d1688cdea343fea
F mkdll.sh 7d09b23c05d56532e9d44a50868eb4b12ff4f74a F mkdll.sh 7d09b23c05d56532e9d44a50868eb4b12ff4f74a
F mkextu.sh 416f9b7089d80e5590a29692c9d9280a10dbad9f F mkextu.sh 416f9b7089d80e5590a29692c9d9280a10dbad9f
F mkextw.sh 4123480947681d9b434a5e7b1ee08135abe409ac F mkextw.sh 4123480947681d9b434a5e7b1ee08135abe409ac
@ -143,12 +144,12 @@ F src/delete.c ad9fa1cbf91a83ec6990d0aecb7e21cd5ff07e71
F src/expr.c e3cf0957c6b8faaaf7386a3bc69e53c0dc9705be F src/expr.c e3cf0957c6b8faaaf7386a3bc69e53c0dc9705be
F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb
F src/fkey.c a43ba8a005fb5efd1deeee06853e3a6120d46a91 F src/fkey.c a43ba8a005fb5efd1deeee06853e3a6120d46a91
F src/func.c 3a8cb2fb2de3e3aed7f39106daf4878d9d17fcce F src/func.c b9117e40975245b8504cf3625d7e321d8d4b63dc
F src/global.c 02335177cf6946fe5525c6f0755cf181140debf3 F src/global.c 02335177cf6946fe5525c6f0755cf181140debf3
F src/hash.c 458488dcc159c301b8e7686280ab209f1fb915af F src/hash.c 458488dcc159c301b8e7686280ab209f1fb915af
F src/hash.h 2894c932d84d9f892d4b4023a75e501f83050970 F src/hash.h 2894c932d84d9f892d4b4023a75e501f83050970
F src/hwtime.h d32741c8f4df852c7d959236615444e2b1063b08 F src/hwtime.h d32741c8f4df852c7d959236615444e2b1063b08
F src/insert.c 8796ca3f9209b699cb8120fc44290fc97ac26abe F src/insert.c 1f1688a9da8b8e27114ba1909a6e74100791139b
F src/journal.c 552839e54d1bf76fb8f7abe51868b66acacf6a0e F src/journal.c 552839e54d1bf76fb8f7abe51868b66acacf6a0e
F src/legacy.c a199d7683d60cef73089e892409113e69c23a99f F src/legacy.c a199d7683d60cef73089e892409113e69c23a99f
F src/lempar.c 7f026423f4d71d989e719a743f98a1cbd4e6d99e F src/lempar.c 7f026423f4d71d989e719a743f98a1cbd4e6d99e
@ -172,8 +173,8 @@ F src/os.c 22ac61d06e72a0dac900400147333b07b13d8e1d
F src/os.h 9dbed8c2b9c1f2f2ebabc09e49829d4777c26bf9 F src/os.h 9dbed8c2b9c1f2f2ebabc09e49829d4777c26bf9
F src/os_common.h a8f95b81eca8a1ab8593d23e94f8a35f35d4078f F src/os_common.h a8f95b81eca8a1ab8593d23e94f8a35f35d4078f
F src/os_os2.c 4a75888ba3dfc820ad5e8177025972d74d7f2440 F src/os_os2.c 4a75888ba3dfc820ad5e8177025972d74d7f2440
F src/os_unix.c d7889a0f9389c8c2e1d3b380f5aa1256c22a90e8 F src/os_unix.c 2c67d126874b78eb427371db4793f0e8fbc7448b
F src/os_win.c d149b9a7dfdd38de09afc054f8168cd3cd80630b F src/os_win.c 4271f0bf733c0b45635ddcfb41c935573de8284c
F src/pager.c 055239dcdfe12b3f5d97f6f01f85da01e2d6d912 F src/pager.c 055239dcdfe12b3f5d97f6f01f85da01e2d6d912
F src/pager.h 3f8c783de1d4706b40b1ac15b64f5f896bcc78d1 F src/pager.h 3f8c783de1d4706b40b1ac15b64f5f896bcc78d1
F src/parse.y 12b7ebd61ea54f0e1b1083ff69cc2c8ce9353d58 F src/parse.y 12b7ebd61ea54f0e1b1083ff69cc2c8ce9353d58
@ -188,14 +189,14 @@ F src/resolve.c 1c0f32b64f8e3f555fe1f732f9d6f501a7f05706
F src/rowset.c 69afa95a97c524ba6faf3805e717b5b7ae85a697 F src/rowset.c 69afa95a97c524ba6faf3805e717b5b7ae85a697
F src/select.c d9d440809025a58547e39f4f268c2a296bfb56ff F src/select.c d9d440809025a58547e39f4f268c2a296bfb56ff
F src/shell.c 72e7e176bf46d5c6518d15ac4ad6847c4bb5df79 F src/shell.c 72e7e176bf46d5c6518d15ac4ad6847c4bb5df79
F src/sqlite.h.in 0cf61c41c48e1e6b863ff8cf9ecd69620932330e F src/sqlite.h.in 628de30f6063695288eadf34c167e49bc34c9828
F src/sqlite3ext.h c90bd5507099f62043832d73f6425d8d5c5da754 F src/sqlite3ext.h c90bd5507099f62043832d73f6425d8d5c5da754
F src/sqliteInt.h 9a29e5bb82f3abef6b4af91e18d637050fa3c883 F src/sqliteInt.h 1577ac69cb67a1dc0c07974a4e5129b1cde039a3
F src/sqliteLimit.h 164b0e6749d31e0daa1a4589a169d31c0dec7b3d F src/sqliteLimit.h 164b0e6749d31e0daa1a4589a169d31c0dec7b3d
F src/status.c 7ac64842c86cec2fc1a1d0e5c16d3beb8ad332bf F src/status.c 7ac64842c86cec2fc1a1d0e5c16d3beb8ad332bf
F src/table.c 2cd62736f845d82200acfa1287e33feb3c15d62e F src/table.c 2cd62736f845d82200acfa1287e33feb3c15d62e
F src/tclsqlite.c fe0da0eb0ebd8d21eec90683b779456e64351de6 F src/tclsqlite.c fe0da0eb0ebd8d21eec90683b779456e64351de6
F src/test1.c 9ca440e80e16e53920904a0a5ac7feffb9b2c9a1 F src/test1.c e0e4af306b678da05334c2ccaf0377ae8f06e911
F src/test2.c 80d323d11e909cf0eb1b6fbb4ac22276483bcf31 F src/test2.c 80d323d11e909cf0eb1b6fbb4ac22276483bcf31
F src/test3.c 056093cfef69ff4227a6bdb9108564dc7f45e4bc F src/test3.c 056093cfef69ff4227a6bdb9108564dc7f45e4bc
F src/test4.c d1e5a5e904d4b444cf572391fdcb017638e36ff7 F src/test4.c d1e5a5e904d4b444cf572391fdcb017638e36ff7
@ -209,7 +210,7 @@ F src/test_autoext.c 30e7bd98ab6d70a62bb9ba572e4c7df347fe645e
F src/test_backup.c c129c91127e9b46e335715ae2e75756e25ba27de F src/test_backup.c c129c91127e9b46e335715ae2e75756e25ba27de
F src/test_btree.c 47cd771250f09cdc6e12dda5bc71bc0b3abc96e2 F src/test_btree.c 47cd771250f09cdc6e12dda5bc71bc0b3abc96e2
F src/test_config.c 25a4128c2dc9e1dbebafcb7e8c61d45f09f7fbc3 F src/test_config.c 25a4128c2dc9e1dbebafcb7e8c61d45f09f7fbc3
F src/test_demovfs.c 31050680fa6925b4f677cfd4fa965b5f19195e50 F src/test_demovfs.c 938d0f595f8bd310076e1c06cf7885a01ce7ce01
F src/test_devsym.c e7498904e72ba7491d142d5c83b476c4e76993bc F src/test_devsym.c e7498904e72ba7491d142d5c83b476c4e76993bc
F src/test_func.c cbdec5cededa0761daedde5baf06004a9bf416b5 F src/test_func.c cbdec5cededa0761daedde5baf06004a9bf416b5
F src/test_fuzzer.c f884f6f32e8513d34248d6e1ac8a32047fead254 F src/test_fuzzer.c f884f6f32e8513d34248d6e1ac8a32047fead254
@ -241,19 +242,19 @@ F src/test_wholenumber.c 6129adfbe7c7444f2e60cc785927f3aa74e12290
F src/test_wsd.c 41cadfd9d97fe8e3e4e44f61a4a8ccd6f7ca8fe9 F src/test_wsd.c 41cadfd9d97fe8e3e4e44f61a4a8ccd6f7ca8fe9
F src/tokenize.c 604607d6813e9551cf5189d899e0a25c12681080 F src/tokenize.c 604607d6813e9551cf5189d899e0a25c12681080
F src/trigger.c 144cc18bb701f3286484aae4292a9531f09278c8 F src/trigger.c 144cc18bb701f3286484aae4292a9531f09278c8
F src/update.c 3f3f3bb734a0da1dffd0ed33e504642b35ed3605 F src/update.c f66b651c15e42875f36501ec39a968e836ee5586
F src/utf.c d83650c3ea08f7407bd9d0839d9885241c209c60 F src/utf.c d83650c3ea08f7407bd9d0839d9885241c209c60
F src/util.c 465fe10aabf0ca7d7826a156dab919b0b65c525a F src/util.c 465fe10aabf0ca7d7826a156dab919b0b65c525a
F src/vacuum.c 05513dca036a1e7848fe18d5ed1265ac0b32365e F src/vacuum.c 05513dca036a1e7848fe18d5ed1265ac0b32365e
F src/vdbe.c dd53dda1cf786397e72643a497b5c2f368ff11ba F src/vdbe.c fa5bfc0c820706a1f78042b134a0f7a388ca9a3e
F src/vdbe.h 44fd57aeed86da0cd31206626c13cdde0e72cc0e F src/vdbe.h 44fd57aeed86da0cd31206626c13cdde0e72cc0e
F src/vdbeInt.h b95de01246c15499c700ae00cfda0de25c01358a F src/vdbeInt.h b95de01246c15499c700ae00cfda0de25c01358a
F src/vdbeapi.c 8051038f7674c708f4515ab189fc3ea929e09a4c F src/vdbeapi.c 8051038f7674c708f4515ab189fc3ea929e09a4c
F src/vdbeaux.c b0a2a184a25380f7eb9d07e9336034ec38d1b213 F src/vdbeaux.c cc95d80b899b75829cb93d260d3f14125a5c26ad
F src/vdbeblob.c c3ccb7c8732858c680f442932e66ad06bb036562 F src/vdbeblob.c c3ccb7c8732858c680f442932e66ad06bb036562
F src/vdbemem.c 0498796b6ffbe45e32960d6a1f5adfb6e419883b F src/vdbemem.c 0498796b6ffbe45e32960d6a1f5adfb6e419883b
F src/vdbetrace.c 5d0dc3d5fd54878cc8d6d28eb41deb8d5885b114 F src/vdbetrace.c 5d0dc3d5fd54878cc8d6d28eb41deb8d5885b114
F src/vtab.c b0abc931f95af94c9ffdf9f747eb191cda953123 F src/vtab.c 1491acb3e0a67eafe69134fb65bfa0b7b7e82342
F src/wal.c 7334009b396285b658a95a3b6bc6d2b016a1f794 F src/wal.c 7334009b396285b658a95a3b6bc6d2b016a1f794
F src/wal.h 7a5fbb00114b7f2cd40c7e1003d4c41ce9d26840 F src/wal.h 7a5fbb00114b7f2cd40c7e1003d4c41ce9d26840
F src/walker.c 3112bb3afe1d85dc52317cb1d752055e9a781f8f F src/walker.c 3112bb3afe1d85dc52317cb1d752055e9a781f8f
@ -375,7 +376,7 @@ F test/descidx2.test 9f1a0c83fd57f8667c82310ca21b30a350888b5d
F test/descidx3.test fe720e8b37d59f4cef808b0bf4e1b391c2e56b6f F test/descidx3.test fe720e8b37d59f4cef808b0bf4e1b391c2e56b6f
F test/diskfull.test 0cede7ef9d8f415d9d3944005c76be7589bb5ebb F test/diskfull.test 0cede7ef9d8f415d9d3944005c76be7589bb5ebb
F test/distinctagg.test 1a6ef9c87a58669438fc771450d7a72577417376 F test/distinctagg.test 1a6ef9c87a58669438fc771450d7a72577417376
F test/e_createtable.test b40fc61bc4f1ad2a3c84590bd1d711507263d921 F test/e_createtable.test 4771686a586b6ae414f927c389b2c101cc05c028
F test/e_delete.test 55d868b647acc091c261a10b9b0cb0ab660a6acb F test/e_delete.test 55d868b647acc091c261a10b9b0cb0ab660a6acb
F test/e_droptrigger.test ddd4b28ed8a3d81bd5153fa0ab7559529a2ca03a F test/e_droptrigger.test ddd4b28ed8a3d81bd5153fa0ab7559529a2ca03a
F test/e_dropview.test b347bab30fc8de67b131594b3cd6f3d3bdaa753d F test/e_dropview.test b347bab30fc8de67b131594b3cd6f3d3bdaa753d
@ -460,11 +461,12 @@ F test/fts3al.test 07d64326e79bbdbab20ee87fc3328fbf01641c9f
F test/fts3am.test 218aa6ba0dfc50c7c16b2022aac5c6be593d08d8 F test/fts3am.test 218aa6ba0dfc50c7c16b2022aac5c6be593d08d8
F test/fts3an.test a49ccadc07a2f7d646ec1b81bc09da2d85a85b18 F test/fts3an.test a49ccadc07a2f7d646ec1b81bc09da2d85a85b18
F test/fts3ao.test b83f99f70e9eec85f27d75801a974b3f820e01f9 F test/fts3ao.test b83f99f70e9eec85f27d75801a974b3f820e01f9
F test/fts3atoken.test 25c2070e1e8755d414bf9c8200427b277a9f99fa F test/fts3atoken.test 402ef2f7c2fb4b3d4fa0587df6441c1447e799b3
F test/fts3aux1.test 719c35cbbcc04dde8e5a54a6f69851a0af9ed1f2 F test/fts3aux1.test 0b02743955d56fc0d4d66236a26177bd1b726de0
F test/fts3b.test e93bbb653e52afde110ad53bbd793f14fe7a8984 F test/fts3b.test e93bbb653e52afde110ad53bbd793f14fe7a8984
F test/fts3c.test fc723a9cf10b397fdfc2b32e73c53c8b1ec02958 F test/fts3c.test fc723a9cf10b397fdfc2b32e73c53c8b1ec02958
F test/fts3comp1.test a0f5b16a2df44dd0b15751787130af2183167c0c F test/fts3comp1.test a0f5b16a2df44dd0b15751787130af2183167c0c
F test/fts3conf.test 8e65ea56f88ced6cdd2252bdddb1a8327ae5af7e
F test/fts3corrupt.test 7890cc202406858386ddf390a879dcf80bc10abf F test/fts3corrupt.test 7890cc202406858386ddf390a879dcf80bc10abf
F test/fts3corrupt2.test 6d96efae2f8a6af3eeaf283aba437e6d0e5447ba F test/fts3corrupt2.test 6d96efae2f8a6af3eeaf283aba437e6d0e5447ba
F test/fts3cov.test e0fb00d8b715ddae4a94c305992dfc3ef70353d7 F test/fts3cov.test e0fb00d8b715ddae4a94c305992dfc3ef70353d7
@ -483,6 +485,7 @@ F test/fts3query.test ef79d31fdb355d094baec1c1b24b60439a1fb8a2
F test/fts3rnd.test 2b1a579be557ab8ac54a51b39caa4aa8043cc4ad F test/fts3rnd.test 2b1a579be557ab8ac54a51b39caa4aa8043cc4ad
F test/fts3shared.test 8bb266521d7c5495c0ae522bb4d376ad5387d4a2 F test/fts3shared.test 8bb266521d7c5495c0ae522bb4d376ad5387d4a2
F test/fts3snippet.test a12f22a3ba4dd59751a57c79b031d07ab5f51ddd F test/fts3snippet.test a12f22a3ba4dd59751a57c79b031d07ab5f51ddd
F test/fts3sort.test e6f24e9cffc46484bcc9fe63d3c2ce41afcaa6c9
F test/fts4aa.test eadf85621c0a113d4c7ad3ccbf8441130e007b8f F test/fts4aa.test eadf85621c0a113d4c7ad3ccbf8441130e007b8f
F test/func.test 6c5ce11e3a0021ca3c0649234e2d4454c89110ca F test/func.test 6c5ce11e3a0021ca3c0649234e2d4454c89110ca
F test/func2.test 772d66227e4e6684b86053302e2d74a2500e1e0f F test/func2.test 772d66227e4e6684b86053302e2d74a2500e1e0f
@ -515,7 +518,7 @@ F test/init.test 15c823093fdabbf7b531fe22cf037134d09587a7
F test/insert.test aef273dd1cee84cc92407469e6bd1b3cdcb76908 F test/insert.test aef273dd1cee84cc92407469e6bd1b3cdcb76908
F test/insert2.test 4f3a04d168c728ed5ec2c88842e772606c7ce435 F test/insert2.test 4f3a04d168c728ed5ec2c88842e772606c7ce435
F test/insert3.test 1b7db95a03ad9c5013fdf7d6722b6cd66ee55e30 F test/insert3.test 1b7db95a03ad9c5013fdf7d6722b6cd66ee55e30
F test/insert4.test c1469999a58e86a85b74df645a820f4cc7a8273b F test/insert4.test b3e02648a5fc3075c29e13c369b5127bf859b5a2
F test/insert5.test 1f93cbe9742110119133d7e8e3ccfe6d7c249766 F test/insert5.test 1f93cbe9742110119133d7e8e3ccfe6d7c249766
F test/intarray.test 066b7d7ac38d25bf96f87f1b017bfc687551cdd4 F test/intarray.test 066b7d7ac38d25bf96f87f1b017bfc687551cdd4
F test/interrupt.test 42e7cf98646fd9cb4a3b131a93ed3c50b9e149f1 F test/interrupt.test 42e7cf98646fd9cb4a3b131a93ed3c50b9e149f1
@ -697,7 +700,7 @@ F test/tclsqlite.test 1ce9b6340d6d412420634e129a2e3722c651056a
F test/tempdb.test 19d0f66e2e3eeffd68661a11c83ba5e6ace9128c F test/tempdb.test 19d0f66e2e3eeffd68661a11c83ba5e6ace9128c
F test/temptable.test f42121a0d29a62f00f93274464164177ab1cc24a F test/temptable.test f42121a0d29a62f00f93274464164177ab1cc24a
F test/temptrigger.test b0273db072ce5f37cf19140ceb1f0d524bbe9f05 F test/temptrigger.test b0273db072ce5f37cf19140ceb1f0d524bbe9f05
F test/tester.tcl fc2e27b3a782c5c5b03f7cded15cf498cad9bfef F test/tester.tcl cf301cdb35f20378b086849acd26a4574187183f
F test/thread001.test a3e6a7254d1cb057836cb3145b60c10bf5b7e60f F test/thread001.test a3e6a7254d1cb057836cb3145b60c10bf5b7e60f
F test/thread002.test afd20095e6e845b405df4f2c920cb93301ca69db F test/thread002.test afd20095e6e845b405df4f2c920cb93301ca69db
F test/thread003.test b824d4f52b870ae39fc5bae4d8070eca73085dca F test/thread003.test b824d4f52b870ae39fc5bae4d8070eca73085dca
@ -910,6 +913,7 @@ F tool/diffdb.c 7524b1b5df217c20cd0431f6789851a4e0cb191b
F tool/fragck.tcl 5265a95126abcf6ab357f7efa544787e5963f439 F tool/fragck.tcl 5265a95126abcf6ab357f7efa544787e5963f439
F tool/genfkey.README cf68fddd4643bbe3ff8e31b8b6d8b0a1b85e20f4 F tool/genfkey.README cf68fddd4643bbe3ff8e31b8b6d8b0a1b85e20f4
F tool/genfkey.test 4196a8928b78f51d54ef58e99e99401ab2f0a7e5 F tool/genfkey.test 4196a8928b78f51d54ef58e99e99401ab2f0a7e5
F tool/getlock.c f4c39b651370156cae979501a7b156bdba50e7ce
F tool/lemon.c dfd81a51b6e27e469ba21d01a75ddf092d429027 F tool/lemon.c dfd81a51b6e27e469ba21d01a75ddf092d429027
F tool/lempar.c 01ca97f87610d1dac6d8cd96ab109ab1130e76dc F tool/lempar.c 01ca97f87610d1dac6d8cd96ab109ab1130e76dc
F tool/mkkeywordhash.c d2e6b4a5965e23afb80fbe74bb54648cd371f309 F tool/mkkeywordhash.c d2e6b4a5965e23afb80fbe74bb54648cd371f309
@ -922,7 +926,7 @@ F tool/omittest.tcl b1dd290c1596e0f31fd335160a74ec5dfea3df4a
F tool/opcodeDoc.awk b3a2a3d5d3075b8bd90b7afe24283efdd586659c F tool/opcodeDoc.awk b3a2a3d5d3075b8bd90b7afe24283efdd586659c
F tool/restore_jrnl.tcl 6957a34f8f1f0f8285e07536225ec3b292a9024a F tool/restore_jrnl.tcl 6957a34f8f1f0f8285e07536225ec3b292a9024a
F tool/rollback-test.c 9fc98427d1e23e84429d7e6d07d9094fbdec65a5 F tool/rollback-test.c 9fc98427d1e23e84429d7e6d07d9094fbdec65a5
F tool/shell1.test fee04fce1bf55e6e081523545fd4e0e83490ff8e F tool/shell1.test 5542ecdc952f91121a835ed817e6feaf8988b333
F tool/shell2.test 5dc76b8005b465f420fed8241621da7513060ff3 F tool/shell2.test 5dc76b8005b465f420fed8241621da7513060ff3
F tool/shell3.test 4fad469e8003938426355afdf34155f08c587836 F tool/shell3.test 4fad469e8003938426355afdf34155f08c587836
F tool/shell4.test 35f9c3d452b4e76d5013c63e1fd07478a62f14ce F tool/shell4.test 35f9c3d452b4e76d5013c63e1fd07478a62f14ce
@ -940,7 +944,7 @@ F tool/speedtest8.c 2902c46588c40b55661e471d7a86e4dd71a18224
F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e
F tool/split-sqlite3c.tcl d9be87f1c340285a3e081eb19b4a247981ed290c F tool/split-sqlite3c.tcl d9be87f1c340285a3e081eb19b4a247981ed290c
F tool/vdbe-compress.tcl d70ea6d8a19e3571d7ab8c9b75cba86d1173ff0f F tool/vdbe-compress.tcl d70ea6d8a19e3571d7ab8c9b75cba86d1173ff0f
P 99f0f35092b0b78b7016b21c242da263ab64b77b 3e135748f1efacb52b414b3ac3f4ae2c08bcd8fb P b91b4c31fe311b292044c9c747feba294ffce25c 930be6a1bdec8c150caafd790973f7a401fc1970
R b2692e50ee7820b096983b9aa33c1efd R 13217a0634732c3fc1250bf1a2936d3d
U dan U drh
Z c7fa020efb14904762c294d2eedf7456 Z f29377c78641e183f6acae5b288b38b3

View File

@ -1 +1 @@
b91b4c31fe311b292044c9c747feba294ffce25c 6883580e6c8973010a42d1d2c5bde04c6b2f4eb7

View File

@ -774,6 +774,21 @@ static void sourceidFunc(
sqlite3_result_text(context, sqlite3_sourceid(), -1, SQLITE_STATIC); sqlite3_result_text(context, sqlite3_sourceid(), -1, SQLITE_STATIC);
} }
/*
** Implementation of the sqlite_log() function. This is a wrapper around
** sqlite3_log(). The return value is NULL. The function exists purely for
** its side-effects.
*/
static void errlogFunc(
sqlite3_context *context,
int argc,
sqlite3_value **argv
){
UNUSED_PARAMETER(argc);
UNUSED_PARAMETER(context);
sqlite3_log(sqlite3_value_int(argv[0]), "%s", sqlite3_value_text(argv[1]));
}
/* /*
** Implementation of the sqlite_compileoption_used() function. ** Implementation of the sqlite_compileoption_used() function.
** The result is an integer that identifies if the compiler option ** The result is an integer that identifies if the compiler option
@ -1541,6 +1556,7 @@ void sqlite3RegisterGlobalFunctions(void){
FUNCTION(nullif, 2, 0, 1, nullifFunc ), FUNCTION(nullif, 2, 0, 1, nullifFunc ),
FUNCTION(sqlite_version, 0, 0, 0, versionFunc ), FUNCTION(sqlite_version, 0, 0, 0, versionFunc ),
FUNCTION(sqlite_source_id, 0, 0, 0, sourceidFunc ), FUNCTION(sqlite_source_id, 0, 0, 0, sourceidFunc ),
FUNCTION(sqlite_log, 2, 0, 0, errlogFunc ),
#ifndef SQLITE_OMIT_COMPILEOPTION_DIAGS #ifndef SQLITE_OMIT_COMPILEOPTION_DIAGS
FUNCTION(sqlite_compileoption_used,1, 0, 0, compileoptionusedFunc ), FUNCTION(sqlite_compileoption_used,1, 0, 0, compileoptionusedFunc ),
FUNCTION(sqlite_compileoption_get, 1, 0, 0, compileoptiongetFunc ), FUNCTION(sqlite_compileoption_get, 1, 0, 0, compileoptiongetFunc ),

View File

@ -969,6 +969,7 @@ void sqlite3Insert(
const char *pVTab = (const char *)sqlite3GetVTable(db, pTab); const char *pVTab = (const char *)sqlite3GetVTable(db, pTab);
sqlite3VtabMakeWritable(pParse, pTab); sqlite3VtabMakeWritable(pParse, pTab);
sqlite3VdbeAddOp4(v, OP_VUpdate, 1, pTab->nCol+2, regIns, pVTab, P4_VTAB); sqlite3VdbeAddOp4(v, OP_VUpdate, 1, pTab->nCol+2, regIns, pVTab, P4_VTAB);
sqlite3VdbeChangeP5(v, onError==OE_Default ? OE_Abort : onError);
sqlite3MayAbort(pParse); sqlite3MayAbort(pParse);
}else }else
#endif #endif
@ -1744,6 +1745,18 @@ static int xferOptimization(
return 0; /* Tables have different CHECK constraints. Ticket #2252 */ return 0; /* Tables have different CHECK constraints. Ticket #2252 */
} }
#endif #endif
#ifndef SQLITE_OMIT_FOREIGN_KEY
/* Disallow the transfer optimization if the destination table constains
** any foreign key constraints. This is more restrictive than necessary.
** But the main beneficiary of the transfer optimization is the VACUUM
** command, and the VACUUM command disables foreign key constraints. So
** the extra complication to make this rule less restrictive is probably
** not worth the effort. Ticket [6284df89debdfa61db8073e062908af0c9b6118e]
*/
if( (pParse->db->flags & SQLITE_ForeignKeys)!=0 && pDest->pFKey!=0 ){
return 0;
}
#endif
/* If we get this far, it means either: /* If we get this far, it means either:
** **

View File

@ -281,6 +281,18 @@ struct unixFile {
#define threadid 0 #define threadid 0
#endif #endif
/*
** Different Unix systems declare open() in different ways. Same use
** open(const char*,int,mode_t). Others use open(const char*,int,...).
** The difference is important when using a pointer to the function.
**
** The safest way to deal with the problem is to always use this wrapper
** which always has the same well-defined interface.
*/
static int posixOpen(const char *zFile, int flags, int mode){
return open(zFile, flags, mode);
}
/* /*
** Many system calls are accessed through pointer-to-functions so that ** Many system calls are accessed through pointer-to-functions so that
** they may be overridden at runtime to facilitate fault injection during ** they may be overridden at runtime to facilitate fault injection during
@ -292,8 +304,8 @@ static struct unix_syscall {
sqlite3_syscall_ptr pCurrent; /* Current value of the system call */ sqlite3_syscall_ptr pCurrent; /* Current value of the system call */
sqlite3_syscall_ptr pDefault; /* Default value */ sqlite3_syscall_ptr pDefault; /* Default value */
} aSyscall[] = { } aSyscall[] = {
{ "open", (sqlite3_syscall_ptr)open, 0 }, { "open", (sqlite3_syscall_ptr)posixOpen, 0 },
#define osOpen ((int(*)(const char*,int,...))aSyscall[0].pCurrent) #define osOpen ((int(*)(const char*,int,int))aSyscall[0].pCurrent)
{ "close", (sqlite3_syscall_ptr)close, 0 }, { "close", (sqlite3_syscall_ptr)close, 0 },
#define osClose ((int(*)(int))aSyscall[1].pCurrent) #define osClose ((int(*)(int))aSyscall[1].pCurrent)

View File

@ -286,7 +286,7 @@ char *sqlite3_win32_mbcs_to_utf8(const char *zFilename){
** Convert UTF-8 to multibyte character string. Space to hold the ** Convert UTF-8 to multibyte character string. Space to hold the
** returned string is obtained from malloc(). ** returned string is obtained from malloc().
*/ */
static char *utf8ToMbcs(const char *zFilename){ char *sqlite3_win32_utf8_to_mbcs(const char *zFilename){
char *zFilenameMbcs; char *zFilenameMbcs;
WCHAR *zTmpWide; WCHAR *zTmpWide;
@ -1052,7 +1052,7 @@ static int unlockReadLock(winFile *pFile){
res = UnlockFile(pFile->h, SHARED_FIRST + pFile->sharedLockByte, 0, 1, 0); res = UnlockFile(pFile->h, SHARED_FIRST + pFile->sharedLockByte, 0, 1, 0);
#endif #endif
} }
if( res == 0 ){ if( res==0 && GetLastError()!=ERROR_NOT_LOCKED ){
pFile->lastErrno = GetLastError(); pFile->lastErrno = GetLastError();
winLogError(SQLITE_IOERR_UNLOCK, "unlockReadLock", pFile->zPath); winLogError(SQLITE_IOERR_UNLOCK, "unlockReadLock", pFile->zPath);
} }
@ -2004,7 +2004,7 @@ static void *convertUtf8Filename(const char *zFilename){
*/ */
#if SQLITE_OS_WINCE==0 #if SQLITE_OS_WINCE==0
}else{ }else{
zConverted = utf8ToMbcs(zFilename); zConverted = sqlite3_win32_utf8_to_mbcs(zFilename);
#endif #endif
} }
/* caller will handle out of memory */ /* caller will handle out of memory */

View File

@ -370,7 +370,8 @@ int sqlite3_exec(
** **
** New error codes may be added in future versions of SQLite. ** New error codes may be added in future versions of SQLite.
** **
** See also: [SQLITE_IOERR_READ | extended result codes] ** See also: [SQLITE_IOERR_READ | extended result codes],
** [sqlite3_vtab_on_conflict()] [SQLITE_ROLLBACK | result codes].
*/ */
#define SQLITE_OK 0 /* Successful result */ #define SQLITE_OK 0 /* Successful result */
/* beginning-of-error-codes */ /* beginning-of-error-codes */
@ -2198,6 +2199,9 @@ int sqlite3_set_authorizer(
** to signal SQLite whether or not the action is permitted. See the ** to signal SQLite whether or not the action is permitted. See the
** [sqlite3_set_authorizer | authorizer documentation] for additional ** [sqlite3_set_authorizer | authorizer documentation] for additional
** information. ** information.
**
** Note that SQLITE_IGNORE is also used as a [SQLITE_ROLLBACK | return code]
** from the [sqlite3_vtab_on_conflict()] interface.
*/ */
#define SQLITE_DENY 1 /* Abort the SQL statement with an error */ #define SQLITE_DENY 1 /* Abort the SQL statement with an error */
#define SQLITE_IGNORE 2 /* Don't allow access, but don't generate an error */ #define SQLITE_IGNORE 2 /* Don't allow access, but don't generate an error */
@ -4607,6 +4611,11 @@ struct sqlite3_module {
void (**pxFunc)(sqlite3_context*,int,sqlite3_value**), void (**pxFunc)(sqlite3_context*,int,sqlite3_value**),
void **ppArg); void **ppArg);
int (*xRename)(sqlite3_vtab *pVtab, const char *zNew); int (*xRename)(sqlite3_vtab *pVtab, const char *zNew);
/* The methods above are in version 0 of the sqlite_module object. Those
** below are for version 1 and greater. */
int (*xSavepoint)(sqlite3_vtab *pVTab, int);
int (*xRelease)(sqlite3_vtab *pVTab, int);
int (*xRollbackTo)(sqlite3_vtab *pVTab, int);
}; };
/* /*
@ -6384,11 +6393,102 @@ int sqlite3_wal_checkpoint_v2(
** [sqlite3_wal_checkpoint_v2()]. See the [sqlite3_wal_checkpoint_v2()] ** [sqlite3_wal_checkpoint_v2()]. See the [sqlite3_wal_checkpoint_v2()]
** documentation for additional information about the meaning and use of ** documentation for additional information about the meaning and use of
** each of these values. ** each of these values.
**
** <dt>SQLITE_CONFIG_GETMUTEX</dt>
** <dd> ^(This option takes a single argument which is a pointer to an
*/ */
#define SQLITE_CHECKPOINT_PASSIVE 0 #define SQLITE_CHECKPOINT_PASSIVE 0
#define SQLITE_CHECKPOINT_FULL 1 #define SQLITE_CHECKPOINT_FULL 1
#define SQLITE_CHECKPOINT_RESTART 2 #define SQLITE_CHECKPOINT_RESTART 2
/*
** CAPI3REF: Virtual Table Interface Configuration
**
** This function may be called by either the [xConnect] or [xCreate] method
** of a [virtual table] implementation to configure
** various facets of the virtual table interface.
**
** If this interface is invoked outside the context of an xConnect or
** xCreate virtual table method then the behavior is undefined.
**
** At present, there is only one option that may be configured using
** this function. (See [SQLITE_VTAB_CONSTRAINT_SUPPORT].) Further options
** may be added in the future.
*/
int sqlite3_vtab_config(sqlite3*, int op, ...);
/*
** CAPI3REF: Virtual Table Configuration Options
**
** These macros define the various options to the
** [sqlite3_vtab_config()] interface that [virtual table] implementations
** can use to customize and optimize their behavior.
**
** <dl>
** <dt>SQLITE_VTAB_CONSTRAINT_SUPPORT
** <dd>If the second argument to [sqlite3_vtab_config()] is
** SQLITE_VTAB_CONSTRAINT_SUPPORT, then SQLite expects this function to
** have been called with three arguments, the third of which being of
** type 'int'. If the third argument is zero, then the virtual table
** is indicating that it does not support constraints. In this case if
** a call to the [xUpdate] method returns [SQLITE_CONSTRAINT], the entire
** statement is rolled back as if [ON CONFLICT | OR ABORT] had been
** specified as part of the users SQL statement, regardless of the actual
** ON CONFLICT mode specified.
**
** If the third argument passed is non-zero, then the virtual table
** implementation must guarantee that if [xUpdate] returns
** [SQLITE_CONSTRAINT], it does so before any modifications to internal
** or persistent data structures have been made. If the [ON CONFLICT]
** mode is ABORT, FAIL, IGNORE or ROLLBACK, SQLite is able to roll back
** a statement or database transaction, and abandon or continue processing
** the current SQL statement as appropriate. If the ON CONFLICT mode is
** REPLACE and the [xUpdate] method returns [SQLITE_CONSTRAINT], SQLite
** handles this as if the ON CONFLICT mode had been ABORT.
**
** Virtual table implementations that are required to handle OR REPLACE
** must do so within the xUpdate method. If a call to the
** [sqlite3_vtab_on_conflict()] function indicates that the current ON
** CONFLICT policy is REPLACE, the virtual table implementation should
** silently replace the appropriate rows within the xUpdate callback and
** return SQLITE_OK. Or, if this is not possible, it may return
** SQLITE_CONSTRAINT, in which case SQLite falls back to OR ABORT
** constraint handling.
** </dl>
**
*/
#define SQLITE_VTAB_CONSTRAINT_SUPPORT 1
/*
** CAPI3REF: Determine The Virtual Table Conflict Policy
**
** This function may only be called from within a call to the [xUpdate] method
** of a [virtual table] implementation for an INSERT or UPDATE operation. ^The
** value returned is one of [SQLITE_ROLLBACK], [SQLITE_IGNORE], [SQLITE_FAIL],
** [SQLITE_ABORT], or [SQLITE_REPLACE], according to the [ON CONFLICT] mode
** of the SQL statement that triggered the call to the [xUpdate] method of the
** [virtual table].
*/
int sqlite3_vtab_on_conflict(sqlite3 *);
/*
** CAPI3REF: Conflict resolution modes
**
** These constants are returned by [sqlite3_vtab_on_conflict()] to
** inform a [virtual table] implementation what the [ON CONFLICT] mode
** is for the SQL statement being evaluated.
**
** Note that the [SQLITE_IGNORE] constant is also used as a potential
** return value from the [sqlite3_set_authorizer()] callback and that
** [SQLITE_ABORT] is also a [result code].
*/
#define SQLITE_ROLLBACK 1
/* #define SQLITE_IGNORE 2 // Also used by sqlite3_authorizer() callback */
#define SQLITE_FAIL 3
/* #define SQLITE_ABORT 4 // Also an error code */
#define SQLITE_REPLACE 5
/* /*
** CAPI3REF: The pre-update hook. ** CAPI3REF: The pre-update hook.

View File

@ -633,6 +633,7 @@ typedef struct TriggerPrg TriggerPrg;
typedef struct TriggerStep TriggerStep; typedef struct TriggerStep TriggerStep;
typedef struct UnpackedRecord UnpackedRecord; typedef struct UnpackedRecord UnpackedRecord;
typedef struct VTable VTable; typedef struct VTable VTable;
typedef struct VtabCtx VtabCtx;
typedef struct Walker Walker; typedef struct Walker Walker;
typedef struct WherePlan WherePlan; typedef struct WherePlan WherePlan;
typedef struct WhereInfo WhereInfo; typedef struct WhereInfo WhereInfo;
@ -812,6 +813,7 @@ struct sqlite3 {
u8 dfltLockMode; /* Default locking-mode for attached dbs */ u8 dfltLockMode; /* Default locking-mode for attached dbs */
signed char nextAutovac; /* Autovac setting after VACUUM if >=0 */ signed char nextAutovac; /* Autovac setting after VACUUM if >=0 */
u8 suppressErr; /* Do not issue error messages if true */ u8 suppressErr; /* Do not issue error messages if true */
u8 vtabOnConflict; /* Value to return for s3_vtab_on_conflict() */
int nextPagesize; /* Pagesize after VACUUM if >0 */ int nextPagesize; /* Pagesize after VACUUM if >0 */
int nTable; /* Number of tables in the database */ int nTable; /* Number of tables in the database */
CollSeq *pDfltColl; /* The default collating sequence (BINARY) */ CollSeq *pDfltColl; /* The default collating sequence (BINARY) */
@ -877,7 +879,7 @@ struct sqlite3 {
#endif #endif
#ifndef SQLITE_OMIT_VIRTUALTABLE #ifndef SQLITE_OMIT_VIRTUALTABLE
Hash aModule; /* populated by sqlite3_create_module() */ Hash aModule; /* populated by sqlite3_create_module() */
Table *pVTab; /* vtab with active Connect/Create method */ VtabCtx *pVtabCtx; /* Context for active vtab connect/create */
VTable **aVTrans; /* Virtual tables with open transactions */ VTable **aVTrans; /* Virtual tables with open transactions */
int nVTrans; /* Allocated size of aVTrans */ int nVTrans; /* Allocated size of aVTrans */
VTable *pDisconnect; /* Disconnect these in next sqlite3_prepare() */ VTable *pDisconnect; /* Disconnect these in next sqlite3_prepare() */
@ -1240,6 +1242,7 @@ struct VTable {
Module *pMod; /* Pointer to module implementation */ Module *pMod; /* Pointer to module implementation */
sqlite3_vtab *pVtab; /* Pointer to vtab instance */ sqlite3_vtab *pVtab; /* Pointer to vtab instance */
int nRef; /* Number of pointers to this structure */ int nRef; /* Number of pointers to this structure */
u8 bConstraint; /* True if constraints are supported */
VTable *pNext; /* Next in linked list (see above) */ VTable *pNext; /* Next in linked list (see above) */
}; };
@ -3052,6 +3055,7 @@ void sqlite3AutoLoadExtensions(sqlite3*);
# define sqlite3VtabLock(X) # define sqlite3VtabLock(X)
# define sqlite3VtabUnlock(X) # define sqlite3VtabUnlock(X)
# define sqlite3VtabUnlockList(X) # define sqlite3VtabUnlockList(X)
# define sqlite3VtabSavepoint(X, Y, Z) SQLITE_OK
#else #else
void sqlite3VtabClear(sqlite3 *db, Table*); void sqlite3VtabClear(sqlite3 *db, Table*);
int sqlite3VtabSync(sqlite3 *db, char **); int sqlite3VtabSync(sqlite3 *db, char **);
@ -3060,6 +3064,7 @@ void sqlite3AutoLoadExtensions(sqlite3*);
void sqlite3VtabLock(VTable *); void sqlite3VtabLock(VTable *);
void sqlite3VtabUnlock(VTable *); void sqlite3VtabUnlock(VTable *);
void sqlite3VtabUnlockList(sqlite3*); void sqlite3VtabUnlockList(sqlite3*);
int sqlite3VtabSavepoint(sqlite3 *, int, int);
# define sqlite3VtabInSync(db) ((db)->nVTrans>0 && (db)->aVTrans==0) # define sqlite3VtabInSync(db) ((db)->nVTrans>0 && (db)->aVTrans==0)
#endif #endif
void sqlite3VtabMakeWritable(Parse*,Table*); void sqlite3VtabMakeWritable(Parse*,Table*);

View File

@ -14,6 +14,7 @@
** testing of the SQLite library. ** testing of the SQLite library.
*/ */
#include "sqliteInt.h" #include "sqliteInt.h"
#include "vdbeInt.h"
#include "tcl.h" #include "tcl.h"
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
@ -2326,6 +2327,32 @@ static int test_stmt_readonly(
return TCL_OK; return TCL_OK;
} }
/*
** Usage: uses_stmt_journal STMT
**
** Return true if STMT uses a statement journal.
*/
static int uses_stmt_journal(
void * clientData,
Tcl_Interp *interp,
int objc,
Tcl_Obj *CONST objv[]
){
sqlite3_stmt *pStmt;
int rc;
if( objc!=2 ){
Tcl_AppendResult(interp, "wrong # args: should be \"",
Tcl_GetStringFromObj(objv[0], 0), " STMT", 0);
return TCL_ERROR;
}
if( getStmtPointer(interp, Tcl_GetString(objv[1]), &pStmt) ) return TCL_ERROR;
rc = sqlite3_stmt_readonly(pStmt);
Tcl_SetObjResult(interp, Tcl_NewBooleanObj(((Vdbe *)pStmt)->usesStmtJournal));
return TCL_OK;
}
/* /*
** Usage: sqlite3_reset STMT ** Usage: sqlite3_reset STMT
@ -5583,6 +5610,7 @@ int Sqlitetest1_Init(Tcl_Interp *interp){
{ "sqlite3_sql", test_sql ,0 }, { "sqlite3_sql", test_sql ,0 },
{ "sqlite3_next_stmt", test_next_stmt ,0 }, { "sqlite3_next_stmt", test_next_stmt ,0 },
{ "sqlite3_stmt_readonly", test_stmt_readonly ,0 }, { "sqlite3_stmt_readonly", test_stmt_readonly ,0 },
{ "uses_stmt_journal", uses_stmt_journal ,0 },
{ "sqlite3_release_memory", test_release_memory, 0}, { "sqlite3_release_memory", test_release_memory, 0},
{ "sqlite3_soft_heap_limit", test_soft_heap_limit, 0}, { "sqlite3_soft_heap_limit", test_soft_heap_limit, 0},

View File

@ -128,6 +128,7 @@
#include <unistd.h> #include <unistd.h>
#include <time.h> #include <time.h>
#include <errno.h> #include <errno.h>
#include <fcntl.h>
/* /*
** Size of the write buffer used by journal files in bytes. ** Size of the write buffer used by journal files in bytes.

View File

@ -23,7 +23,8 @@ static void updateVirtualTable(
ExprList *pChanges, /* The columns to change in the UPDATE statement */ ExprList *pChanges, /* The columns to change in the UPDATE statement */
Expr *pRowidExpr, /* Expression used to recompute the rowid */ Expr *pRowidExpr, /* Expression used to recompute the rowid */
int *aXRef, /* Mapping from columns of pTab to entries in pChanges */ int *aXRef, /* Mapping from columns of pTab to entries in pChanges */
Expr *pWhere /* WHERE clause of the UPDATE statement */ Expr *pWhere, /* WHERE clause of the UPDATE statement */
int onError /* ON CONFLICT strategy */
); );
#endif /* SQLITE_OMIT_VIRTUALTABLE */ #endif /* SQLITE_OMIT_VIRTUALTABLE */
@ -267,7 +268,7 @@ void sqlite3Update(
/* Virtual tables must be handled separately */ /* Virtual tables must be handled separately */
if( IsVirtual(pTab) ){ if( IsVirtual(pTab) ){
updateVirtualTable(pParse, pTabList, pTab, pChanges, pRowidExpr, aXRef, updateVirtualTable(pParse, pTabList, pTab, pChanges, pRowidExpr, aXRef,
pWhere); pWhere, onError);
pWhere = 0; pWhere = 0;
pTabList = 0; pTabList = 0;
goto update_cleanup; goto update_cleanup;
@ -610,7 +611,8 @@ static void updateVirtualTable(
ExprList *pChanges, /* The columns to change in the UPDATE statement */ ExprList *pChanges, /* The columns to change in the UPDATE statement */
Expr *pRowid, /* Expression used to recompute the rowid */ Expr *pRowid, /* Expression used to recompute the rowid */
int *aXRef, /* Mapping from columns of pTab to entries in pChanges */ int *aXRef, /* Mapping from columns of pTab to entries in pChanges */
Expr *pWhere /* WHERE clause of the UPDATE statement */ Expr *pWhere, /* WHERE clause of the UPDATE statement */
int onError /* ON CONFLICT strategy */
){ ){
Vdbe *v = pParse->pVdbe; /* Virtual machine under construction */ Vdbe *v = pParse->pVdbe; /* Virtual machine under construction */
ExprList *pEList = 0; /* The result set of the SELECT statement */ ExprList *pEList = 0; /* The result set of the SELECT statement */
@ -667,6 +669,7 @@ static void updateVirtualTable(
} }
sqlite3VtabMakeWritable(pParse, pTab); sqlite3VtabMakeWritable(pParse, pTab);
sqlite3VdbeAddOp4(v, OP_VUpdate, 0, pTab->nCol+2, iReg, pVTab, P4_VTAB); sqlite3VdbeAddOp4(v, OP_VUpdate, 0, pTab->nCol+2, iReg, pVTab, P4_VTAB);
sqlite3VdbeChangeP5(v, onError==OE_Default ? OE_Abort : onError);
sqlite3MayAbort(pParse); sqlite3MayAbort(pParse);
sqlite3VdbeAddOp2(v, OP_Next, ephemTab, addr+1); sqlite3VdbeAddOp2(v, OP_Next, ephemTab, addr+1);
sqlite3VdbeJumpHere(v, addr); sqlite3VdbeJumpHere(v, addr);

View File

@ -2590,6 +2590,14 @@ case OP_Savepoint: {
}else{ }else{
nName = sqlite3Strlen30(zName); nName = sqlite3Strlen30(zName);
/* This call is Ok even if this savepoint is actually a transaction
** savepoint (and therefore should not prompt xSavepoint()) callbacks.
** If this is a transaction savepoint being opened, it is guaranteed
** that the db->aVTrans[] array is empty. */
assert( db->autoCommit==0 || db->nVTrans==0 );
rc = sqlite3VtabSavepoint(db, SAVEPOINT_BEGIN, p->iStatement);
if( rc!=SQLITE_OK ) goto abort_due_to_error;
/* Create a new savepoint structure. */ /* Create a new savepoint structure. */
pNew = sqlite3DbMallocRaw(db, sizeof(Savepoint)+nName+1); pNew = sqlite3DbMallocRaw(db, sizeof(Savepoint)+nName+1);
if( pNew ){ if( pNew ){
@ -2696,6 +2704,11 @@ case OP_Savepoint: {
}else{ }else{
db->nDeferredCons = pSavepoint->nDeferredCons; db->nDeferredCons = pSavepoint->nDeferredCons;
} }
if( !isTransaction ){
rc = sqlite3VtabSavepoint(db, p1, iSavepoint);
if( rc!=SQLITE_OK ) goto abort_due_to_error;
}
} }
} }
@ -2831,7 +2844,11 @@ case OP_Transaction: {
db->nStatement++; db->nStatement++;
p->iStatement = db->nSavepoint + db->nStatement; p->iStatement = db->nSavepoint + db->nStatement;
} }
rc = sqlite3BtreeBeginStmt(pBt, p->iStatement);
rc = sqlite3VtabSavepoint(db, SAVEPOINT_BEGIN, p->iStatement);
if( rc==SQLITE_OK ){
rc = sqlite3BtreeBeginStmt(pBt, p->iStatement);
}
/* Store the current value of the database handles deferred constraint /* Store the current value of the database handles deferred constraint
** counter. If the statement transaction needs to be rolled back, ** counter. If the statement transaction needs to be rolled back,
@ -5825,11 +5842,15 @@ case OP_VUpdate: {
Mem **apArg; Mem **apArg;
Mem *pX; Mem *pX;
assert( pOp->p2==1 || pOp->p5==OE_Fail || pOp->p5==OE_Rollback
|| pOp->p5==OE_Abort || pOp->p5==OE_Ignore || pOp->p5==OE_Replace
);
pVtab = pOp->p4.pVtab->pVtab; pVtab = pOp->p4.pVtab->pVtab;
pModule = (sqlite3_module *)pVtab->pModule; pModule = (sqlite3_module *)pVtab->pModule;
nArg = pOp->p2; nArg = pOp->p2;
assert( pOp->p4type==P4_VTAB ); assert( pOp->p4type==P4_VTAB );
if( ALWAYS(pModule->xUpdate) ){ if( ALWAYS(pModule->xUpdate) ){
u8 vtabOnConflict = db->vtabOnConflict;
apArg = p->apArg; apArg = p->apArg;
pX = &aMem[pOp->p3]; pX = &aMem[pOp->p3];
for(i=0; i<nArg; i++){ for(i=0; i<nArg; i++){
@ -5839,13 +5860,23 @@ case OP_VUpdate: {
apArg[i] = pX; apArg[i] = pX;
pX++; pX++;
} }
db->vtabOnConflict = pOp->p5;
rc = pModule->xUpdate(pVtab, nArg, apArg, &rowid); rc = pModule->xUpdate(pVtab, nArg, apArg, &rowid);
db->vtabOnConflict = vtabOnConflict;
importVtabErrMsg(p, pVtab); importVtabErrMsg(p, pVtab);
if( rc==SQLITE_OK && pOp->p1 ){ if( rc==SQLITE_OK && pOp->p1 ){
assert( nArg>1 && apArg[0] && (apArg[0]->flags&MEM_Null) ); assert( nArg>1 && apArg[0] && (apArg[0]->flags&MEM_Null) );
db->lastRowid = rowid; db->lastRowid = rowid;
} }
p->nChange++; if( rc==SQLITE_CONSTRAINT && pOp->p4.pVtab->bConstraint ){
if( pOp->p5==OE_Ignore ){
rc = SQLITE_OK;
}else{
p->errorAction = ((pOp->p5==OE_Replace) ? OE_Abort : pOp->p5);
}
}else{
p->nChange++;
}
} }
break; break;
} }

View File

@ -2014,6 +2014,15 @@ int sqlite3VdbeCloseStatement(Vdbe *p, int eOp){
db->nStatement--; db->nStatement--;
p->iStatement = 0; p->iStatement = 0;
if( rc==SQLITE_OK ){
if( eOp==SAVEPOINT_ROLLBACK ){
rc = sqlite3VtabSavepoint(db, SAVEPOINT_ROLLBACK, iSavepoint);
}
if( rc==SQLITE_OK ){
rc = sqlite3VtabSavepoint(db, SAVEPOINT_RELEASE, iSavepoint);
}
}
/* If the statement transaction is being rolled back, also restore the /* If the statement transaction is being rolled back, also restore the
** database handles deferred constraint counter to the value it had when ** database handles deferred constraint counter to the value it had when
** the statement transaction was opened. */ ** the statement transaction was opened. */
@ -2931,7 +2940,7 @@ int sqlite3VdbeRecordCompare(
/* Compilers may complain that mem1.u.i is potentially uninitialized. /* Compilers may complain that mem1.u.i is potentially uninitialized.
** We could initialize it, as shown here, to silence those complaints. ** We could initialize it, as shown here, to silence those complaints.
** But in fact, mem1.u.i will never actually be used initialized, and doing ** But in fact, mem1.u.i will never actually be used uninitialized, and doing
** the unnecessary initialization has a measurable negative performance ** the unnecessary initialization has a measurable negative performance
** impact, since this routine is a very high runner. And so, we choose ** impact, since this routine is a very high runner. And so, we choose
** to ignore the compiler warnings and leave this variable uninitialized. ** to ignore the compiler warnings and leave this variable uninitialized.

View File

@ -14,6 +14,18 @@
#ifndef SQLITE_OMIT_VIRTUALTABLE #ifndef SQLITE_OMIT_VIRTUALTABLE
#include "sqliteInt.h" #include "sqliteInt.h"
/*
** Before a virtual table xCreate() or xConnect() method is invoked, the
** sqlite3.pVtabCtx member variable is set to point to an instance of
** this struct allocated on the stack. It is used by the implementation of
** the sqlite3_declare_vtab() and sqlite3_vtab_config() APIs, both of which
** are invoked only from within xCreate and xConnect methods.
*/
struct VtabCtx {
Table *pTab;
VTable *pVTable;
};
/* /*
** The actual function that does the work of creating a new module. ** The actual function that does the work of creating a new module.
** This function implements the sqlite3_create_module() and ** This function implements the sqlite3_create_module() and
@ -42,13 +54,13 @@ static int createModule(
pMod->xDestroy = xDestroy; pMod->xDestroy = xDestroy;
pDel = (Module *)sqlite3HashInsert(&db->aModule, zCopy, nName, (void*)pMod); pDel = (Module *)sqlite3HashInsert(&db->aModule, zCopy, nName, (void*)pMod);
if( pDel && pDel->xDestroy ){ if( pDel && pDel->xDestroy ){
sqlite3ResetInternalSchema(db, -1);
pDel->xDestroy(pDel->pAux); pDel->xDestroy(pDel->pAux);
} }
sqlite3DbFree(db, pDel); sqlite3DbFree(db, pDel);
if( pDel==pMod ){ if( pDel==pMod ){
db->mallocFailed = 1; db->mallocFailed = 1;
} }
sqlite3ResetInternalSchema(db, -1);
}else if( xDestroy ){ }else if( xDestroy ){
xDestroy(pAux); xDestroy(pAux);
} }
@ -434,6 +446,7 @@ static int vtabCallConstructor(
int (*xConstruct)(sqlite3*,void*,int,const char*const*,sqlite3_vtab**,char**), int (*xConstruct)(sqlite3*,void*,int,const char*const*,sqlite3_vtab**,char**),
char **pzErr char **pzErr
){ ){
VtabCtx sCtx;
VTable *pVTable; VTable *pVTable;
int rc; int rc;
const char *const*azArg = (const char *const*)pTab->azModuleArg; const char *const*azArg = (const char *const*)pTab->azModuleArg;
@ -453,12 +466,14 @@ static int vtabCallConstructor(
pVTable->db = db; pVTable->db = db;
pVTable->pMod = pMod; pVTable->pMod = pMod;
assert( !db->pVTab );
assert( xConstruct );
db->pVTab = pTab;
/* Invoke the virtual table constructor */ /* Invoke the virtual table constructor */
assert( &db->pVtabCtx );
assert( xConstruct );
sCtx.pTab = pTab;
sCtx.pVTable = pVTable;
db->pVtabCtx = &sCtx;
rc = xConstruct(db, pMod->pAux, nArg, azArg, &pVTable->pVtab, &zErr); rc = xConstruct(db, pMod->pAux, nArg, azArg, &pVTable->pVtab, &zErr);
db->pVtabCtx = 0;
if( rc==SQLITE_NOMEM ) db->mallocFailed = 1; if( rc==SQLITE_NOMEM ) db->mallocFailed = 1;
if( SQLITE_OK!=rc ){ if( SQLITE_OK!=rc ){
@ -474,7 +489,7 @@ static int vtabCallConstructor(
** the sqlite3_vtab object if successful. */ ** the sqlite3_vtab object if successful. */
pVTable->pVtab->pModule = pMod->pModule; pVTable->pVtab->pModule = pMod->pModule;
pVTable->nRef = 1; pVTable->nRef = 1;
if( db->pVTab ){ if( sCtx.pTab ){
const char *zFormat = "vtable constructor did not declare schema: %s"; const char *zFormat = "vtable constructor did not declare schema: %s";
*pzErr = sqlite3MPrintf(db, zFormat, pTab->zName); *pzErr = sqlite3MPrintf(db, zFormat, pTab->zName);
sqlite3VtabUnlock(pVTable); sqlite3VtabUnlock(pVTable);
@ -522,7 +537,6 @@ static int vtabCallConstructor(
} }
sqlite3DbFree(db, zModuleName); sqlite3DbFree(db, zModuleName);
db->pVTab = 0;
return rc; return rc;
} }
@ -642,8 +656,7 @@ int sqlite3_declare_vtab(sqlite3 *db, const char *zCreateTable){
char *zErr = 0; char *zErr = 0;
sqlite3_mutex_enter(db->mutex); sqlite3_mutex_enter(db->mutex);
pTab = db->pVTab; if( !db->pVtabCtx || !(pTab = db->pVtabCtx->pTab) ){
if( !pTab ){
sqlite3Error(db, SQLITE_MISUSE, 0); sqlite3Error(db, SQLITE_MISUSE, 0);
sqlite3_mutex_leave(db->mutex); sqlite3_mutex_leave(db->mutex);
return SQLITE_MISUSE_BKPT; return SQLITE_MISUSE_BKPT;
@ -670,7 +683,7 @@ int sqlite3_declare_vtab(sqlite3 *db, const char *zCreateTable){
pParse->pNewTable->nCol = 0; pParse->pNewTable->nCol = 0;
pParse->pNewTable->aCol = 0; pParse->pNewTable->aCol = 0;
} }
db->pVTab = 0; db->pVtabCtx->pTab = 0;
}else{ }else{
sqlite3Error(db, SQLITE_ERROR, (zErr ? "%s" : 0), zErr); sqlite3Error(db, SQLITE_ERROR, (zErr ? "%s" : 0), zErr);
sqlite3DbFree(db, zErr); sqlite3DbFree(db, zErr);
@ -822,7 +835,6 @@ int sqlite3VtabBegin(sqlite3 *db, VTable *pVTab){
if( pModule->xBegin ){ if( pModule->xBegin ){
int i; int i;
/* If pVtab is already in the aVTrans array, return early */ /* If pVtab is already in the aVTrans array, return early */
for(i=0; i<db->nVTrans; i++){ for(i=0; i<db->nVTrans; i++){
if( db->aVTrans[i]==pVTab ){ if( db->aVTrans[i]==pVTab ){
@ -839,6 +851,49 @@ int sqlite3VtabBegin(sqlite3 *db, VTable *pVTab){
return rc; return rc;
} }
/*
** Invoke either the xSavepoint, xRollbackTo or xRelease method of all
** virtual tables that currently have an open transaction. Pass iSavepoint
** as the second argument to the virtual table method invoked.
**
** If op is SAVEPOINT_BEGIN, the xSavepoint method is invoked. If it is
** SAVEPOINT_ROLLBACK, the xRollbackTo method. Otherwise, if op is
** SAVEPOINT_RELEASE, then the xRelease method of each virtual table with
** an open transaction is invoked.
**
** If any virtual table method returns an error code other than SQLITE_OK,
** processing is abandoned and the error returned to the caller of this
** function immediately. If all calls to virtual table methods are successful,
** SQLITE_OK is returned.
*/
int sqlite3VtabSavepoint(sqlite3 *db, int op, int iSavepoint){
int rc = SQLITE_OK;
assert( op==SAVEPOINT_RELEASE||op==SAVEPOINT_ROLLBACK||op==SAVEPOINT_BEGIN );
if( db->aVTrans ){
int i;
for(i=0; rc==SQLITE_OK && i<db->nVTrans; i++){
const sqlite3_module *pMod = db->aVTrans[i]->pMod->pModule;
if( pMod->iVersion>=1 ){
int (*xMethod)(sqlite3_vtab *, int);
switch( op ){
case SAVEPOINT_BEGIN:
xMethod = pMod->xSavepoint;
break;
case SAVEPOINT_ROLLBACK:
xMethod = pMod->xRollbackTo;
break;
default:
xMethod = pMod->xRelease;
break;
}
if( xMethod ) rc = xMethod(db->aVTrans[i]->pVtab, iSavepoint);
}
}
}
return rc;
}
/* /*
** The first parameter (pDef) is a function implementation. The ** The first parameter (pDef) is a function implementation. The
** second parameter (pExpr) is the first argument to this function. ** second parameter (pExpr) is the first argument to this function.
@ -937,4 +992,55 @@ void sqlite3VtabMakeWritable(Parse *pParse, Table *pTab){
} }
} }
/*
** Return the ON CONFLICT resolution mode in effect for the virtual
** table update operation currently in progress.
**
** The results of this routine are undefined unless it is called from
** within an xUpdate method.
*/
int sqlite3_vtab_on_conflict(sqlite3 *db){
static const unsigned char aMap[] = {
SQLITE_ROLLBACK, SQLITE_IGNORE, SQLITE_ABORT, SQLITE_FAIL, SQLITE_REPLACE
};
assert( OE_Rollback==1 && OE_Abort==2 && OE_Fail==3 );
assert( OE_Ignore==4 && OE_Replace==5 );
assert( db->vtabOnConflict>=1 && db->vtabOnConflict<=5 );
return (int)aMap[db->vtabOnConflict-1];
}
/*
** Call from within the xCreate() or xConnect() methods to provide
** the SQLite core with additional information about the behavior
** of the virtual table being implemented.
*/
int sqlite3_vtab_config(sqlite3 *db, int op, ...){
va_list ap;
int rc = SQLITE_OK;
sqlite3_mutex_enter(db->mutex);
va_start(ap, op);
switch( op ){
case SQLITE_VTAB_CONSTRAINT_SUPPORT: {
VtabCtx *p = db->pVtabCtx;
if( !p ){
rc = SQLITE_MISUSE_BKPT;
}else{
assert( (p->pTab->tabFlags & TF_Virtual)!=0 );
p->pVTable->bConstraint = (u8)va_arg(ap, int);
}
break;
}
default:
rc = SQLITE_MISUSE_BKPT;
break;
}
va_end(ap);
if( rc!=SQLITE_OK ) sqlite3Error(db, rc, 0);
sqlite3_mutex_leave(db->mutex);
return rc;
}
#endif /* SQLITE_OMIT_VIRTUALTABLE */ #endif /* SQLITE_OMIT_VIRTUALTABLE */

View File

@ -989,9 +989,9 @@ do_execsql_test e_createtable-3.7.4 {
SELECT quote(a), quote(b) FROM t6; SELECT quote(a), quote(b) FROM t6;
} {1 2 'X' 3 1 4 'X' 5} } {1 2 'X' 3 1 4 'X' 5}
# EVIDENCE-OF: R-18683-56219 If the default value of a column is # EVIDENCE-OF: R-15363-55230 If the default value of a column is
# CURRENT_TIME, CURRENT_DATE or CURRENT_DATETIME, then the value used in # CURRENT_TIME, CURRENT_DATE or CURRENT_TIMESTAMP, then the value used
# the new row is a text representation of the current UTC date and/or # in the new row is a text representation of the current UTC date and/or
# time. # time.
# #
# This is difficult to test literally without knowing what time the # This is difficult to test literally without knowing what time the

View File

@ -24,6 +24,8 @@ ifcapable !fts3 {
return return
} }
set ::testprefix fts3token
proc escape_string {str} { proc escape_string {str} {
set out "" set out ""
foreach char [split $str ""] { foreach char [split $str ""] {
@ -165,10 +167,21 @@ ifcapable icu {
do_icu_test fts3token-4.6 MiddleOfTheOcean $input $output do_icu_test fts3token-4.6 MiddleOfTheOcean $input $output
do_icu_test fts3token-4.7 th_TH $input $output do_icu_test fts3token-4.7 th_TH $input $output
do_icu_test fts3token-4.8 en_US $input $output do_icu_test fts3token-4.8 en_US $input $output
do_execsql_test 5.1 {
CREATE VIRTUAL TABLE x1 USING fts3(name,TOKENIZE icu en_US);
insert into x1 (name) values (NULL);
insert into x1 (name) values (NULL);
delete from x1;
}
} }
do_test fts3token-internal { do_test fts3token-internal {
execsql { SELECT fts3_tokenizer_internal_test() } execsql { SELECT fts3_tokenizer_internal_test() }
} {ok} } {ok}
finish_test finish_test

View File

@ -38,10 +38,10 @@ do_execsql_test 1.2 {
six 1 1 three 4 6 two 1 1 six 1 1 three 4 6 two 1 1
} }
do_execsql_test 1.3 { do_execsql_test 1.3.1 { DELETE FROM t1; }
DELETE FROM t1; do_execsql_test 1.3.2 {
SELECT term, documents, occurrences FROM terms WHERE col = '*'; SELECT term, documents, occurrences FROM terms WHERE col = '*';
} {} }
do_execsql_test 1.4 { do_execsql_test 1.4 {
INSERT INTO t1 VALUES('a b a b a b a'); INSERT INTO t1 VALUES('a b a b a b a');

139
test/fts3conf.test Normal file
View File

@ -0,0 +1,139 @@
# 2011 April 25
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#*************************************************************************
# This file implements regression tests for SQLite library. The
# focus of this script is testing the FTS3 module.
set testdir [file dirname $argv0]
source $testdir/tester.tcl
set testprefix fts3conf
# If SQLITE_ENABLE_FTS3 is defined, omit this file.
ifcapable !fts3 {
finish_test
return
}
proc fts3_integrity {tn db tbl} {
if {[sqlite3_get_autocommit $db]==0} {
error "fts3_integrity does not work with an open transaction"
}
set sql [db one {SELECT sql FROM sqlite_master WHERE name = $tbl}]
regexp -nocase {[^(]* using (.*)} $sql -> tail
set cols [list]
$db eval "PRAGMA table_info($tbl)" {
lappend cols $name
}
set cols [join [concat docid $cols] ,]
$db eval [subst {
CREATE VIRTUAL TABLE fts3check USING fts4term($tbl);
CREATE VIRTUAL TABLE temp.fts3check2 USING $tail;
INSERT INTO temp.fts3check2($cols) SELECT docid, * FROM $tbl;
CREATE VIRTUAL TABLE temp.fts3check3 USING fts4term(fts3check2);
}]
set m1 [$db one {SELECT md5sum(term, docid, col, pos) FROM fts3check}]
set m2 [$db one {SELECT md5sum(term, docid, col, pos) FROM fts3check3}]
$db eval {
DROP TABLE fts3check;
DROP TABLE temp.fts3check2;
DROP TABLE temp.fts3check3;
}
uplevel [list do_test $tn [list set {} $m1] $m2]
}
do_execsql_test 1.0.1 {
CREATE VIRTUAL TABLE t1 USING fts3(x);
INSERT INTO t1(rowid, x) VALUES(1, 'a b c d');
INSERT INTO t1(rowid, x) VALUES(2, 'e f g h');
CREATE TABLE source(a, b);
INSERT INTO source VALUES(4, 'z');
INSERT INTO source VALUES(2, 'y');
}
db_save_and_close
set T1 "INTO t1(rowid, x) VALUES(1, 'x')"
set T2 "INTO t1(rowid, x) SELECT * FROM source"
set T3 "t1 SET docid = 2 WHERE docid = 1"
set T4 "t1 SET docid = CASE WHEN docid = 1 THEN 4 ELSE 3 END WHERE docid <=2"
foreach {tn sql uses constraint data} [subst {
1 "INSERT OR ROLLBACK $T1" 0 1 {{a b c d} {e f g h}}
2 "INSERT OR ABORT $T1" 0 1 {{a b c d} {e f g h} {i j k l}}
3 "INSERT OR FAIL $T1" 0 1 {{a b c d} {e f g h} {i j k l}}
4 "INSERT OR IGNORE $T1" 0 0 {{a b c d} {e f g h} {i j k l}}
5 "INSERT OR REPLACE $T1" 0 0 {x {e f g h} {i j k l}}
6 "INSERT OR ROLLBACK $T2" 1 1 {{a b c d} {e f g h}}
7 "INSERT OR ABORT $T2" 1 1 {{a b c d} {e f g h} {i j k l}}
8 "INSERT OR FAIL $T2" 1 1 {{a b c d} {e f g h} {i j k l} z}
9 "INSERT OR IGNORE $T2" 1 0 {{a b c d} {e f g h} {i j k l} z}
10 "INSERT OR REPLACE $T2" 1 0 {{a b c d} y {i j k l} z}
11 "UPDATE OR ROLLBACK $T3" 1 1 {{a b c d} {e f g h}}
12 "UPDATE OR ABORT $T3" 1 1 {{a b c d} {e f g h} {i j k l}}
13 "UPDATE OR FAIL $T3" 1 1 {{a b c d} {e f g h} {i j k l}}
14 "UPDATE OR IGNORE $T3" 1 0 {{a b c d} {e f g h} {i j k l}}
15 "UPDATE OR REPLACE $T3" 1 0 {{a b c d} {i j k l}}
16 "UPDATE OR ROLLBACK $T4" 1 1 {{a b c d} {e f g h}}
17 "UPDATE OR ABORT $T4" 1 1 {{a b c d} {e f g h} {i j k l}}
18 "UPDATE OR FAIL $T4" 1 1 {{e f g h} {i j k l} {a b c d}}
19 "UPDATE OR IGNORE $T4" 1 0 {{e f g h} {i j k l} {a b c d}}
20 "UPDATE OR REPLACE $T4" 1 0 {{e f g h} {a b c d}}
}] {
db_restore_and_reopen
execsql {
BEGIN;
INSERT INTO t1(rowid, x) VALUES(3, 'i j k l');
}
set R(0) {0 {}}
set R(1) {1 {constraint failed}}
do_catchsql_test 1.$tn.1 $sql $R($constraint)
do_catchsql_test 1.$tn.2 { SELECT * FROM t1 } [list 0 $data]
catchsql COMMIT
fts3_integrity 1.$tn.3 db t1
do_test 1.$tn.4 [list sql_uses_stmt db $sql] $uses
}
do_execsql_test 2.1.1 {
DELETE FROM t1;
BEGIN;
INSERT INTO t1 VALUES('a b c');
SAVEPOINT a;
INSERT INTO t1 VALUES('x y z');
ROLLBACK TO a;
COMMIT;
}
fts3_integrity 2.1.2 db t1
do_catchsql_test 2.2.1 {
DELETE FROM t1;
BEGIN;
INSERT INTO t1(docid, x) VALUES(0, 'a b c');
INSERT INTO t1(docid, x) VALUES(1, 'a b c');
REPLACE INTO t1(docid, x) VALUES('zero', 'd e f');
} {1 {datatype mismatch}}
do_execsql_test 2.2.2 { COMMIT }
do_execsql_test 2.2.3 { SELECT * FROM t1 } {{a b c} {a b c}}
fts3_integrity 2.2.4 db t1
finish_test

108
test/fts3sort.test Normal file
View File

@ -0,0 +1,108 @@
# 2011 May 04
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#*************************************************************************
# This file implements regression tests for SQLite library. The
# focus of this script is testing the FTS3 module.
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
# If SQLITE_ENABLE_FTS3 is defined, omit this file.
ifcapable !fts3 {
finish_test
return
}
set testprefix fts3sort
proc build_database {nRow} {
db close
forcedelete test.db
sqlite3 db test.db
set vocab [list aa ab ac ba bb bc ca cb cc da]
expr srand(0)
execsql { CREATE VIRTUAL TABLE t1 USING fts4 }
for {set i 0} {$i < $nRow} {incr i} {
set v [expr int(rand()*1000000)]
set doc [list]
for {set div 1} {$div < 1000000} {set div [expr $div*10]} {
lappend doc [lindex $vocab [expr ($v/$div) % 10]]
}
execsql { INSERT INTO t1 VALUES($doc) }
}
}
set nRow 1000
do_test 1.0 {
build_database $nRow
execsql { SELECT count(*) FROM t1 }
} $nRow
foreach {tn query} {
1 "SELECT docid, * FROM t1"
2 "SELECT docid, * FROM t1 WHERE t1 MATCH 'aa'"
3 "SELECT docid, * FROM t1 WHERE t1 MATCH 'a*'"
4 "SELECT docid, quote(matchinfo(t1)) FROM t1 WHERE t1 MATCH 'a*'"
5 "SELECT docid, quote(matchinfo(t1,'pcnxals')) FROM t1 WHERE t1 MATCH 'b*'"
6 "SELECT docid, * FROM t1 WHERE t1 MATCH 'a* b* c*'"
7 "SELECT docid, * FROM t1 WHERE t1 MATCH 'aa OR da'"
8 "SELECT docid, * FROM t1 WHERE t1 MATCH 'nosuchtoken'"
9 "SELECT docid, snippet(t1) FROM t1 WHERE t1 MATCH 'aa OR da'"
10 "SELECT docid, snippet(t1) FROM t1 WHERE t1 MATCH 'aa OR nosuchtoken'"
} {
unset -nocomplain A B C D
set A_list [list]
set B_list [list]
set C_list [list]
set D_list [list]
unset -nocomplain X
db eval "$query ORDER BY rowid ASC" X {
set A($X(docid)) [array get X]
lappend A_list $X(docid)
}
unset -nocomplain X
db eval "$query ORDER BY rowid DESC" X {
set B($X(docid)) [array get X]
lappend B_list $X(docid)
}
unset -nocomplain X
db eval "$query ORDER BY docid ASC" X {
set C($X(docid)) [array get X]
lappend C_list $X(docid)
}
unset -nocomplain X
db eval "$query ORDER BY docid DESC" X {
set D($X(docid)) [array get X]
lappend D_list $X(docid)
}
do_test 1.$tn.1 { set A_list } [lsort -integer -increasing $A_list]
do_test 1.$tn.2 { set B_list } [lsort -integer -decreasing $B_list]
do_test 1.$tn.3 { set C_list } [lsort -integer -increasing $C_list]
do_test 1.$tn.4 { set D_list } [lsort -integer -decreasing $D_list]
unset -nocomplain DATA
unset -nocomplain X
db eval "$query" X {
set DATA($X(docid)) [array get X]
}
do_test 1.$tn.5 { lsort [array get A] } [lsort [array get DATA]]
do_test 1.$tn.6 { lsort [array get B] } [lsort [array get DATA]]
do_test 1.$tn.7 { lsort [array get C] } [lsort [array get DATA]]
do_test 1.$tn.8 { lsort [array get D] } [lsort [array get DATA]]
}
finish_test

View File

@ -326,4 +326,64 @@ do_test insert4-6.7 {
} }
} {1 {constraint failed}} } {1 {constraint failed}}
# Ticket [6284df89debdfa61db8073e062908af0c9b6118e]
# Disable the xfer optimization if the destination table contains
# a foreign key constraint
#
ifcapable foreignkey {
do_test insert4-7.1 {
set ::sqlite3_xferopt_count 0
execsql {
CREATE TABLE t7a(x INTEGER PRIMARY KEY); INSERT INTO t7a VALUES(123);
CREATE TABLE t7b(y INTEGER REFERENCES t7a);
CREATE TABLE t7c(z INT); INSERT INTO t7c VALUES(234);
INSERT INTO t7b SELECT * FROM t7c;
SELECT * FROM t7b;
}
} {234}
do_test insert4-7.2 {
set ::sqlite3_xferopt_count
} {1}
do_test insert4-7.3 {
set ::sqlite3_xferopt_count 0
execsql {
DELETE FROM t7b;
PRAGMA foreign_keys=ON;
}
catchsql {
INSERT INTO t7b SELECT * FROM t7c;
}
} {1 {foreign key constraint failed}}
do_test insert4-7.4 {
execsql {SELECT * FROM t7b}
} {}
do_test insert4-7.5 {
set ::sqlite3_xferopt_count
} {0}
do_test insert4-7.6 {
set ::sqlite3_xferopt_count 0
execsql {
DELETE FROM t7b; DELETE FROM t7c;
INSERT INTO t7c VALUES(123);
INSERT INTO t7b SELECT * FROM t7c;
SELECT * FROM t7b;
}
} {123}
do_test insert4-7.7 {
set ::sqlite3_xferopt_count
} {0}
do_test insert4-7.7 {
set ::sqlite3_xferopt_count 0
execsql {
PRAGMA foreign_keys=OFF;
DELETE FROM t7b;
INSERT INTO t7b SELECT * FROM t7c;
SELECT * FROM t7b;
}
} {123}
do_test insert4-7.8 {
set ::sqlite3_xferopt_count
} {1}
}
finish_test finish_test

View File

@ -753,6 +753,17 @@ proc integrity_check {name {db db}} {
} }
} }
# Return true if the SQL statement passed as the second argument uses a
# statement transaction.
#
proc sql_uses_stmt {db sql} {
set stmt [sqlite3_prepare $db $sql -1 dummy]
set uses [uses_stmt_journal $stmt]
sqlite3_finalize $stmt
return $uses
}
proc fix_ifcapable_expr {expr} { proc fix_ifcapable_expr {expr} {
set ret "" set ret ""
set state 0 set state 0

134
tool/getlock.c Normal file
View File

@ -0,0 +1,134 @@
/*
** This utility program looks at an SQLite database and determines whether
** or not it is locked, the kind of lock, and who is holding this lock.
**
** This only works on unix when the posix advisory locking method is used
** (which is the default on unix) and when the PENDING_BYTE is in its
** usual place.
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
static void usage(const char *argv0){
fprintf(stderr, "Usage: %s database\n", argv0);
exit(1);
}
/* Check for a conflicting lock. If one is found, print an this
** on standard output using the format string given and return 1.
** If there are no conflicting locks, return 0.
*/
static int isLocked(
int h, /* File descriptor to check */
int type, /* F_RDLCK or F_WRLCK */
unsigned int iOfst, /* First byte of the lock */
unsigned int iCnt, /* Number of bytes in the lock range */
const char *zType /* Type of lock */
){
struct flock lk;
memset(&lk, 0, sizeof(lk));
lk.l_type = type;
lk.l_whence = SEEK_SET;
lk.l_start = iOfst;
lk.l_len = iCnt;
if( fcntl(h, F_GETLK, &lk)==(-1) ){
fprintf(stderr, "fcntl(%d) failed: errno=%d\n", h, errno);
exit(1);
}
if( lk.l_type==F_UNLCK ) return 0;
printf("%s lock held by %d\n", zType, (int)lk.l_pid);
return 1;
}
/*
** Location of locking bytes in the database file
*/
#define PENDING_BYTE (0x40000000)
#define RESERVED_BYTE (PENDING_BYTE+1)
#define SHARED_FIRST (PENDING_BYTE+2)
#define SHARED_SIZE 510
/*
** Lock locations for shared-memory locks used by WAL mode.
*/
#define SHM_BASE 120
#define SHM_WRITE SHM_BASE
#define SHM_CHECKPOINT (SHM_BASE+1)
#define SHM_RECOVER (SHM_BASE+2)
#define SHM_READ_FIRST (SHM_BASE+3)
#define SHM_READ_SIZE 5
int main(int argc, char **argv){
int hDb; /* File descriptor for the open database file */
int hShm; /* File descriptor for WAL shared-memory file */
char *zShm; /* Name of the shared-memory file for WAL mode */
ssize_t got; /* Bytes read from header */
int isWal; /* True if in WAL mode */
int nName; /* Length of filename */
unsigned char aHdr[100]; /* Database header */
int nLock = 0; /* Number of locks held */
int i; /* Loop counter */
if( argc!=2 ) usage(argv[0]);
hDb = open(argv[1], O_RDONLY, 0);
if( hDb<0 ){
fprintf(stderr, "cannot open %s\n", argv[1]);
return 1;
}
/* Make sure we are dealing with an database file */
got = read(hDb, aHdr, 100);
if( got!=100 || memcmp(aHdr, "SQLite format 3",16)!=0 ){
fprintf(stderr, "not an SQLite database: %s\n", argv[1]);
exit(1);
}
/* First check for an exclusive lock */
if( isLocked(hDb, F_RDLCK, SHARED_FIRST, SHARED_SIZE, "EXCLUSIVE") ){
return 0;
}
isWal = aHdr[18]==2;
if( isWal==0 ){
/* Rollback mode */
if( isLocked(hDb, F_RDLCK, PENDING_BYTE, 1, "PENDING") ) return 0;
if( isLocked(hDb, F_RDLCK, RESERVED_BYTE, 1, "RESERVED") ) return 0;
if( isLocked(hDb, F_WRLCK, SHARED_FIRST, SHARED_SIZE, "SHARED") ){
return 0;
}
}else{
/* WAL mode */
nName = (int)strlen(argv[1]);
zShm = malloc( nName + 100 );
if( zShm==0 ){
fprintf(stderr, "out of memory\n");
exit(1);
}
memcpy(zShm, argv[1], nName);
memcpy(&zShm[nName], "-shm", 5);
hShm = open(zShm, O_RDONLY, 0);
if( hShm<0 ){
fprintf(stderr, "cannot open %s\n", zShm);
return 1;
}
if( isLocked(hShm, F_RDLCK, SHM_RECOVER, 1, "WAL-RECOVERY") ){
return 0;
}
nLock += isLocked(hShm, F_RDLCK, SHM_CHECKPOINT, 1, "WAL-CHECKPOINT");
nLock += isLocked(hShm, F_RDLCK, SHM_WRITE, 1, "WAL-WRITE");
for(i=0; i<SHM_READ_SIZE; i++){
nLock += isLocked(hShm, F_WRLCK, SHM_READ_FIRST+i, 1, "WAL-READ");
}
}
if( nLock==0 ){
printf("file is not locked\n");
}
return 0;
}

View File

@ -200,7 +200,7 @@ do_test shell1-1.15.3 {
# -version show SQLite version # -version show SQLite version
do_test shell1-1.16.1 { do_test shell1-1.16.1 {
catchcmd "-version test.db" "" catchcmd "-version test.db" ""
} {0 3.7.6.1} } {0 3.7.7}
#---------------------------------------------------------------------------- #----------------------------------------------------------------------------
# Test cases shell1-2.*: Basic "dot" command token parsing. # Test cases shell1-2.*: Basic "dot" command token parsing.
@ -711,4 +711,9 @@ do_test shell1-3.27.4 {
catchcmd "test.db" ".timer OFF BAD" catchcmd "test.db" ".timer OFF BAD"
} {1 {Error: unknown command or invalid arguments: "timer". Enter ".help" for help}} } {1 {Error: unknown command or invalid arguments: "timer". Enter ".help" for help}}
do_test shell1-3-28.1 {
catchcmd test.db \
".log stdout\nSELECT coalesce(sqlite_log(123,'hello'),'456');"
} "0 {(123) hello\n456}"
puts "CLI tests completed successfully" puts "CLI tests completed successfully"