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:
525
ext/fts3/fts3.c
525
ext/fts3/fts3.c
@ -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
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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) */
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
|
@ -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
701
ext/misc/vfslog.c
Normal 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);
|
||||
}
|
Reference in New Issue
Block a user