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

Synchronize with the trunk.

FossilOrigin-Name: 136445ba020c9475d3f5a7843d7d0add98477138
This commit is contained in:
drh
2013-10-10 20:13:18 +00:00
94 changed files with 7481 additions and 2859 deletions

View File

@ -1457,7 +1457,11 @@ static int fts3BestIndexMethod(sqlite3_vtab *pVTab, sqlite3_index_info *pInfo){
Fts3Table *p = (Fts3Table *)pVTab;
int i; /* Iterator variable */
int iCons = -1; /* Index of constraint to use */
int iLangidCons = -1; /* Index of langid=x constraint, if present */
int iDocidGe = -1; /* Index of docid>=x constraint, if present */
int iDocidLe = -1; /* Index of docid<=x constraint, if present */
int iIdx;
/* By default use a full table scan. This is an expensive option,
** so search through the constraints to see if a more efficient
@ -1466,14 +1470,14 @@ static int fts3BestIndexMethod(sqlite3_vtab *pVTab, sqlite3_index_info *pInfo){
pInfo->idxNum = FTS3_FULLSCAN_SEARCH;
pInfo->estimatedCost = 5000000;
for(i=0; i<pInfo->nConstraint; i++){
int bDocid; /* True if this constraint is on docid */
struct sqlite3_index_constraint *pCons = &pInfo->aConstraint[i];
if( pCons->usable==0 ) continue;
bDocid = (pCons->iColumn<0 || pCons->iColumn==p->nColumn+1);
/* A direct lookup on the rowid or docid column. Assign a cost of 1.0. */
if( iCons<0
&& pCons->op==SQLITE_INDEX_CONSTRAINT_EQ
&& (pCons->iColumn<0 || pCons->iColumn==p->nColumn+1 )
){
if( iCons<0 && pCons->op==SQLITE_INDEX_CONSTRAINT_EQ && bDocid ){
pInfo->idxNum = FTS3_DOCID_SEARCH;
pInfo->estimatedCost = 1.0;
iCons = i;
@ -1502,14 +1506,38 @@ static int fts3BestIndexMethod(sqlite3_vtab *pVTab, sqlite3_index_info *pInfo){
){
iLangidCons = i;
}
if( bDocid ){
switch( pCons->op ){
case SQLITE_INDEX_CONSTRAINT_GE:
case SQLITE_INDEX_CONSTRAINT_GT:
iDocidGe = i;
break;
case SQLITE_INDEX_CONSTRAINT_LE:
case SQLITE_INDEX_CONSTRAINT_LT:
iDocidLe = i;
break;
}
}
}
iIdx = 1;
if( iCons>=0 ){
pInfo->aConstraintUsage[iCons].argvIndex = 1;
pInfo->aConstraintUsage[iCons].argvIndex = iIdx++;
pInfo->aConstraintUsage[iCons].omit = 1;
}
if( iLangidCons>=0 ){
pInfo->aConstraintUsage[iLangidCons].argvIndex = 2;
pInfo->idxNum |= FTS3_HAVE_LANGID;
pInfo->aConstraintUsage[iLangidCons].argvIndex = iIdx++;
}
if( iDocidGe>=0 ){
pInfo->idxNum |= FTS3_HAVE_DOCID_GE;
pInfo->aConstraintUsage[iDocidGe].argvIndex = iIdx++;
}
if( iDocidLe>=0 ){
pInfo->idxNum |= FTS3_HAVE_DOCID_LE;
pInfo->aConstraintUsage[iDocidLe].argvIndex = iIdx++;
}
/* Regardless of the strategy selected, FTS can deliver rows in rowid (or
@ -2956,6 +2984,33 @@ static int fts3NextMethod(sqlite3_vtab_cursor *pCursor){
return rc;
}
/*
** The following are copied from sqliteInt.h.
**
** Constants for the largest and smallest possible 64-bit signed integers.
** These macros are designed to work correctly on both 32-bit and 64-bit
** compilers.
*/
#ifndef SQLITE_AMALGAMATION
# define LARGEST_INT64 (0xffffffff|(((sqlite3_int64)0x7fffffff)<<32))
# define SMALLEST_INT64 (((sqlite3_int64)-1) - LARGEST_INT64)
#endif
/*
** If the numeric type of argument pVal is "integer", then return it
** converted to a 64-bit signed integer. Otherwise, return a copy of
** the second parameter, iDefault.
*/
static sqlite3_int64 fts3DocidRange(sqlite3_value *pVal, i64 iDefault){
if( pVal ){
int eType = sqlite3_value_numeric_type(pVal);
if( eType==SQLITE_INTEGER ){
return sqlite3_value_int64(pVal);
}
}
return iDefault;
}
/*
** This is the xFilter interface for the virtual table. See
** the virtual table xFilter method documentation for additional
@ -2981,40 +3036,58 @@ static int fts3FilterMethod(
){
int rc;
char *zSql; /* SQL statement used to access %_content */
int eSearch;
Fts3Table *p = (Fts3Table *)pCursor->pVtab;
Fts3Cursor *pCsr = (Fts3Cursor *)pCursor;
sqlite3_value *pCons = 0; /* The MATCH or rowid constraint, if any */
sqlite3_value *pLangid = 0; /* The "langid = ?" constraint, if any */
sqlite3_value *pDocidGe = 0; /* The "docid >= ?" constraint, if any */
sqlite3_value *pDocidLe = 0; /* The "docid <= ?" constraint, if any */
int iIdx;
UNUSED_PARAMETER(idxStr);
UNUSED_PARAMETER(nVal);
assert( idxNum>=0 && idxNum<=(FTS3_FULLTEXT_SEARCH+p->nColumn) );
assert( nVal==0 || nVal==1 || nVal==2 );
assert( (nVal==0)==(idxNum==FTS3_FULLSCAN_SEARCH) );
eSearch = (idxNum & 0x0000FFFF);
assert( eSearch>=0 && eSearch<=(FTS3_FULLTEXT_SEARCH+p->nColumn) );
assert( p->pSegments==0 );
/* Collect arguments into local variables */
iIdx = 0;
if( eSearch!=FTS3_FULLSCAN_SEARCH ) pCons = apVal[iIdx++];
if( idxNum & FTS3_HAVE_LANGID ) pLangid = apVal[iIdx++];
if( idxNum & FTS3_HAVE_DOCID_GE ) pDocidGe = apVal[iIdx++];
if( idxNum & FTS3_HAVE_DOCID_LE ) pDocidLe = apVal[iIdx++];
assert( iIdx==nVal );
/* In case the cursor has been used before, clear it now. */
sqlite3_finalize(pCsr->pStmt);
sqlite3_free(pCsr->aDoclist);
sqlite3Fts3ExprFree(pCsr->pExpr);
memset(&pCursor[1], 0, sizeof(Fts3Cursor)-sizeof(sqlite3_vtab_cursor));
/* Set the lower and upper bounds on docids to return */
pCsr->iMinDocid = fts3DocidRange(pDocidGe, SMALLEST_INT64);
pCsr->iMaxDocid = fts3DocidRange(pDocidLe, LARGEST_INT64);
if( idxStr ){
pCsr->bDesc = (idxStr[0]=='D');
}else{
pCsr->bDesc = p->bDescIdx;
}
pCsr->eSearch = (i16)idxNum;
pCsr->eSearch = (i16)eSearch;
if( idxNum!=FTS3_DOCID_SEARCH && idxNum!=FTS3_FULLSCAN_SEARCH ){
int iCol = idxNum-FTS3_FULLTEXT_SEARCH;
const char *zQuery = (const char *)sqlite3_value_text(apVal[0]);
if( eSearch!=FTS3_DOCID_SEARCH && eSearch!=FTS3_FULLSCAN_SEARCH ){
int iCol = eSearch-FTS3_FULLTEXT_SEARCH;
const char *zQuery = (const char *)sqlite3_value_text(pCons);
if( zQuery==0 && sqlite3_value_type(apVal[0])!=SQLITE_NULL ){
if( zQuery==0 && sqlite3_value_type(pCons)!=SQLITE_NULL ){
return SQLITE_NOMEM;
}
pCsr->iLangid = 0;
if( nVal==2 ) pCsr->iLangid = sqlite3_value_int(apVal[1]);
if( pLangid ) pCsr->iLangid = sqlite3_value_int(pLangid);
assert( p->base.zErrMsg==0 );
rc = sqlite3Fts3ExprParse(p->pTokenizer, pCsr->iLangid,
@ -3037,7 +3110,7 @@ static int fts3FilterMethod(
** full-text query or docid lookup, the statement retrieves a single
** row by docid.
*/
if( idxNum==FTS3_FULLSCAN_SEARCH ){
if( eSearch==FTS3_FULLSCAN_SEARCH ){
zSql = sqlite3_mprintf(
"SELECT %s ORDER BY rowid %s",
p->zReadExprlist, (pCsr->bDesc ? "DESC" : "ASC")
@ -3048,10 +3121,10 @@ static int fts3FilterMethod(
}else{
rc = SQLITE_NOMEM;
}
}else if( idxNum==FTS3_DOCID_SEARCH ){
}else if( eSearch==FTS3_DOCID_SEARCH ){
rc = fts3CursorSeekStmt(pCsr, &pCsr->pStmt);
if( rc==SQLITE_OK ){
rc = sqlite3_bind_value(pCsr->pStmt, 1, apVal[0]);
rc = sqlite3_bind_value(pCsr->pStmt, 1, pCons);
}
}
if( rc!=SQLITE_OK ) return rc;
@ -3942,6 +4015,12 @@ static int fts3EvalDeferredPhrase(Fts3Cursor *pCsr, Fts3Phrase *pPhrase){
return SQLITE_OK;
}
/*
** Maximum number of tokens a phrase may have to be considered for the
** incremental doclists strategy.
*/
#define MAX_INCR_PHRASE_TOKENS 4
/*
** This function is called for each Fts3Phrase in a full-text query
** expression to initialize the mechanism for returning rows. Once this
@ -3955,23 +4034,43 @@ static int fts3EvalDeferredPhrase(Fts3Cursor *pCsr, Fts3Phrase *pPhrase){
** SQLITE_OK is returned if no error occurs, otherwise an SQLite error code.
*/
static int fts3EvalPhraseStart(Fts3Cursor *pCsr, int bOptOk, Fts3Phrase *p){
int rc; /* Error code */
Fts3PhraseToken *pFirst = &p->aToken[0];
Fts3Table *pTab = (Fts3Table *)pCsr->base.pVtab;
int rc = SQLITE_OK; /* Error code */
int i;
if( pCsr->bDesc==pTab->bDescIdx
&& bOptOk==1
&& p->nToken==1
&& pFirst->pSegcsr
&& pFirst->pSegcsr->bLookup
&& pFirst->bFirst==0
){
/* Determine if doclists may be loaded from disk incrementally. This is
** possible if the bOptOk argument is true, the FTS doclists will be
** scanned in forward order, and the phrase consists of
** MAX_INCR_PHRASE_TOKENS or fewer tokens, none of which are are "^first"
** tokens or prefix tokens that cannot use a prefix-index. */
int bHaveIncr = 0;
int bIncrOk = (bOptOk
&& pCsr->bDesc==pTab->bDescIdx
&& p->nToken<=MAX_INCR_PHRASE_TOKENS && p->nToken>0
&& p->nToken<=MAX_INCR_PHRASE_TOKENS && p->nToken>0
#ifdef SQLITE_TEST
&& pTab->bNoIncrDoclist==0
#endif
);
for(i=0; bIncrOk==1 && i<p->nToken; i++){
Fts3PhraseToken *pToken = &p->aToken[i];
if( pToken->bFirst || (pToken->pSegcsr!=0 && !pToken->pSegcsr->bLookup) ){
bIncrOk = 0;
}
if( pToken->pSegcsr ) bHaveIncr = 1;
}
if( bIncrOk && bHaveIncr ){
/* Use the incremental approach. */
int iCol = (p->iColumn >= pTab->nColumn ? -1 : p->iColumn);
rc = sqlite3Fts3MsrIncrStart(
pTab, pFirst->pSegcsr, iCol, pFirst->z, pFirst->n);
for(i=0; rc==SQLITE_OK && i<p->nToken; i++){
Fts3PhraseToken *pToken = &p->aToken[i];
Fts3MultiSegReader *pSegcsr = pToken->pSegcsr;
if( pSegcsr ){
rc = sqlite3Fts3MsrIncrStart(pTab, pSegcsr, iCol, pToken->z, pToken->n);
}
}
p->bIncr = 1;
}else{
/* Load the full doclist for the phrase into memory. */
rc = fts3EvalPhraseLoad(pCsr, p);
@ -4080,6 +4179,216 @@ void sqlite3Fts3DoclistNext(
*ppIter = p;
}
/*
** Advance the iterator pDL to the next entry in pDL->aAll/nAll. Set *pbEof
** to true if EOF is reached.
*/
static void fts3EvalDlPhraseNext(
Fts3Table *pTab,
Fts3Doclist *pDL,
u8 *pbEof
){
char *pIter; /* Used to iterate through aAll */
char *pEnd = &pDL->aAll[pDL->nAll]; /* 1 byte past end of aAll */
if( pDL->pNextDocid ){
pIter = pDL->pNextDocid;
}else{
pIter = pDL->aAll;
}
if( pIter>=pEnd ){
/* We have already reached the end of this doclist. EOF. */
*pbEof = 1;
}else{
sqlite3_int64 iDelta;
pIter += sqlite3Fts3GetVarint(pIter, &iDelta);
if( pTab->bDescIdx==0 || pDL->pNextDocid==0 ){
pDL->iDocid += iDelta;
}else{
pDL->iDocid -= iDelta;
}
pDL->pList = pIter;
fts3PoslistCopy(0, &pIter);
pDL->nList = (int)(pIter - pDL->pList);
/* pIter now points just past the 0x00 that terminates the position-
** list for document pDL->iDocid. However, if this position-list was
** edited in place by fts3EvalNearTrim(), then pIter may not actually
** point to the start of the next docid value. The following line deals
** with this case by advancing pIter past the zero-padding added by
** fts3EvalNearTrim(). */
while( pIter<pEnd && *pIter==0 ) pIter++;
pDL->pNextDocid = pIter;
assert( pIter>=&pDL->aAll[pDL->nAll] || *pIter );
*pbEof = 0;
}
}
/*
** Helper type used by fts3EvalIncrPhraseNext() and incrPhraseTokenNext().
*/
typedef struct TokenDoclist TokenDoclist;
struct TokenDoclist {
int bIgnore;
sqlite3_int64 iDocid;
char *pList;
int nList;
};
/*
** Token pToken is an incrementally loaded token that is part of a
** multi-token phrase. Advance it to the next matching document in the
** database and populate output variable *p with the details of the new
** entry. Or, if the iterator has reached EOF, set *pbEof to true.
**
** If an error occurs, return an SQLite error code. Otherwise, return
** SQLITE_OK.
*/
static int incrPhraseTokenNext(
Fts3Table *pTab, /* Virtual table handle */
Fts3Phrase *pPhrase, /* Phrase to advance token of */
int iToken, /* Specific token to advance */
TokenDoclist *p, /* OUT: Docid and doclist for new entry */
u8 *pbEof /* OUT: True if iterator is at EOF */
){
int rc = SQLITE_OK;
if( pPhrase->iDoclistToken==iToken ){
assert( p->bIgnore==0 );
assert( pPhrase->aToken[iToken].pSegcsr==0 );
fts3EvalDlPhraseNext(pTab, &pPhrase->doclist, pbEof);
p->pList = pPhrase->doclist.pList;
p->nList = pPhrase->doclist.nList;
p->iDocid = pPhrase->doclist.iDocid;
}else{
Fts3PhraseToken *pToken = &pPhrase->aToken[iToken];
assert( pToken->pDeferred==0 );
assert( pToken->pSegcsr || pPhrase->iDoclistToken>=0 );
if( pToken->pSegcsr ){
assert( p->bIgnore==0 );
rc = sqlite3Fts3MsrIncrNext(
pTab, pToken->pSegcsr, &p->iDocid, &p->pList, &p->nList
);
if( p->pList==0 ) *pbEof = 1;
}else{
p->bIgnore = 1;
}
}
return rc;
}
/*
** The phrase iterator passed as the second argument:
**
** * features at least one token that uses an incremental doclist, and
**
** * does not contain any deferred tokens.
**
** Advance it to the next matching documnent in the database and populate
** the Fts3Doclist.pList and nList fields.
**
** If there is no "next" entry and no error occurs, then *pbEof is set to
** 1 before returning. Otherwise, if no error occurs and the iterator is
** successfully advanced, *pbEof is set to 0.
**
** If an error occurs, return an SQLite error code. Otherwise, return
** SQLITE_OK.
*/
static int fts3EvalIncrPhraseNext(
Fts3Cursor *pCsr, /* FTS Cursor handle */
Fts3Phrase *p, /* Phrase object to advance to next docid */
u8 *pbEof /* OUT: Set to 1 if EOF */
){
int rc = SQLITE_OK;
Fts3Doclist *pDL = &p->doclist;
Fts3Table *pTab = (Fts3Table *)pCsr->base.pVtab;
u8 bEof = 0;
/* This is only called if it is guaranteed that the phrase has at least
** one incremental token. In which case the bIncr flag is set. */
assert( p->bIncr==1 );
if( p->nToken==1 && p->bIncr ){
rc = sqlite3Fts3MsrIncrNext(pTab, p->aToken[0].pSegcsr,
&pDL->iDocid, &pDL->pList, &pDL->nList
);
if( pDL->pList==0 ) bEof = 1;
}else{
int bDescDoclist = pCsr->bDesc;
struct TokenDoclist a[MAX_INCR_PHRASE_TOKENS];
memset(a, 0, sizeof(a));
assert( p->nToken<=MAX_INCR_PHRASE_TOKENS );
assert( p->iDoclistToken<MAX_INCR_PHRASE_TOKENS );
while( bEof==0 ){
int bMaxSet = 0;
sqlite3_int64 iMax; /* Largest docid for all iterators */
int i; /* Used to iterate through tokens */
/* Advance the iterator for each token in the phrase once. */
for(i=0; rc==SQLITE_OK && i<p->nToken; i++){
rc = incrPhraseTokenNext(pTab, p, i, &a[i], &bEof);
if( a[i].bIgnore==0 && (bMaxSet==0 || DOCID_CMP(iMax, a[i].iDocid)<0) ){
iMax = a[i].iDocid;
bMaxSet = 1;
}
}
assert( rc!=SQLITE_OK || a[p->nToken-1].bIgnore==0 );
assert( rc!=SQLITE_OK || bMaxSet );
/* Keep advancing iterators until they all point to the same document */
for(i=0; i<p->nToken; i++){
while( rc==SQLITE_OK && bEof==0
&& a[i].bIgnore==0 && DOCID_CMP(a[i].iDocid, iMax)<0
){
rc = incrPhraseTokenNext(pTab, p, i, &a[i], &bEof);
if( DOCID_CMP(a[i].iDocid, iMax)>0 ){
iMax = a[i].iDocid;
i = 0;
}
}
}
/* Check if the current entries really are a phrase match */
if( bEof==0 ){
int nList = 0;
int nByte = a[p->nToken-1].nList;
char *aDoclist = sqlite3_malloc(nByte+1);
if( !aDoclist ) return SQLITE_NOMEM;
memcpy(aDoclist, a[p->nToken-1].pList, nByte+1);
for(i=0; i<(p->nToken-1); i++){
if( a[i].bIgnore==0 ){
char *pL = a[i].pList;
char *pR = aDoclist;
char *pOut = aDoclist;
int nDist = p->nToken-1-i;
int res = fts3PoslistPhraseMerge(&pOut, nDist, 0, 1, &pL, &pR);
if( res==0 ) break;
nList = (pOut - aDoclist);
}
}
if( i==(p->nToken-1) ){
pDL->iDocid = iMax;
pDL->pList = aDoclist;
pDL->nList = nList;
pDL->bFreeList = 1;
break;
}
sqlite3_free(aDoclist);
}
}
}
*pbEof = bEof;
return rc;
}
/*
** Attempt to move the phrase iterator to point to the next matching docid.
** If an error occurs, return an SQLite error code. Otherwise, return
@ -4099,55 +4408,14 @@ static int fts3EvalPhraseNext(
Fts3Table *pTab = (Fts3Table *)pCsr->base.pVtab;
if( p->bIncr ){
assert( p->nToken==1 );
assert( pDL->pNextDocid==0 );
rc = sqlite3Fts3MsrIncrNext(pTab, p->aToken[0].pSegcsr,
&pDL->iDocid, &pDL->pList, &pDL->nList
);
if( rc==SQLITE_OK && !pDL->pList ){
*pbEof = 1;
}
rc = fts3EvalIncrPhraseNext(pCsr, p, pbEof);
}else if( pCsr->bDesc!=pTab->bDescIdx && pDL->nAll ){
sqlite3Fts3DoclistPrev(pTab->bDescIdx, pDL->aAll, pDL->nAll,
&pDL->pNextDocid, &pDL->iDocid, &pDL->nList, pbEof
);
pDL->pList = pDL->pNextDocid;
}else{
char *pIter; /* Used to iterate through aAll */
char *pEnd = &pDL->aAll[pDL->nAll]; /* 1 byte past end of aAll */
if( pDL->pNextDocid ){
pIter = pDL->pNextDocid;
}else{
pIter = pDL->aAll;
}
if( pIter>=pEnd ){
/* We have already reached the end of this doclist. EOF. */
*pbEof = 1;
}else{
sqlite3_int64 iDelta;
pIter += sqlite3Fts3GetVarint(pIter, &iDelta);
if( pTab->bDescIdx==0 || pDL->pNextDocid==0 ){
pDL->iDocid += iDelta;
}else{
pDL->iDocid -= iDelta;
}
pDL->pList = pIter;
fts3PoslistCopy(0, &pIter);
pDL->nList = (int)(pIter - pDL->pList);
/* pIter now points just past the 0x00 that terminates the position-
** list for document pDL->iDocid. However, if this position-list was
** edited in place by fts3EvalNearTrim(), then pIter may not actually
** point to the start of the next docid value. The following line deals
** with this case by advancing pIter past the zero-padding added by
** fts3EvalNearTrim(). */
while( pIter<pEnd && *pIter==0 ) pIter++;
pDL->pNextDocid = pIter;
assert( pIter>=&pDL->aAll[pDL->nAll] || *pIter );
*pbEof = 0;
}
fts3EvalDlPhraseNext(pTab, pDL, pbEof);
}
return rc;
@ -4172,7 +4440,6 @@ static int fts3EvalPhraseNext(
static void fts3EvalStartReaders(
Fts3Cursor *pCsr, /* FTS Cursor handle */
Fts3Expr *pExpr, /* Expression to initialize phrases in */
int bOptOk, /* True to enable incremental loading */
int *pRc /* IN/OUT: Error code */
){
if( pExpr && SQLITE_OK==*pRc ){
@ -4183,10 +4450,10 @@ static void fts3EvalStartReaders(
if( pExpr->pPhrase->aToken[i].pDeferred==0 ) break;
}
pExpr->bDeferred = (i==nToken);
*pRc = fts3EvalPhraseStart(pCsr, bOptOk, pExpr->pPhrase);
*pRc = fts3EvalPhraseStart(pCsr, 1, pExpr->pPhrase);
}else{
fts3EvalStartReaders(pCsr, pExpr->pLeft, bOptOk, pRc);
fts3EvalStartReaders(pCsr, pExpr->pRight, bOptOk, pRc);
fts3EvalStartReaders(pCsr, pExpr->pLeft, pRc);
fts3EvalStartReaders(pCsr, pExpr->pRight, pRc);
pExpr->bDeferred = (pExpr->pLeft->bDeferred && pExpr->pRight->bDeferred);
}
}
@ -4428,7 +4695,7 @@ static int fts3EvalSelectDeferred(
** overflowing the 32-bit integer it is stored in. */
if( ii<12 ) nLoad4 = nLoad4*4;
if( ii==0 || pTC->pPhrase->nToken>1 ){
if( ii==0 || (pTC->pPhrase->nToken>1 && ii!=nToken-1) ){
/* Either this is the cheapest token in the entire query, or it is
** part of a multi-token phrase. Either way, the entire doclist will
** (eventually) be loaded into memory. It may as well be now. */
@ -4508,7 +4775,7 @@ static int fts3EvalStart(Fts3Cursor *pCsr){
}
#endif
fts3EvalStartReaders(pCsr, pCsr->pExpr, 1, &rc);
fts3EvalStartReaders(pCsr, pCsr->pExpr, &rc);
return rc;
}
@ -4991,6 +5258,16 @@ static int fts3EvalNext(Fts3Cursor *pCsr){
pCsr->iPrevId = pExpr->iDocid;
}while( pCsr->isEof==0 && fts3EvalTestDeferredAndNear(pCsr, &rc) );
}
/* Check if the cursor is past the end of the docid range specified
** by Fts3Cursor.iMinDocid/iMaxDocid. If so, set the EOF flag. */
if( rc==SQLITE_OK && (
(pCsr->bDesc==0 && pCsr->iPrevId>pCsr->iMaxDocid)
|| (pCsr->bDesc!=0 && pCsr->iPrevId<pCsr->iMinDocid)
)){
pCsr->isEof = 1;
}
return rc;
}
@ -5014,12 +5291,16 @@ static void fts3EvalRestart(
if( pPhrase ){
fts3EvalInvalidatePoslist(pPhrase);
if( pPhrase->bIncr ){
assert( pPhrase->nToken==1 );
assert( pPhrase->aToken[0].pSegcsr );
sqlite3Fts3MsrIncrRestart(pPhrase->aToken[0].pSegcsr);
int i;
for(i=0; i<pPhrase->nToken; i++){
Fts3PhraseToken *pToken = &pPhrase->aToken[i];
assert( pToken->pDeferred==0 );
if( pToken->pSegcsr ){
sqlite3Fts3MsrIncrRestart(pToken->pSegcsr);
}
}
*pRc = fts3EvalPhraseStart(pCsr, 0, pPhrase);
}
pPhrase->doclist.pNextDocid = 0;
pPhrase->doclist.iDocid = 0;
}
@ -5268,15 +5549,23 @@ int sqlite3Fts3EvalPhrasePoslist(
pIter = pPhrase->doclist.pList;
if( iDocid!=pCsr->iPrevId || pExpr->bEof ){
int bDescDoclist = pTab->bDescIdx; /* For DOCID_CMP macro */
int iMul; /* +1 if csr dir matches index dir, else -1 */
int bOr = 0;
u8 bEof = 0;
Fts3Expr *p;
u8 bTreeEof = 0;
Fts3Expr *p; /* Used to iterate from pExpr to root */
Fts3Expr *pNear; /* Most senior NEAR ancestor (or pExpr) */
/* Check if this phrase descends from an OR expression node. If not,
** return NULL. Otherwise, the entry that corresponds to docid
** pCsr->iPrevId may lie earlier in the doclist buffer. */
** pCsr->iPrevId may lie earlier in the doclist buffer. Or, if the
** tree that the node is part of has been marked as EOF, but the node
** itself is not EOF, then it may point to an earlier entry. */
pNear = pExpr;
for(p=pExpr->pParent; p; p=p->pParent){
if( p->eType==FTSQUERY_OR ) bOr = 1;
if( p->eType==FTSQUERY_NEAR ) pNear = p;
if( p->bEof ) bTreeEof = 1;
}
if( bOr==0 ) return SQLITE_OK;
@ -5295,29 +5584,59 @@ int sqlite3Fts3EvalPhrasePoslist(
assert( rc!=SQLITE_OK || pPhrase->bIncr==0 );
if( rc!=SQLITE_OK ) return rc;
}
if( pExpr->bEof ){
pIter = 0;
iDocid = 0;
iMul = ((pCsr->bDesc==bDescDoclist) ? 1 : -1);
while( bTreeEof==1
&& pNear->bEof==0
&& (DOCID_CMP(pNear->iDocid, pCsr->iPrevId) * iMul)<0
){
int rc = SQLITE_OK;
fts3EvalNextRow(pCsr, pExpr, &rc);
if( rc!=SQLITE_OK ) return rc;
iDocid = pExpr->iDocid;
pIter = pPhrase->doclist.pList;
}
bEof = (pPhrase->doclist.nAll==0);
assert( bDescDoclist==0 || bDescDoclist==1 );
assert( pCsr->bDesc==0 || pCsr->bDesc==1 );
if( pCsr->bDesc==bDescDoclist ){
int dummy;
while( (pIter==0 || DOCID_CMP(iDocid, pCsr->iPrevId)>0 ) && bEof==0 ){
sqlite3Fts3DoclistPrev(
bDescDoclist, pPhrase->doclist.aAll, pPhrase->doclist.nAll,
&pIter, &iDocid, &dummy, &bEof
);
}
}else{
while( (pIter==0 || DOCID_CMP(iDocid, pCsr->iPrevId)<0 ) && bEof==0 ){
sqlite3Fts3DoclistNext(
bDescDoclist, pPhrase->doclist.aAll, pPhrase->doclist.nAll,
&pIter, &iDocid, &bEof
);
if( bEof==0 ){
if( pCsr->bDesc==bDescDoclist ){
int dummy;
if( pNear->bEof ){
/* This expression is already at EOF. So position it to point to the
** last entry in the doclist at pPhrase->doclist.aAll[]. Variable
** iDocid is already set for this entry, so all that is required is
** to set pIter to point to the first byte of the last position-list
** in the doclist.
**
** It would also be correct to set pIter and iDocid to zero. In
** this case, the first call to sqltie3Fts4DoclistPrev() below
** would also move the iterator to point to the last entry in the
** doclist. However, this is expensive, as to do so it has to
** iterate through the entire doclist from start to finish (since
** it does not know the docid for the last entry). */
pIter = &pPhrase->doclist.aAll[pPhrase->doclist.nAll-1];
fts3ReversePoslist(pPhrase->doclist.aAll, &pIter);
}
while( (pIter==0 || DOCID_CMP(iDocid, pCsr->iPrevId)>0 ) && bEof==0 ){
sqlite3Fts3DoclistPrev(
bDescDoclist, pPhrase->doclist.aAll, pPhrase->doclist.nAll,
&pIter, &iDocid, &dummy, &bEof
);
}
}else{
if( pNear->bEof ){
pIter = 0;
iDocid = 0;
}
while( (pIter==0 || DOCID_CMP(iDocid, pCsr->iPrevId)<0 ) && bEof==0 ){
sqlite3Fts3DoclistNext(
bDescDoclist, pPhrase->doclist.aAll, pPhrase->doclist.nAll,
&pIter, &iDocid, &bEof
);
}
}
}

View File

@ -267,6 +267,12 @@ struct Fts3Table {
int inTransaction; /* True after xBegin but before xCommit/xRollback */
int mxSavepoint; /* Largest valid xSavepoint integer */
#endif
#ifdef SQLITE_TEST
/* True to disable the incremental doclist optimization. This is controled
** by special insert command 'test-no-incr-doclist'. */
int bNoIncrDoclist;
#endif
};
/*
@ -292,7 +298,8 @@ struct Fts3Cursor {
int eEvalmode; /* An FTS3_EVAL_XX constant */
int nRowAvg; /* Average size of database rows, in pages */
sqlite3_int64 nDoc; /* Documents in table */
i64 iMinDocid; /* Minimum docid to return */
i64 iMaxDocid; /* Maximum docid to return */
int isMatchinfoNeeded; /* True when aMatchinfo[] needs filling in */
u32 *aMatchinfo; /* Information about most recent match */
int nMatchinfo; /* Number of elements in aMatchinfo[] */
@ -322,6 +329,15 @@ struct Fts3Cursor {
#define FTS3_DOCID_SEARCH 1 /* Lookup by rowid on %_content table */
#define FTS3_FULLTEXT_SEARCH 2 /* Full-text index search */
/*
** The lower 16-bits of the sqlite3_index_info.idxNum value set by
** the xBestIndex() method contains the Fts3Cursor.eSearch value described
** above. The upper 16-bits contain a combination of the following
** bits, used to describe extra constraints on full-text searches.
*/
#define FTS3_HAVE_LANGID 0x00010000 /* languageid=? */
#define FTS3_HAVE_DOCID_GE 0x00020000 /* docid>=? */
#define FTS3_HAVE_DOCID_LE 0x00040000 /* docid<=? */
struct Fts3Doclist {
char *aAll; /* Array containing doclist (or NULL) */

View File

@ -4780,7 +4780,7 @@ static int fts3DoAutoincrmerge(
if( rc ) return rc;
}
rc = fts3SqlStmt(p, SQL_REPLACE_STAT, &pStmt, 0);
if( rc ) return rc;;
if( rc ) return rc;
sqlite3_bind_int(pStmt, 1, FTS_STAT_AUTOINCRMERGE);
sqlite3_bind_int(pStmt, 2, p->bAutoincrmerge);
sqlite3_step(pStmt);
@ -5050,6 +5050,9 @@ static int fts3SpecialInsert(Fts3Table *p, sqlite3_value *pVal){
}else if( nVal>11 && 0==sqlite3_strnicmp(zVal, "maxpending=", 9) ){
p->nMaxPendingData = atoi(&zVal[11]);
rc = SQLITE_OK;
}else if( nVal>21 && 0==sqlite3_strnicmp(zVal, "test-no-incr-doclist=", 21) ){
p->bNoIncrDoclist = atoi(&zVal[21]);
rc = SQLITE_OK;
#endif
}else{
rc = SQLITE_ERROR;

View File

@ -786,6 +786,7 @@ static void amatchFree(amatch_vtab *p){
sqlite3_free(p->zVocabTab);
sqlite3_free(p->zVocabWord);
sqlite3_free(p->zVocabLang);
sqlite3_free(p->zSelf);
memset(p, 0, sizeof(*p));
sqlite3_free(p);
}
@ -948,6 +949,9 @@ static void amatchClearCursor(amatch_cursor *pCur){
pCur->pAllWords = 0;
sqlite3_free(pCur->zInput);
pCur->zInput = 0;
sqlite3_free(pCur->zBuf);
pCur->zBuf = 0;
pCur->nBuf = 0;
pCur->pCost = 0;
pCur->pWord = 0;
pCur->pCurrent = 0;
@ -1103,7 +1107,7 @@ static int amatchNext(sqlite3_vtab_cursor *cur){
char *zSql;
if( p->zVocabLang && p->zVocabLang[0] ){
zSql = sqlite3_mprintf(
"SELECT \"%s\" FROM \"%s\"",
"SELECT \"%w\" FROM \"%w\"",
" WHERE \"%w\">=?1 AND \"%w\"=?2"
" ORDER BY 1",
p->zVocabWord, p->zVocabTab,
@ -1111,7 +1115,7 @@ static int amatchNext(sqlite3_vtab_cursor *cur){
);
}else{
zSql = sqlite3_mprintf(
"SELECT \"%s\" FROM \"%s\""
"SELECT \"%w\" FROM \"%w\""
" WHERE \"%w\">=?1"
" ORDER BY 1",
p->zVocabWord, p->zVocabTab,

View File

@ -38,6 +38,19 @@
** out) run the following query:
**
** SELECT next_char('cha','dictionary','word');
**
** IMPLEMENTATION NOTES:
**
** The next_char function is implemented using recursive SQL that makes
** use of the table name and column name as part of a query. If either
** the table name or column name are keywords or contain special characters,
** then they should be escaped. For example:
**
** SELECT next_char('cha','[dictionary]','[word]');
**
** This also means that the table name can be a subquery:
**
** SELECT next_char('cha','(SELECT word AS w FROM dictionary)','w');
*/
#include "sqlite3ext.h"
SQLITE_EXTENSION_INIT1
@ -231,9 +244,9 @@ static void nextCharFunc(
zColl = "";
}
zSql = sqlite3_mprintf(
"SELECT \"%w\" FROM \"%w\""
" WHERE \"%w\">=(?1 || ?2) %s"
" AND \"%w\"<=(?1 || char(1114111)) %s" /* 1114111 == 0x10ffff */
"SELECT %s FROM %s"
" WHERE %s>=(?1 || ?2) %s"
" AND %s<=(?1 || char(1114111)) %s" /* 1114111 == 0x10ffff */
" %s"
" ORDER BY 1 %s ASC LIMIT 1",
zField, zTable, zField, zColl, zField, zColl, zWhereClause, zColl

701
ext/misc/vfslog.c Normal file
View File

@ -0,0 +1,701 @@
/*
** 2013-10-09
**
** The author disclaims copyright to this source code. In place of
** a legal notice, here is a blessing:
**
** May you do good and not evil.
** May you find forgiveness for yourself and forgive others.
** May you share freely, never taking more than you give.
**
******************************************************************************
**
** This file contains the implementation of an SQLite vfs wrapper for
** unix that generates per-database log files of all disk activity.
*/
/*
** This module contains code for a wrapper VFS that causes a log of
** most VFS calls to be written into a file on disk. The log
** is stored as comma-separated variables.
**
** All calls on sqlite3_file objects are logged.
** Additionally, calls to the xAccess(), xOpen(), and xDelete()
** methods are logged. The other sqlite3_vfs object methods (xDlXXX,
** xRandomness, xSleep, xCurrentTime, xGetLastError and xCurrentTimeInt64)
** are not logged.
*/
#include "sqlite3.h"
#include <string.h>
#include <assert.h>
#include <stdio.h>
/*
** Forward declaration of objects used by this utility
*/
typedef struct VLogLog VLogLog;
typedef struct VLogVfs VLogVfs;
typedef struct VLogFile VLogFile;
/* There is a pair (an array of size 2) of the following objects for
** each database file being logged. The first contains the filename
** and is used to log I/O with the main database. The second has
** a NULL filename and is used to log I/O for the journal. Both
** out pointers are the same.
*/
struct VLogLog {
VLogLog *pNext; /* Next in a list of all active logs */
VLogLog **ppPrev; /* Pointer to this in the list */
int nRef; /* Number of references to this object */
int nFilename; /* Length of zFilename in bytes */
char *zFilename; /* Name of database file. NULL for journal */
FILE *out; /* Write information here */
};
struct VLogVfs {
sqlite3_vfs base; /* VFS methods */
sqlite3_vfs *pVfs; /* Parent VFS */
};
struct VLogFile {
sqlite3_file base; /* IO methods */
sqlite3_file *pReal; /* Underlying file handle */
VLogLog *pLog; /* The log file for this file */
};
#define REALVFS(p) (((VLogVfs*)(p))->pVfs)
/*
** Methods for VLogFile
*/
static int vlogClose(sqlite3_file*);
static int vlogRead(sqlite3_file*, void*, int iAmt, sqlite3_int64 iOfst);
static int vlogWrite(sqlite3_file*,const void*,int iAmt, sqlite3_int64 iOfst);
static int vlogTruncate(sqlite3_file*, sqlite3_int64 size);
static int vlogSync(sqlite3_file*, int flags);
static int vlogFileSize(sqlite3_file*, sqlite3_int64 *pSize);
static int vlogLock(sqlite3_file*, int);
static int vlogUnlock(sqlite3_file*, int);
static int vlogCheckReservedLock(sqlite3_file*, int *pResOut);
static int vlogFileControl(sqlite3_file*, int op, void *pArg);
static int vlogSectorSize(sqlite3_file*);
static int vlogDeviceCharacteristics(sqlite3_file*);
/*
** Methods for VLogVfs
*/
static int vlogOpen(sqlite3_vfs*, const char *, sqlite3_file*, int , int *);
static int vlogDelete(sqlite3_vfs*, const char *zName, int syncDir);
static int vlogAccess(sqlite3_vfs*, const char *zName, int flags, int *);
static int vlogFullPathname(sqlite3_vfs*, const char *zName, int, char *zOut);
static void *vlogDlOpen(sqlite3_vfs*, const char *zFilename);
static void vlogDlError(sqlite3_vfs*, int nByte, char *zErrMsg);
static void (*vlogDlSym(sqlite3_vfs *pVfs, void *p, const char*zSym))(void);
static void vlogDlClose(sqlite3_vfs*, void*);
static int vlogRandomness(sqlite3_vfs*, int nByte, char *zOut);
static int vlogSleep(sqlite3_vfs*, int microseconds);
static int vlogCurrentTime(sqlite3_vfs*, double*);
static int vlogGetLastError(sqlite3_vfs*, int, char *);
static int vlogCurrentTimeInt64(sqlite3_vfs*, sqlite3_int64*);
static VLogVfs vlog_vfs = {
{
1, /* iVersion */
0, /* szOsFile (set by register_vlog()) */
1024, /* mxPathname */
0, /* pNext */
"vfslog", /* zName */
0, /* pAppData */
vlogOpen, /* xOpen */
vlogDelete, /* xDelete */
vlogAccess, /* xAccess */
vlogFullPathname, /* xFullPathname */
vlogDlOpen, /* xDlOpen */
vlogDlError, /* xDlError */
vlogDlSym, /* xDlSym */
vlogDlClose, /* xDlClose */
vlogRandomness, /* xRandomness */
vlogSleep, /* xSleep */
vlogCurrentTime, /* xCurrentTime */
vlogGetLastError, /* xGetLastError */
vlogCurrentTimeInt64 /* xCurrentTimeInt64 */
},
0
};
static sqlite3_io_methods vlog_io_methods = {
1, /* iVersion */
vlogClose, /* xClose */
vlogRead, /* xRead */
vlogWrite, /* xWrite */
vlogTruncate, /* xTruncate */
vlogSync, /* xSync */
vlogFileSize, /* xFileSize */
vlogLock, /* xLock */
vlogUnlock, /* xUnlock */
vlogCheckReservedLock, /* xCheckReservedLock */
vlogFileControl, /* xFileControl */
vlogSectorSize, /* xSectorSize */
vlogDeviceCharacteristics, /* xDeviceCharacteristics */
0, /* xShmMap */
0, /* xShmLock */
0, /* xShmBarrier */
0 /* xShmUnmap */
};
#if SQLITE_OS_UNIX && !defined(NO_GETTOD)
#include <sys/time.h>
static sqlite3_uint64 vlog_time(){
struct timeval sTime;
gettimeofday(&sTime, 0);
return sTime.tv_usec + (sqlite3_uint64)sTime.tv_sec * 1000000;
}
#elif SQLITE_OS_WIN
#include <windows.h>
#include <time.h>
static sqlite3_uint64 vlog_time(){
FILETIME ft;
sqlite3_uint64 u64time = 0;
GetSystemTimeAsFileTime(&ft);
u64time |= ft.dwHighDateTime;
u64time <<= 32;
u64time |= ft.dwLowDateTime;
/* ft is 100-nanosecond intervals, we want microseconds */
return u64time /(sqlite3_uint64)10;
}
#else
static sqlite3_uint64 vlog_time(){
return 0;
}
#endif
/*
** Write a message to the log file
*/
static void vlogLogPrint(
VLogLog *pLog, /* The log file to write into */
sqlite3_int64 tStart, /* Start time of system call */
sqlite3_int64 tElapse, /* Elapse time of system call */
const char *zOp, /* Type of system call */
sqlite3_int64 iArg1, /* First argument */
sqlite3_int64 iArg2, /* Second argument */
const char *zArg3, /* Third argument */
int iRes /* Result */
){
char z1[40], z2[40], z3[70];
if( pLog==0 ) return;
if( iArg1>=0 ){
sqlite3_snprintf(sizeof(z1), z1, "%lld", iArg1);
}else{
z1[0] = 0;
}
if( iArg2>=0 ){
sqlite3_snprintf(sizeof(z2), z2, "%lld", iArg2);
}else{
z2[0] = 0;
}
if( zArg3 ){
sqlite3_snprintf(sizeof(z3), z3, "\"%s\"", zArg3);
}else{
z3[0] = 0;
}
fprintf(pLog->out,"%lld,%lld,%s,%d,%s,%s,%s,%d\n",
tStart, tElapse, zOp, pLog->zFilename==0, z1, z2, z3, iRes);
}
/*
** List of all active log connections. Protected by the master mutex.
*/
static VLogLog *allLogs = 0;
/*
** Close a VLogLog object
*/
static void vlogLogClose(VLogLog *p){
if( p ){
sqlite3_mutex *pMutex;
p->nRef--;
if( p->nRef>0 || p->zFilename==0 ) return;
pMutex = sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER);
sqlite3_mutex_enter(pMutex);
*p->ppPrev = p->pNext;
if( p->pNext ) p->pNext->ppPrev = p->ppPrev;
sqlite3_mutex_leave(pMutex);
fclose(p->out);
sqlite3_free(p);
}
}
/*
** Open a VLogLog object on the given file
*/
static VLogLog *vlogLogOpen(const char *zFilename){
int nName = (int)strlen(zFilename);
int isJournal = 0;
sqlite3_mutex *pMutex;
VLogLog *pLog, *pTemp;
sqlite3_int64 tNow = 0;
if( nName>4 && strcmp(zFilename+nName-4,"-wal")==0 ){
return 0; /* Do not log wal files */
}else
if( nName>8 && strcmp(zFilename+nName-8,"-journal")==0 ){
nName -= 8;
isJournal = 1;
}else if( nName>12
&& sqlite3_strglob("-mj??????9??", zFilename+nName-12)==0 ){
return 0; /* Do not log master journal files */
}
pTemp = sqlite3_malloc( sizeof(*pLog)*2 + nName + 60 );
if( pTemp==0 ) return 0;
pMutex = sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER);
sqlite3_mutex_enter(pMutex);
for(pLog=allLogs; pLog; pLog=pLog->pNext){
if( pLog->nFilename==nName && !memcmp(pLog->zFilename, zFilename, nName) ){
break;
}
}
if( pLog==0 ){
pLog = pTemp;
pTemp = 0;
memset(pLog, 0, sizeof(*pLog)*2);
pLog->zFilename = (char*)&pLog[2];
tNow = vlog_time();
sqlite3_snprintf(nName+60, pLog->zFilename, "%.*s-debuglog-%lld",
nName, zFilename, tNow);
pLog->out = fopen(pLog->zFilename, "a");
if( pLog->out==0 ){
sqlite3_mutex_leave(pMutex);
sqlite3_free(pLog);
return 0;
}
pLog->nFilename = nName;
pLog[1].out = pLog[0].out;
pLog->ppPrev = &allLogs;
if( allLogs ) allLogs->ppPrev = &pLog->pNext;
pLog->pNext = allLogs;
allLogs = pLog;
}
sqlite3_mutex_leave(pMutex);
if( pTemp ){
sqlite3_free(pTemp);
}else{
char zHost[200];
zHost[0] = 0;
gethostname(zHost, sizeof(zHost)-1);
zHost[sizeof(zHost)-1] = 0;
vlogLogPrint(pLog, tNow, 0, "IDENT", getpid(), -1, zHost, 0);
}
if( pLog && isJournal ) pLog++;
pLog->nRef++;
return pLog;
}
/*
** Close an vlog-file.
*/
static int vlogClose(sqlite3_file *pFile){
sqlite3_uint64 tStart, tElapse;
int rc = SQLITE_OK;
VLogFile *p = (VLogFile *)pFile;
tStart = vlog_time();
if( p->pReal->pMethods ){
rc = p->pReal->pMethods->xClose(p->pReal);
}
tElapse = vlog_time() - tStart;
vlogLogPrint(p->pLog, tStart, tElapse, "CLOSE", -1, -1, 0, rc);
vlogLogClose(p->pLog);
return rc;
}
/*
** Compute signature for a block of content.
**
** For blocks of 16 or fewer bytes, the signature is just a hex dump of
** the entire block.
**
** For blocks of more than 16 bytes, the signature is a hex dump of the
** first 8 bytes followed by a 64-bit has of the entire block.
*/
static void vlogSignature(unsigned char *p, int n, char *zCksum){
unsigned int s0 = 0, s1 = 0;
unsigned int *pI;
int i;
if( n<=16 ){
for(i=0; i<n; i++) sqlite3_snprintf(3, zCksum+i*2, "%02x", p[i]);
}else{
pI = (unsigned int*)p;
for(i=0; i<n-7; i+=8){
s0 += pI[0] + s1;
s1 += pI[1] + s0;
pI += 2;
}
for(i=0; i<8; i++) sqlite3_snprintf(3, zCksum+i*2, "%02x", p[i]);
sqlite3_snprintf(18, zCksum+i*2, "-%08x%08x", s0, s1);
}
}
/*
** Read data from an vlog-file.
*/
static int vlogRead(
sqlite3_file *pFile,
void *zBuf,
int iAmt,
sqlite_int64 iOfst
){
int rc;
sqlite3_uint64 tStart, tElapse;
VLogFile *p = (VLogFile *)pFile;
char zSig[40];
tStart = vlog_time();
rc = p->pReal->pMethods->xRead(p->pReal, zBuf, iAmt, iOfst);
tElapse = vlog_time() - tStart;
if( rc==SQLITE_OK ){
vlogSignature(zBuf, iAmt, zSig);
}else{
zSig[0] = 0;
}
vlogLogPrint(p->pLog, tStart, tElapse, "READ", iAmt, iOfst, zSig, rc);
if( rc==SQLITE_OK
&& p->pLog
&& p->pLog->zFilename
&& iOfst<=24
&& iOfst+iAmt>=28
){
unsigned char *x = ((unsigned char*)zBuf)+(24-iOfst);
unsigned iCtr;
iCtr = (x[0]<<24) + (x[1]<<16) + (x[2]<<8) + x[3];
vlogLogPrint(p->pLog, tStart, 0, "CHNGCTR-READ", iCtr, -1, 0, 0);
}
return rc;
}
/*
** Write data to an vlog-file.
*/
static int vlogWrite(
sqlite3_file *pFile,
const void *z,
int iAmt,
sqlite_int64 iOfst
){
int rc;
sqlite3_uint64 tStart, tElapse;
VLogFile *p = (VLogFile *)pFile;
char zSig[40];
tStart = vlog_time();
vlogSignature((unsigned char*)z, iAmt, zSig);
rc = p->pReal->pMethods->xWrite(p->pReal, z, iAmt, iOfst);
tElapse = vlog_time() - tStart;
vlogLogPrint(p->pLog, tStart, tElapse, "WRITE", iAmt, iOfst, zSig, rc);
if( rc==SQLITE_OK
&& p->pLog
&& p->pLog->zFilename
&& iOfst<=24
&& iOfst+iAmt>=28
){
unsigned char *x = ((unsigned char*)z)+(24-iOfst);
unsigned iCtr;
iCtr = (x[0]<<24) + (x[1]<<16) + (x[2]<<8) + x[3];
vlogLogPrint(p->pLog, tStart, 0, "CHNGCTR-WRITE", iCtr, -1, 0, 0);
}
return rc;
}
/*
** Truncate an vlog-file.
*/
static int vlogTruncate(sqlite3_file *pFile, sqlite_int64 size){
int rc;
sqlite3_uint64 tStart, tElapse;
VLogFile *p = (VLogFile *)pFile;
tStart = vlog_time();
rc = p->pReal->pMethods->xTruncate(p->pReal, size);
tElapse = vlog_time() - tStart;
vlogLogPrint(p->pLog, tStart, tElapse, "TRUNCATE", size, -1, 0, rc);
return rc;
}
/*
** Sync an vlog-file.
*/
static int vlogSync(sqlite3_file *pFile, int flags){
int rc;
sqlite3_uint64 tStart, tElapse;
VLogFile *p = (VLogFile *)pFile;
tStart = vlog_time();
rc = p->pReal->pMethods->xSync(p->pReal, flags);
tElapse = vlog_time() - tStart;
vlogLogPrint(p->pLog, tStart, tElapse, "SYNC", flags, -1, 0, rc);
return rc;
}
/*
** Return the current file-size of an vlog-file.
*/
static int vlogFileSize(sqlite3_file *pFile, sqlite_int64 *pSize){
int rc;
sqlite3_uint64 tStart, tElapse;
VLogFile *p = (VLogFile *)pFile;
tStart = vlog_time();
rc = p->pReal->pMethods->xFileSize(p->pReal, pSize);
tElapse = vlog_time() - tStart;
vlogLogPrint(p->pLog, tStart, tElapse, "FILESIZE", *pSize, -1, 0, rc);
return rc;
}
/*
** Lock an vlog-file.
*/
static int vlogLock(sqlite3_file *pFile, int eLock){
int rc;
sqlite3_uint64 tStart, tElapse;
VLogFile *p = (VLogFile *)pFile;
tStart = vlog_time();
rc = p->pReal->pMethods->xLock(p->pReal, eLock);
tElapse = vlog_time() - tStart;
vlogLogPrint(p->pLog, tStart, tElapse, "LOCK", eLock, -1, 0, rc);
return rc;
}
/*
** Unlock an vlog-file.
*/
static int vlogUnlock(sqlite3_file *pFile, int eLock){
int rc;
sqlite3_uint64 tStart, tElapse;
VLogFile *p = (VLogFile *)pFile;
tStart = vlog_time();
rc = p->pReal->pMethods->xUnlock(p->pReal, eLock);
tElapse = vlog_time() - tStart;
vlogLogPrint(p->pLog, tStart, tElapse, "UNLOCK", eLock, -1, 0, rc);
return rc;
}
/*
** Check if another file-handle holds a RESERVED lock on an vlog-file.
*/
static int vlogCheckReservedLock(sqlite3_file *pFile, int *pResOut){
int rc;
sqlite3_uint64 tStart, tElapse;
VLogFile *p = (VLogFile *)pFile;
tStart = vlog_time();
rc = p->pReal->pMethods->xCheckReservedLock(p->pReal, pResOut);
tElapse = vlog_time() - tStart;
vlogLogPrint(p->pLog, tStart, tElapse, "CHECKRESERVEDLOCK",
*pResOut, -1, "", rc);
return rc;
}
/*
** File control method. For custom operations on an vlog-file.
*/
static int vlogFileControl(sqlite3_file *pFile, int op, void *pArg){
VLogFile *p = (VLogFile *)pFile;
sqlite3_uint64 tStart, tElapse;
int rc;
tStart = vlog_time();
rc = p->pReal->pMethods->xFileControl(p->pReal, op, pArg);
if( op==SQLITE_FCNTL_VFSNAME && rc==SQLITE_OK ){
*(char**)pArg = sqlite3_mprintf("vlog/%z", *(char**)pArg);
}
tElapse = vlog_time() - tStart;
vlogLogPrint(p->pLog, tStart, tElapse, "FILECONTROL", op, -1, 0, rc);
return rc;
}
/*
** Return the sector-size in bytes for an vlog-file.
*/
static int vlogSectorSize(sqlite3_file *pFile){
int rc;
sqlite3_uint64 tStart, tElapse;
VLogFile *p = (VLogFile *)pFile;
tStart = vlog_time();
rc = p->pReal->pMethods->xSectorSize(p->pReal);
tElapse = vlog_time() - tStart;
vlogLogPrint(p->pLog, tStart, tElapse, "SECTORSIZE", -1, -1, 0, rc);
return rc;
}
/*
** Return the device characteristic flags supported by an vlog-file.
*/
static int vlogDeviceCharacteristics(sqlite3_file *pFile){
int rc;
sqlite3_uint64 tStart, tElapse;
VLogFile *p = (VLogFile *)pFile;
tStart = vlog_time();
rc = p->pReal->pMethods->xDeviceCharacteristics(p->pReal);
tElapse = vlog_time() - tStart;
vlogLogPrint(p->pLog, tStart, tElapse, "DEVCHAR", -1, -1, 0, rc);
return rc;
}
/*
** Open an vlog file handle.
*/
static int vlogOpen(
sqlite3_vfs *pVfs,
const char *zName,
sqlite3_file *pFile,
int flags,
int *pOutFlags
){
int rc;
sqlite3_uint64 tStart, tElapse;
sqlite3_int64 iArg2;
VLogFile *p = (VLogFile*)pFile;
p->pReal = (sqlite3_file*)&p[1];
if( (flags & (SQLITE_OPEN_MAIN_DB|SQLITE_OPEN_MAIN_JOURNAL))!=0 ){
p->pLog = vlogLogOpen(zName);
}else{
p->pLog = 0;
}
tStart = vlog_time();
rc = REALVFS(pVfs)->xOpen(REALVFS(pVfs), zName, p->pReal, flags, pOutFlags);
tElapse = vlog_time() - tStart;
iArg2 = pOutFlags ? *pOutFlags : -1;
vlogLogPrint(p->pLog, tStart, tElapse, "OPEN", flags, iArg2, 0, rc);
if( rc==SQLITE_OK ){
pFile->pMethods = &vlog_io_methods;
}else{
if( p->pLog ) vlogLogClose(p->pLog);
p->pLog = 0;
}
return rc;
}
/*
** Delete the file located at zPath. If the dirSync argument is true,
** ensure the file-system modifications are synced to disk before
** returning.
*/
static int vlogDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){
int rc;
sqlite3_uint64 tStart, tElapse;
VLogLog *pLog;
tStart = vlog_time();
rc = REALVFS(pVfs)->xDelete(REALVFS(pVfs), zPath, dirSync);
tElapse = vlog_time() - tStart;
pLog = vlogLogOpen(zPath);
vlogLogPrint(pLog, tStart, tElapse, "DELETE", dirSync, -1, 0, rc);
vlogLogClose(pLog);
return rc;
}
/*
** Test for access permissions. Return true if the requested permission
** is available, or false otherwise.
*/
static int vlogAccess(
sqlite3_vfs *pVfs,
const char *zPath,
int flags,
int *pResOut
){
int rc;
sqlite3_uint64 tStart, tElapse;
VLogLog *pLog;
tStart = vlog_time();
rc = REALVFS(pVfs)->xAccess(REALVFS(pVfs), zPath, flags, pResOut);
tElapse = vlog_time() - tStart;
pLog = vlogLogOpen(zPath);
vlogLogPrint(pLog, tStart, tElapse, "ACCESS", flags, *pResOut, 0, rc);
vlogLogClose(pLog);
return rc;
}
/*
** Populate buffer zOut with the full canonical pathname corresponding
** to the pathname in zPath. zOut is guaranteed to point to a buffer
** of at least (INST_MAX_PATHNAME+1) bytes.
*/
static int vlogFullPathname(
sqlite3_vfs *pVfs,
const char *zPath,
int nOut,
char *zOut
){
return REALVFS(pVfs)->xFullPathname(REALVFS(pVfs), zPath, nOut, zOut);
}
/*
** Open the dynamic library located at zPath and return a handle.
*/
static void *vlogDlOpen(sqlite3_vfs *pVfs, const char *zPath){
return REALVFS(pVfs)->xDlOpen(REALVFS(pVfs), zPath);
}
/*
** Populate the buffer zErrMsg (size nByte bytes) with a human readable
** utf-8 string describing the most recent error encountered associated
** with dynamic libraries.
*/
static void vlogDlError(sqlite3_vfs *pVfs, int nByte, char *zErrMsg){
REALVFS(pVfs)->xDlError(REALVFS(pVfs), nByte, zErrMsg);
}
/*
** Return a pointer to the symbol zSymbol in the dynamic library pHandle.
*/
static void (*vlogDlSym(sqlite3_vfs *pVfs, void *p, const char *zSym))(void){
return REALVFS(pVfs)->xDlSym(REALVFS(pVfs), p, zSym);
}
/*
** Close the dynamic library handle pHandle.
*/
static void vlogDlClose(sqlite3_vfs *pVfs, void *pHandle){
REALVFS(pVfs)->xDlClose(REALVFS(pVfs), pHandle);
}
/*
** Populate the buffer pointed to by zBufOut with nByte bytes of
** random data.
*/
static int vlogRandomness(sqlite3_vfs *pVfs, int nByte, char *zBufOut){
return REALVFS(pVfs)->xRandomness(REALVFS(pVfs), nByte, zBufOut);
}
/*
** Sleep for nMicro microseconds. Return the number of microseconds
** actually slept.
*/
static int vlogSleep(sqlite3_vfs *pVfs, int nMicro){
return REALVFS(pVfs)->xSleep(REALVFS(pVfs), nMicro);
}
/*
** Return the current time as a Julian Day number in *pTimeOut.
*/
static int vlogCurrentTime(sqlite3_vfs *pVfs, double *pTimeOut){
return REALVFS(pVfs)->xCurrentTime(REALVFS(pVfs), pTimeOut);
}
static int vlogGetLastError(sqlite3_vfs *pVfs, int a, char *b){
return REALVFS(pVfs)->xGetLastError(REALVFS(pVfs), a, b);
}
static int vlogCurrentTimeInt64(sqlite3_vfs *pVfs, sqlite3_int64 *p){
return REALVFS(pVfs)->xCurrentTimeInt64(REALVFS(pVfs), p);
}
/*
** Register debugvfs as the default VFS for this process.
*/
int sqlite3_register_vfslog(const char *zArg){
vlog_vfs.pVfs = sqlite3_vfs_find(0);
vlog_vfs.base.szOsFile = sizeof(VLogFile) + vlog_vfs.pVfs->szOsFile;
return sqlite3_vfs_register(&vlog_vfs.base, 1);
}