mirror of
https://github.com/sqlite/sqlite.git
synced 2025-08-08 14:02:16 +03:00
Merge the 3.8.12 beta changes from trunk.
FossilOrigin-Name: 35b1b8d4b97715030700e37b292bb4f1bb3f44d6
This commit is contained in:
@@ -793,7 +793,8 @@ static int fts3ExprBalance(Fts3Expr **pp, int nMaxDepth){
|
||||
rc = SQLITE_ERROR;
|
||||
}
|
||||
|
||||
if( rc==SQLITE_OK && (eType==FTSQUERY_AND || eType==FTSQUERY_OR) ){
|
||||
if( rc==SQLITE_OK ){
|
||||
if( (eType==FTSQUERY_AND || eType==FTSQUERY_OR) ){
|
||||
Fts3Expr **apLeaf;
|
||||
apLeaf = (Fts3Expr **)sqlite3_malloc(sizeof(Fts3Expr *) * nMaxDepth);
|
||||
if( 0==apLeaf ){
|
||||
@@ -910,6 +911,31 @@ static int fts3ExprBalance(Fts3Expr **pp, int nMaxDepth){
|
||||
assert( pFree==0 );
|
||||
sqlite3_free( apLeaf );
|
||||
}
|
||||
}else if( eType==FTSQUERY_NOT ){
|
||||
Fts3Expr *pLeft = pRoot->pLeft;
|
||||
Fts3Expr *pRight = pRoot->pRight;
|
||||
|
||||
pRoot->pLeft = 0;
|
||||
pRoot->pRight = 0;
|
||||
pLeft->pParent = 0;
|
||||
pRight->pParent = 0;
|
||||
|
||||
rc = fts3ExprBalance(&pLeft, nMaxDepth-1);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = fts3ExprBalance(&pRight, nMaxDepth-1);
|
||||
}
|
||||
|
||||
if( rc!=SQLITE_OK ){
|
||||
sqlite3Fts3ExprFree(pRight);
|
||||
sqlite3Fts3ExprFree(pLeft);
|
||||
}else{
|
||||
assert( pLeft && pRight );
|
||||
pRoot->pLeft = pLeft;
|
||||
pLeft->pParent = pRoot;
|
||||
pRoot->pRight = pRight;
|
||||
pRight->pParent = pRoot;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( rc!=SQLITE_OK ){
|
||||
|
@@ -1322,14 +1322,19 @@ static int fts3SegReaderNext(
|
||||
|
||||
if( fts3SegReaderIsPending(pReader) ){
|
||||
Fts3HashElem *pElem = *(pReader->ppNextElem);
|
||||
if( pElem==0 ){
|
||||
sqlite3_free(pReader->aNode);
|
||||
pReader->aNode = 0;
|
||||
}else{
|
||||
if( pElem ){
|
||||
char *aCopy;
|
||||
PendingList *pList = (PendingList *)fts3HashData(pElem);
|
||||
int nCopy = pList->nData+1;
|
||||
pReader->zTerm = (char *)fts3HashKey(pElem);
|
||||
pReader->nTerm = fts3HashKeysize(pElem);
|
||||
pReader->nNode = pReader->nDoclist = pList->nData + 1;
|
||||
pReader->aNode = pReader->aDoclist = pList->aData;
|
||||
aCopy = (char*)sqlite3_malloc(nCopy);
|
||||
if( !aCopy ) return SQLITE_NOMEM;
|
||||
memcpy(aCopy, pList->aData, nCopy);
|
||||
pReader->nNode = pReader->nDoclist = nCopy;
|
||||
pReader->aNode = pReader->aDoclist = aCopy;
|
||||
pReader->ppNextElem++;
|
||||
assert( pReader->aNode );
|
||||
}
|
||||
@@ -1569,12 +1574,14 @@ int sqlite3Fts3MsrOvfl(
|
||||
** second argument.
|
||||
*/
|
||||
void sqlite3Fts3SegReaderFree(Fts3SegReader *pReader){
|
||||
if( pReader && !fts3SegReaderIsPending(pReader) ){
|
||||
if( pReader ){
|
||||
if( !fts3SegReaderIsPending(pReader) ){
|
||||
sqlite3_free(pReader->zTerm);
|
||||
}
|
||||
if( !fts3SegReaderIsRootOnly(pReader) ){
|
||||
sqlite3_free(pReader->aNode);
|
||||
sqlite3_blob_close(pReader->pBlob);
|
||||
}
|
||||
sqlite3_blob_close(pReader->pBlob);
|
||||
}
|
||||
sqlite3_free(pReader);
|
||||
}
|
||||
|
@@ -81,6 +81,20 @@ extern int sqlite3_fts5_may_be_corrupt;
|
||||
#endif
|
||||
|
||||
typedef struct Fts5Global Fts5Global;
|
||||
typedef struct Fts5Colset Fts5Colset;
|
||||
|
||||
/* If a NEAR() clump or phrase may only match a specific set of columns,
|
||||
** then an object of the following type is used to record the set of columns.
|
||||
** Each entry in the aiCol[] array is a column that may be matched.
|
||||
**
|
||||
** This object is used by fts5_expr.c and fts5_index.c.
|
||||
*/
|
||||
struct Fts5Colset {
|
||||
int nCol;
|
||||
int aiCol[1];
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
** Interface to code in fts5_config.c. fts5_config.c contains contains code
|
||||
@@ -305,7 +319,7 @@ int sqlite3Fts5IndexClose(Fts5Index *p);
|
||||
|
||||
/*
|
||||
** for(
|
||||
** pIter = sqlite3Fts5IndexQuery(p, "token", 5, 0);
|
||||
** sqlite3Fts5IndexQuery(p, "token", 5, 0, 0, &pIter);
|
||||
** 0==sqlite3Fts5IterEof(pIter);
|
||||
** sqlite3Fts5IterNext(pIter)
|
||||
** ){
|
||||
@@ -321,7 +335,8 @@ int sqlite3Fts5IndexQuery(
|
||||
Fts5Index *p, /* FTS index to query */
|
||||
const char *pToken, int nToken, /* Token (or prefix) to query for */
|
||||
int flags, /* Mask of FTS5INDEX_QUERY_X flags */
|
||||
Fts5IndexIter **ppIter
|
||||
Fts5Colset *pColset, /* Match these columns only */
|
||||
Fts5IndexIter **ppIter /* OUT: New iterator object */
|
||||
);
|
||||
|
||||
/*
|
||||
@@ -370,6 +385,7 @@ int sqlite3Fts5IndexWrite(
|
||||
*/
|
||||
int sqlite3Fts5IndexBeginWrite(
|
||||
Fts5Index *p, /* Index to write to */
|
||||
int bDelete, /* True if current operation is a delete */
|
||||
i64 iDocid /* Docid to add or remove data from */
|
||||
);
|
||||
|
||||
@@ -434,6 +450,15 @@ int sqlite3Fts5PutVarint(unsigned char *p, u64 v);
|
||||
#define fts5GetVarint32(a,b) sqlite3Fts5GetVarint32(a,(u32*)&b)
|
||||
#define fts5GetVarint sqlite3Fts5GetVarint
|
||||
|
||||
#define fts5FastGetVarint32(a, iOff, nVal) { \
|
||||
nVal = (a)[iOff++]; \
|
||||
if( nVal & 0x80 ){ \
|
||||
iOff--; \
|
||||
iOff += fts5GetVarint32(&(a)[iOff], nVal); \
|
||||
} \
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** End of interface to code in fts5_varint.c.
|
||||
**************************************************************************/
|
||||
@@ -526,7 +551,8 @@ int sqlite3Fts5DropAll(Fts5Config*);
|
||||
int sqlite3Fts5CreateTable(Fts5Config*, const char*, const char*, int, char **);
|
||||
|
||||
int sqlite3Fts5StorageDelete(Fts5Storage *p, i64);
|
||||
int sqlite3Fts5StorageInsert(Fts5Storage *p, sqlite3_value **apVal, int, i64*);
|
||||
int sqlite3Fts5StorageContentInsert(Fts5Storage *p, sqlite3_value**, i64*);
|
||||
int sqlite3Fts5StorageIndexInsert(Fts5Storage *p, sqlite3_value**, i64);
|
||||
|
||||
int sqlite3Fts5StorageIntegrity(Fts5Storage *p);
|
||||
|
||||
@@ -565,7 +591,6 @@ typedef struct Fts5Parse Fts5Parse;
|
||||
typedef struct Fts5Token Fts5Token;
|
||||
typedef struct Fts5ExprPhrase Fts5ExprPhrase;
|
||||
typedef struct Fts5ExprNearset Fts5ExprNearset;
|
||||
typedef struct Fts5ExprColset Fts5ExprColset;
|
||||
|
||||
struct Fts5Token {
|
||||
const char *p; /* Token text (not NULL terminated) */
|
||||
@@ -633,9 +658,9 @@ Fts5ExprNearset *sqlite3Fts5ParseNearset(
|
||||
Fts5ExprPhrase*
|
||||
);
|
||||
|
||||
Fts5ExprColset *sqlite3Fts5ParseColset(
|
||||
Fts5Colset *sqlite3Fts5ParseColset(
|
||||
Fts5Parse*,
|
||||
Fts5ExprColset*,
|
||||
Fts5Colset*,
|
||||
Fts5Token *
|
||||
);
|
||||
|
||||
@@ -644,7 +669,7 @@ void sqlite3Fts5ParseNearsetFree(Fts5ExprNearset*);
|
||||
void sqlite3Fts5ParseNodeFree(Fts5ExprNode*);
|
||||
|
||||
void sqlite3Fts5ParseSetDistance(Fts5Parse*, Fts5ExprNearset*, Fts5Token*);
|
||||
void sqlite3Fts5ParseSetColset(Fts5Parse*, Fts5ExprNearset*, Fts5ExprColset*);
|
||||
void sqlite3Fts5ParseSetColset(Fts5Parse*, Fts5ExprNearset*, Fts5Colset*);
|
||||
void sqlite3Fts5ParseFinished(Fts5Parse *pParse, Fts5ExprNode *p);
|
||||
void sqlite3Fts5ParseNear(Fts5Parse *pParse, Fts5Token*);
|
||||
|
||||
|
@@ -185,11 +185,11 @@ int sqlite3Fts5PoslistNext64(
|
||||
}else{
|
||||
i64 iOff = *piOff;
|
||||
int iVal;
|
||||
i += fts5GetVarint32(&a[i], iVal);
|
||||
fts5FastGetVarint32(a, i, iVal);
|
||||
if( iVal==1 ){
|
||||
i += fts5GetVarint32(&a[i], iVal);
|
||||
fts5FastGetVarint32(a, i, iVal);
|
||||
iOff = ((i64)iVal) << 32;
|
||||
i += fts5GetVarint32(&a[i], iVal);
|
||||
fts5FastGetVarint32(a, i, iVal);
|
||||
}
|
||||
*piOff = iOff + (iVal-2);
|
||||
*pi = i;
|
||||
@@ -233,13 +233,15 @@ int sqlite3Fts5PoslistWriterAppend(
|
||||
){
|
||||
static const i64 colmask = ((i64)(0x7FFFFFFF)) << 32;
|
||||
int rc = SQLITE_OK;
|
||||
if( 0==sqlite3Fts5BufferGrow(&rc, pBuf, 5+5+5) ){
|
||||
if( (iPos & colmask) != (pWriter->iPrev & colmask) ){
|
||||
fts5BufferAppendVarint(&rc, pBuf, 1);
|
||||
fts5BufferAppendVarint(&rc, pBuf, (iPos >> 32));
|
||||
pBuf->p[pBuf->n++] = 1;
|
||||
pBuf->n += sqlite3Fts5PutVarint(&pBuf->p[pBuf->n], (iPos>>32));
|
||||
pWriter->iPrev = (iPos & colmask);
|
||||
}
|
||||
fts5BufferAppendVarint(&rc, pBuf, (iPos - pWriter->iPrev) + 2);
|
||||
pBuf->n += sqlite3Fts5PutVarint(&pBuf->p[pBuf->n], (iPos-pWriter->iPrev)+2);
|
||||
pWriter->iPrev = iPos;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
@@ -290,11 +292,12 @@ char *sqlite3Fts5Strndup(int *pRc, const char *pIn, int nIn){
|
||||
** * The 52 upper and lower case ASCII characters, and
|
||||
** * The 10 integer ASCII characters.
|
||||
** * The underscore character "_" (0x5F).
|
||||
** * The unicode "subsitute" character (0x1A).
|
||||
*/
|
||||
int sqlite3Fts5IsBareword(char t){
|
||||
u8 aBareword[128] = {
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x00 .. 0x0F */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x10 .. 0x1F */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, /* 0x10 .. 0x1F */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x20 .. 0x2F */
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, /* 0x30 .. 0x3F */
|
||||
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x40 .. 0x4F */
|
||||
|
@@ -89,23 +89,13 @@ struct Fts5ExprPhrase {
|
||||
Fts5ExprTerm aTerm[1]; /* Terms that make up this phrase */
|
||||
};
|
||||
|
||||
/*
|
||||
** If a NEAR() clump may only match a specific set of columns, then
|
||||
** Fts5ExprNearset.pColset points to an object of the following type.
|
||||
** Each entry in the aiCol[] array
|
||||
*/
|
||||
struct Fts5ExprColset {
|
||||
int nCol;
|
||||
int aiCol[1];
|
||||
};
|
||||
|
||||
/*
|
||||
** One or more phrases that must appear within a certain token distance of
|
||||
** each other within each matching document.
|
||||
*/
|
||||
struct Fts5ExprNearset {
|
||||
int nNear; /* NEAR parameter */
|
||||
Fts5ExprColset *pColset; /* Columns to search (NULL -> all columns) */
|
||||
Fts5Colset *pColset; /* Columns to search (NULL -> all columns) */
|
||||
int nPhrase; /* Number of entries in aPhrase[] array */
|
||||
Fts5ExprPhrase *apPhrase[1]; /* Array of phrase pointers */
|
||||
};
|
||||
@@ -276,7 +266,7 @@ void sqlite3Fts5ExprFree(Fts5Expr *p){
|
||||
}
|
||||
}
|
||||
|
||||
static int fts5ExprColsetTest(Fts5ExprColset *pColset, int iCol){
|
||||
static int fts5ExprColsetTest(Fts5Colset *pColset, int iCol){
|
||||
int i;
|
||||
for(i=0; i<pColset->nCol; i++){
|
||||
if( pColset->aiCol[i]==iCol ) return 1;
|
||||
@@ -405,7 +395,7 @@ static int fts5ExprSynonymPoslist(
|
||||
*/
|
||||
static int fts5ExprPhraseIsMatch(
|
||||
Fts5ExprNode *pNode, /* Node pPhrase belongs to */
|
||||
Fts5ExprColset *pColset, /* Restrict matches to these columns */
|
||||
Fts5Colset *pColset, /* Restrict matches to these columns */
|
||||
Fts5ExprPhrase *pPhrase, /* Phrase object to initialize */
|
||||
int *pbMatch /* OUT: Set to true if really a match */
|
||||
){
|
||||
@@ -805,7 +795,7 @@ static int fts5ExprExtractCol(
|
||||
}
|
||||
|
||||
static int fts5ExprExtractColset (
|
||||
Fts5ExprColset *pColset, /* Colset to filter on */
|
||||
Fts5Colset *pColset, /* Colset to filter on */
|
||||
const u8 *pPos, int nPos, /* Position list */
|
||||
Fts5Buffer *pBuf /* Output buffer */
|
||||
){
|
||||
@@ -868,7 +858,7 @@ static int fts5ExprTokenTest(
|
||||
Fts5ExprNearset *pNear = pNode->pNear;
|
||||
Fts5ExprPhrase *pPhrase = pNear->apPhrase[0];
|
||||
Fts5IndexIter *pIter = pPhrase->aTerm[0].pIter;
|
||||
Fts5ExprColset *pColset = pNear->pColset;
|
||||
Fts5Colset *pColset = pNear->pColset;
|
||||
const u8 *pPos;
|
||||
int nPos;
|
||||
int rc;
|
||||
@@ -1002,6 +992,7 @@ static int fts5ExprNearInitAll(
|
||||
pExpr->pIndex, p->zTerm, strlen(p->zTerm),
|
||||
(pTerm->bPrefix ? FTS5INDEX_QUERY_PREFIX : 0) |
|
||||
(pExpr->bDesc ? FTS5INDEX_QUERY_DESC : 0),
|
||||
pNear->pColset,
|
||||
&p->pIter
|
||||
);
|
||||
assert( rc==SQLITE_OK || p->pIter==0 );
|
||||
@@ -1754,25 +1745,25 @@ void sqlite3Fts5ParseSetDistance(
|
||||
|
||||
/*
|
||||
** The second argument passed to this function may be NULL, or it may be
|
||||
** an existing Fts5ExprColset object. This function returns a pointer to
|
||||
** an existing Fts5Colset object. This function returns a pointer to
|
||||
** a new colset object containing the contents of (p) with new value column
|
||||
** number iCol appended.
|
||||
**
|
||||
** If an OOM error occurs, store an error code in pParse and return NULL.
|
||||
** The old colset object (if any) is not freed in this case.
|
||||
*/
|
||||
static Fts5ExprColset *fts5ParseColset(
|
||||
static Fts5Colset *fts5ParseColset(
|
||||
Fts5Parse *pParse, /* Store SQLITE_NOMEM here if required */
|
||||
Fts5ExprColset *p, /* Existing colset object */
|
||||
Fts5Colset *p, /* Existing colset object */
|
||||
int iCol /* New column to add to colset object */
|
||||
){
|
||||
int nCol = p ? p->nCol : 0; /* Num. columns already in colset object */
|
||||
Fts5ExprColset *pNew; /* New colset object to return */
|
||||
Fts5Colset *pNew; /* New colset object to return */
|
||||
|
||||
assert( pParse->rc==SQLITE_OK );
|
||||
assert( iCol>=0 && iCol<pParse->pConfig->nCol );
|
||||
|
||||
pNew = sqlite3_realloc(p, sizeof(Fts5ExprColset) + sizeof(int)*nCol);
|
||||
pNew = sqlite3_realloc(p, sizeof(Fts5Colset) + sizeof(int)*nCol);
|
||||
if( pNew==0 ){
|
||||
pParse->rc = SQLITE_NOMEM;
|
||||
}else{
|
||||
@@ -1797,12 +1788,12 @@ static Fts5ExprColset *fts5ParseColset(
|
||||
return pNew;
|
||||
}
|
||||
|
||||
Fts5ExprColset *sqlite3Fts5ParseColset(
|
||||
Fts5Colset *sqlite3Fts5ParseColset(
|
||||
Fts5Parse *pParse, /* Store SQLITE_NOMEM here if required */
|
||||
Fts5ExprColset *pColset, /* Existing colset object */
|
||||
Fts5Colset *pColset, /* Existing colset object */
|
||||
Fts5Token *p
|
||||
){
|
||||
Fts5ExprColset *pRet = 0;
|
||||
Fts5Colset *pRet = 0;
|
||||
int iCol;
|
||||
char *z; /* Dequoted copy of token p */
|
||||
|
||||
@@ -1832,7 +1823,7 @@ Fts5ExprColset *sqlite3Fts5ParseColset(
|
||||
void sqlite3Fts5ParseSetColset(
|
||||
Fts5Parse *pParse,
|
||||
Fts5ExprNearset *pNear,
|
||||
Fts5ExprColset *pColset
|
||||
Fts5Colset *pColset
|
||||
){
|
||||
if( pNear ){
|
||||
pNear->pColset = pColset;
|
||||
|
@@ -291,7 +291,7 @@ struct Fts5Index {
|
||||
int nMaxPendingData; /* Max pending data before flush to disk */
|
||||
int nPendingData; /* Current bytes of pending data */
|
||||
i64 iWriteRowid; /* Rowid for current doc being written */
|
||||
Fts5Buffer scratch;
|
||||
int bDelete; /* Current write is a delete */
|
||||
|
||||
/* Error state. */
|
||||
int rc; /* Current error code */
|
||||
@@ -307,14 +307,13 @@ struct Fts5Index {
|
||||
};
|
||||
|
||||
struct Fts5DoclistIter {
|
||||
u8 *a;
|
||||
int n;
|
||||
int i;
|
||||
u8 *aEof; /* Pointer to 1 byte past end of doclist */
|
||||
|
||||
/* Output variables. aPoslist==0 at EOF */
|
||||
i64 iRowid;
|
||||
u8 *aPoslist;
|
||||
int nPoslist;
|
||||
int nSize;
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -1780,6 +1779,7 @@ static void fts5SegIterNext(
|
||||
pIter->iEndofDoclist = nList+1;
|
||||
sqlite3Fts5BufferSet(&p->rc, &pIter->term, strlen(zTerm), (u8*)zTerm);
|
||||
pIter->iLeafOffset = fts5GetVarint(pList, (u64*)&pIter->iRowid);
|
||||
if( pbNewTerm ) *pbNewTerm = 1;
|
||||
}
|
||||
}else{
|
||||
iOff = 0;
|
||||
@@ -1940,14 +1940,6 @@ static void fts5SegIterLoadDlidx(Fts5Index *p, Fts5SegIter *pIter){
|
||||
pIter->pDlidx = fts5DlidxIterInit(p, bRev, iSeg, pIter->iTermLeafPgno);
|
||||
}
|
||||
|
||||
#define fts5IndexGetVarint32(a, iOff, nVal) { \
|
||||
nVal = (a)[iOff++]; \
|
||||
if( nVal & 0x80 ){ \
|
||||
iOff--; \
|
||||
iOff += fts5GetVarint32(&(a)[iOff], nVal); \
|
||||
} \
|
||||
}
|
||||
|
||||
#define fts5IndexSkipVarint(a, iOff) { \
|
||||
int iEnd = iOff+9; \
|
||||
while( (a[iOff++] & 0x80) && iOff<iEnd ); \
|
||||
@@ -1994,7 +1986,7 @@ static void fts5LeafSeek(
|
||||
while( 1 ){
|
||||
|
||||
/* Figure out how many new bytes are in this term */
|
||||
fts5IndexGetVarint32(a, iOff, nNew);
|
||||
fts5FastGetVarint32(a, iOff, nNew);
|
||||
if( nKeep<nMatch ){
|
||||
goto search_failed;
|
||||
}
|
||||
@@ -2030,7 +2022,7 @@ static void fts5LeafSeek(
|
||||
iOff = iTermOff;
|
||||
|
||||
/* Read the nKeep field of the next term. */
|
||||
fts5IndexGetVarint32(a, iOff, nKeep);
|
||||
fts5FastGetVarint32(a, iOff, nKeep);
|
||||
}
|
||||
|
||||
search_failed:
|
||||
@@ -3706,9 +3698,14 @@ static int fts5PoslistPrefix(const u8 *aBuf, int nMax){
|
||||
}
|
||||
|
||||
#define fts5BufferSafeAppendBlob(pBuf, pBlob, nBlob) { \
|
||||
assert( pBuf->nSpace>=(pBuf->n+nBlob) ); \
|
||||
memcpy(&pBuf->p[pBuf->n], pBlob, nBlob); \
|
||||
pBuf->n += nBlob; \
|
||||
assert( (pBuf)->nSpace>=((pBuf)->n+nBlob) ); \
|
||||
memcpy(&(pBuf)->p[(pBuf)->n], pBlob, nBlob); \
|
||||
(pBuf)->n += nBlob; \
|
||||
}
|
||||
|
||||
#define fts5BufferSafeAppendVarint(pBuf, iVal) { \
|
||||
(pBuf)->n += sqlite3Fts5PutVarint(&(pBuf)->p[(pBuf)->n], (iVal)); \
|
||||
assert( (pBuf)->nSpace>=(pBuf)->n ); \
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -3938,12 +3935,81 @@ int sqlite3Fts5IndexMerge(Fts5Index *p, int nMerge){
|
||||
|
||||
static void fts5PoslistCallback(
|
||||
Fts5Index *p,
|
||||
void *pCtx,
|
||||
void *pContext,
|
||||
const u8 *pChunk, int nChunk
|
||||
){
|
||||
assert_nc( nChunk>=0 );
|
||||
if( nChunk>0 ){
|
||||
fts5BufferAppendBlob(&p->rc, (Fts5Buffer*)pCtx, nChunk, pChunk);
|
||||
fts5BufferAppendBlob(&p->rc, (Fts5Buffer*)pContext, nChunk, pChunk);
|
||||
}
|
||||
}
|
||||
|
||||
typedef struct PoslistCallbackCtx PoslistCallbackCtx;
|
||||
struct PoslistCallbackCtx {
|
||||
Fts5Buffer *pBuf; /* Append to this buffer */
|
||||
Fts5Colset *pColset; /* Restrict matches to this column */
|
||||
int eState; /* See above */
|
||||
};
|
||||
|
||||
/*
|
||||
** TODO: Make this more efficient!
|
||||
*/
|
||||
static int fts5IndexColsetTest(Fts5Colset *pColset, int iCol){
|
||||
int i;
|
||||
for(i=0; i<pColset->nCol; i++){
|
||||
if( pColset->aiCol[i]==iCol ) return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void fts5PoslistFilterCallback(
|
||||
Fts5Index *p,
|
||||
void *pContext,
|
||||
const u8 *pChunk, int nChunk
|
||||
){
|
||||
PoslistCallbackCtx *pCtx = (PoslistCallbackCtx*)pContext;
|
||||
assert_nc( nChunk>=0 );
|
||||
if( nChunk>0 ){
|
||||
/* Search through to find the first varint with value 1. This is the
|
||||
** start of the next columns hits. */
|
||||
int i = 0;
|
||||
int iStart = 0;
|
||||
|
||||
if( pCtx->eState==2 ){
|
||||
int iCol;
|
||||
fts5FastGetVarint32(pChunk, i, iCol);
|
||||
if( fts5IndexColsetTest(pCtx->pColset, iCol) ){
|
||||
pCtx->eState = 1;
|
||||
fts5BufferAppendVarint(&p->rc, pCtx->pBuf, 1);
|
||||
}else{
|
||||
pCtx->eState = 0;
|
||||
}
|
||||
}
|
||||
|
||||
do {
|
||||
while( i<nChunk && pChunk[i]!=0x01 ){
|
||||
while( pChunk[i] & 0x80 ) i++;
|
||||
i++;
|
||||
}
|
||||
if( pCtx->eState ){
|
||||
fts5BufferAppendBlob(&p->rc, pCtx->pBuf, i-iStart, &pChunk[iStart]);
|
||||
}
|
||||
if( i<nChunk ){
|
||||
int iCol;
|
||||
iStart = i;
|
||||
i++;
|
||||
if( i>=nChunk ){
|
||||
pCtx->eState = 2;
|
||||
}else{
|
||||
fts5FastGetVarint32(pChunk, i, iCol);
|
||||
pCtx->eState = fts5IndexColsetTest(pCtx->pColset, iCol);
|
||||
if( pCtx->eState ){
|
||||
fts5BufferAppendBlob(&p->rc, pCtx->pBuf, i-iStart, &pChunk[iStart]);
|
||||
iStart = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
}while( i<nChunk );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3956,9 +4022,19 @@ static void fts5PoslistCallback(
|
||||
static void fts5SegiterPoslist(
|
||||
Fts5Index *p,
|
||||
Fts5SegIter *pSeg,
|
||||
Fts5Colset *pColset,
|
||||
Fts5Buffer *pBuf
|
||||
){
|
||||
if( pColset==0 ){
|
||||
fts5ChunkIterate(p, pSeg, (void*)pBuf, fts5PoslistCallback);
|
||||
}else{
|
||||
PoslistCallbackCtx sCtx;
|
||||
sCtx.pBuf = pBuf;
|
||||
sCtx.pColset = pColset;
|
||||
sCtx.eState = pColset ? fts5IndexColsetTest(pColset, 0) : 1;
|
||||
assert( sCtx.eState==0 || sCtx.eState==1 );
|
||||
fts5ChunkIterate(p, pSeg, (void*)&sCtx, fts5PoslistFilterCallback);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -3969,41 +4045,67 @@ static void fts5SegiterPoslist(
|
||||
** If an error occurs, an error code is left in p->rc. It is assumed
|
||||
** no error has already occurred when this function is called.
|
||||
*/
|
||||
static void fts5MultiIterPoslist(
|
||||
static int fts5MultiIterPoslist(
|
||||
Fts5Index *p,
|
||||
Fts5IndexIter *pMulti,
|
||||
int bSz, /* Append a size field before the data */
|
||||
Fts5Colset *pColset,
|
||||
Fts5Buffer *pBuf
|
||||
){
|
||||
if( p->rc==SQLITE_OK ){
|
||||
int iSz;
|
||||
int iData;
|
||||
|
||||
Fts5SegIter *pSeg = &pMulti->aSeg[ pMulti->aFirst[1].iFirst ];
|
||||
assert( fts5MultiIterEof(p, pMulti)==0 );
|
||||
|
||||
if( bSz ){
|
||||
/* WRITEPOSLISTSIZE */
|
||||
fts5BufferAppendVarint(&p->rc, pBuf, pSeg->nPos*2);
|
||||
iSz = pBuf->n;
|
||||
fts5BufferSafeAppendVarint(pBuf, pSeg->nPos*2);
|
||||
iData = pBuf->n;
|
||||
|
||||
fts5SegiterPoslist(p, pSeg, pColset, pBuf);
|
||||
|
||||
if( pColset ){
|
||||
int nActual = pBuf->n - iData;
|
||||
if( nActual!=pSeg->nPos ){
|
||||
/* WRITEPOSLISTSIZE */
|
||||
if( nActual==0 ){
|
||||
return 1;
|
||||
}else{
|
||||
int nReq = sqlite3Fts5GetVarintLen((u32)(nActual*2));
|
||||
while( iSz<(iData-nReq) ){ pBuf->p[iSz++] = 0x80; }
|
||||
sqlite3Fts5PutVarint(&pBuf->p[iSz], nActual*2);
|
||||
}
|
||||
}
|
||||
fts5SegiterPoslist(p, pSeg, pBuf);
|
||||
}
|
||||
}
|
||||
|
||||
static void fts5DoclistIterNext(Fts5DoclistIter *pIter){
|
||||
if( pIter->i<pIter->n ){
|
||||
int bDummy;
|
||||
if( pIter->i ){
|
||||
i64 iDelta;
|
||||
pIter->i += fts5GetVarint(&pIter->a[pIter->i], (u64*)&iDelta);
|
||||
pIter->iRowid += iDelta;
|
||||
}else{
|
||||
pIter->i += fts5GetVarint(&pIter->a[pIter->i], (u64*)&pIter->iRowid);
|
||||
return 0;
|
||||
}
|
||||
pIter->i += fts5GetPoslistSize(
|
||||
&pIter->a[pIter->i], &pIter->nPoslist, &bDummy
|
||||
);
|
||||
pIter->aPoslist = &pIter->a[pIter->i];
|
||||
pIter->i += pIter->nPoslist;
|
||||
}else{
|
||||
|
||||
static void fts5DoclistIterNext(Fts5DoclistIter *pIter){
|
||||
u8 *p = pIter->aPoslist + pIter->nSize + pIter->nPoslist;
|
||||
|
||||
assert( pIter->aPoslist );
|
||||
if( p>=pIter->aEof ){
|
||||
pIter->aPoslist = 0;
|
||||
}else{
|
||||
i64 iDelta;
|
||||
|
||||
p += fts5GetVarint(p, (u64*)&iDelta);
|
||||
pIter->iRowid += iDelta;
|
||||
|
||||
/* Read position list size */
|
||||
if( p[0] & 0x80 ){
|
||||
int nPos;
|
||||
pIter->nSize = fts5GetVarint32(p, nPos);
|
||||
pIter->nPoslist = (nPos>>1);
|
||||
}else{
|
||||
pIter->nPoslist = ((int)(p[0])) >> 1;
|
||||
pIter->nSize = 1;
|
||||
}
|
||||
|
||||
pIter->aPoslist = p;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4012,27 +4114,34 @@ static void fts5DoclistIterInit(
|
||||
Fts5DoclistIter *pIter
|
||||
){
|
||||
memset(pIter, 0, sizeof(*pIter));
|
||||
pIter->a = pBuf->p;
|
||||
pIter->n = pBuf->n;
|
||||
pIter->aPoslist = pBuf->p;
|
||||
pIter->aEof = &pBuf->p[pBuf->n];
|
||||
fts5DoclistIterNext(pIter);
|
||||
}
|
||||
|
||||
#if 0
|
||||
/*
|
||||
** Append a doclist to buffer pBuf.
|
||||
**
|
||||
** This function assumes that space within the buffer has already been
|
||||
** allocated.
|
||||
*/
|
||||
static void fts5MergeAppendDocid(
|
||||
int *pRc, /* IN/OUT: Error code */
|
||||
Fts5Buffer *pBuf, /* Buffer to write to */
|
||||
i64 *piLastRowid, /* IN/OUT: Previous rowid written (if any) */
|
||||
i64 iRowid /* Rowid to append */
|
||||
){
|
||||
if( pBuf->n==0 ){
|
||||
fts5BufferAppendVarint(pRc, pBuf, iRowid);
|
||||
}else{
|
||||
fts5BufferAppendVarint(pRc, pBuf, iRowid - *piLastRowid);
|
||||
}
|
||||
assert( pBuf->n!=0 || (*piLastRowid)==0 );
|
||||
fts5BufferSafeAppendVarint(pBuf, iRowid - *piLastRowid);
|
||||
*piLastRowid = iRowid;
|
||||
}
|
||||
#endif
|
||||
|
||||
#define fts5MergeAppendDocid(pBuf, iLastRowid, iRowid) { \
|
||||
assert( (pBuf)->n!=0 || (iLastRowid)==0 ); \
|
||||
fts5BufferSafeAppendVarint((pBuf), (iRowid) - (iLastRowid)); \
|
||||
(iLastRowid) = (iRowid); \
|
||||
}
|
||||
|
||||
/*
|
||||
** Buffers p1 and p2 contain doclists. This function merges the content
|
||||
@@ -4056,53 +4165,58 @@ static void fts5MergePrefixLists(
|
||||
memset(&out, 0, sizeof(out));
|
||||
memset(&tmp, 0, sizeof(tmp));
|
||||
|
||||
sqlite3Fts5BufferGrow(&p->rc, &out, p1->n + p2->n);
|
||||
fts5DoclistIterInit(p1, &i1);
|
||||
fts5DoclistIterInit(p2, &i2);
|
||||
while( p->rc==SQLITE_OK && (i1.aPoslist!=0 || i2.aPoslist!=0) ){
|
||||
if( i2.aPoslist==0 || (i1.aPoslist && i1.iRowid<i2.iRowid) ){
|
||||
/* Copy entry from i1 */
|
||||
fts5MergeAppendDocid(&p->rc, &out, &iLastRowid, i1.iRowid);
|
||||
/* WRITEPOSLISTSIZE */
|
||||
fts5BufferAppendVarint(&p->rc, &out, i1.nPoslist * 2);
|
||||
fts5BufferAppendBlob(&p->rc, &out, i1.nPoslist, i1.aPoslist);
|
||||
fts5MergeAppendDocid(&out, iLastRowid, i1.iRowid);
|
||||
fts5BufferSafeAppendBlob(&out, i1.aPoslist, i1.nPoslist+i1.nSize);
|
||||
fts5DoclistIterNext(&i1);
|
||||
}
|
||||
else if( i1.aPoslist==0 || i2.iRowid!=i1.iRowid ){
|
||||
/* Copy entry from i2 */
|
||||
fts5MergeAppendDocid(&p->rc, &out, &iLastRowid, i2.iRowid);
|
||||
/* WRITEPOSLISTSIZE */
|
||||
fts5BufferAppendVarint(&p->rc, &out, i2.nPoslist * 2);
|
||||
fts5BufferAppendBlob(&p->rc, &out, i2.nPoslist, i2.aPoslist);
|
||||
fts5MergeAppendDocid(&out, iLastRowid, i2.iRowid);
|
||||
fts5BufferSafeAppendBlob(&out, i2.aPoslist, i2.nPoslist+i2.nSize);
|
||||
fts5DoclistIterNext(&i2);
|
||||
}
|
||||
else{
|
||||
Fts5PoslistReader r1;
|
||||
Fts5PoslistReader r2;
|
||||
Fts5PoslistWriter writer;
|
||||
i64 iPos1 = 0;
|
||||
i64 iPos2 = 0;
|
||||
int iOff1 = 0;
|
||||
int iOff2 = 0;
|
||||
u8 *a1 = &i1.aPoslist[i1.nSize];
|
||||
u8 *a2 = &i2.aPoslist[i2.nSize];
|
||||
|
||||
Fts5PoslistWriter writer;
|
||||
memset(&writer, 0, sizeof(writer));
|
||||
|
||||
/* Merge the two position lists. */
|
||||
fts5MergeAppendDocid(&p->rc, &out, &iLastRowid, i2.iRowid);
|
||||
fts5MergeAppendDocid(&out, iLastRowid, i2.iRowid);
|
||||
fts5BufferZero(&tmp);
|
||||
sqlite3Fts5PoslistReaderInit(-1, i1.aPoslist, i1.nPoslist, &r1);
|
||||
sqlite3Fts5PoslistReaderInit(-1, i2.aPoslist, i2.nPoslist, &r2);
|
||||
while( p->rc==SQLITE_OK && (r1.bEof==0 || r2.bEof==0) ){
|
||||
|
||||
sqlite3Fts5PoslistNext64(a1, i1.nPoslist, &iOff1, &iPos1);
|
||||
sqlite3Fts5PoslistNext64(a2, i2.nPoslist, &iOff2, &iPos2);
|
||||
|
||||
while( p->rc==SQLITE_OK && (iPos1>=0 || iPos2>=0) ){
|
||||
i64 iNew;
|
||||
if( r2.bEof || (r1.bEof==0 && r1.iPos<r2.iPos) ){
|
||||
iNew = r1.iPos;
|
||||
sqlite3Fts5PoslistReaderNext(&r1);
|
||||
if( iPos2<0 || (iPos1>=0 && iPos1<iPos2) ){
|
||||
iNew = iPos1;
|
||||
sqlite3Fts5PoslistNext64(a1, i1.nPoslist, &iOff1, &iPos1);
|
||||
}else{
|
||||
iNew = r2.iPos;
|
||||
sqlite3Fts5PoslistReaderNext(&r2);
|
||||
if( r1.iPos==r2.iPos ) sqlite3Fts5PoslistReaderNext(&r1);
|
||||
iNew = iPos2;
|
||||
sqlite3Fts5PoslistNext64(a2, i2.nPoslist, &iOff2, &iPos2);
|
||||
if( iPos1==iPos2 ){
|
||||
sqlite3Fts5PoslistNext64(a1, i1.nPoslist, &iOff1,&iPos1);
|
||||
}
|
||||
}
|
||||
p->rc = sqlite3Fts5PoslistWriterAppend(&tmp, &writer, iNew);
|
||||
}
|
||||
|
||||
/* WRITEPOSLISTSIZE */
|
||||
fts5BufferAppendVarint(&p->rc, &out, tmp.n * 2);
|
||||
fts5BufferAppendBlob(&p->rc, &out, tmp.n, tmp.p);
|
||||
fts5BufferSafeAppendVarint(&out, tmp.n * 2);
|
||||
fts5BufferSafeAppendBlob(&out, tmp.p, tmp.n);
|
||||
fts5DoclistIterNext(&i1);
|
||||
fts5DoclistIterNext(&i2);
|
||||
}
|
||||
@@ -4125,6 +4239,7 @@ static void fts5SetupPrefixIter(
|
||||
int bDesc, /* True for "ORDER BY rowid DESC" */
|
||||
const u8 *pToken, /* Buffer containing prefix to match */
|
||||
int nToken, /* Size of buffer pToken in bytes */
|
||||
Fts5Colset *pColset, /* Restrict matches to these columns */
|
||||
Fts5IndexIter **ppIter /* OUT: New iterator */
|
||||
){
|
||||
Fts5Structure *pStruct;
|
||||
@@ -4164,14 +4279,25 @@ static void fts5SetupPrefixIter(
|
||||
fts5BufferZero(&aBuf[i]);
|
||||
}
|
||||
}
|
||||
iLastRowid = 0;
|
||||
}
|
||||
|
||||
fts5MergeAppendDocid(&p->rc, &doclist, &iLastRowid, iRowid);
|
||||
fts5MultiIterPoslist(p, p1, 1, &doclist);
|
||||
if( 0==sqlite3Fts5BufferGrow(&p->rc, &doclist, 9) ){
|
||||
int iSave = doclist.n;
|
||||
assert( doclist.n!=0 || iLastRowid==0 );
|
||||
fts5BufferSafeAppendVarint(&doclist, iRowid - iLastRowid);
|
||||
if( fts5MultiIterPoslist(p, p1, pColset, &doclist) ){
|
||||
doclist.n = iSave;
|
||||
}else{
|
||||
iLastRowid = iRowid;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for(i=0; i<nBuf; i++){
|
||||
if( p->rc==SQLITE_OK ){
|
||||
fts5MergePrefixLists(p, &doclist, &aBuf[i]);
|
||||
}
|
||||
fts5BufferFree(&aBuf[i]);
|
||||
}
|
||||
fts5MultiIterFree(p, p1);
|
||||
@@ -4195,7 +4321,7 @@ static void fts5SetupPrefixIter(
|
||||
** Indicate that all subsequent calls to sqlite3Fts5IndexWrite() pertain
|
||||
** to the document with rowid iRowid.
|
||||
*/
|
||||
int sqlite3Fts5IndexBeginWrite(Fts5Index *p, i64 iRowid){
|
||||
int sqlite3Fts5IndexBeginWrite(Fts5Index *p, int bDelete, i64 iRowid){
|
||||
assert( p->rc==SQLITE_OK );
|
||||
|
||||
/* Allocate the hash table if it has not already been allocated */
|
||||
@@ -4204,10 +4330,15 @@ int sqlite3Fts5IndexBeginWrite(Fts5Index *p, i64 iRowid){
|
||||
}
|
||||
|
||||
/* Flush the hash table to disk if required */
|
||||
if( iRowid<=p->iWriteRowid || (p->nPendingData > p->nMaxPendingData) ){
|
||||
if( iRowid<p->iWriteRowid
|
||||
|| (iRowid==p->iWriteRowid && p->bDelete==0)
|
||||
|| (p->nPendingData > p->nMaxPendingData)
|
||||
){
|
||||
fts5IndexFlush(p);
|
||||
}
|
||||
|
||||
p->iWriteRowid = iRowid;
|
||||
p->bDelete = bDelete;
|
||||
return fts5IndexReturn(p);
|
||||
}
|
||||
|
||||
@@ -4306,7 +4437,6 @@ int sqlite3Fts5IndexClose(Fts5Index *p){
|
||||
sqlite3_finalize(p->pIdxDeleter);
|
||||
sqlite3_finalize(p->pIdxSelect);
|
||||
sqlite3Fts5HashFree(p->pHash);
|
||||
sqlite3Fts5BufferFree(&p->scratch);
|
||||
sqlite3_free(p->zDataTbl);
|
||||
sqlite3_free(p);
|
||||
}
|
||||
@@ -4367,6 +4497,7 @@ int sqlite3Fts5IndexWrite(
|
||||
Fts5Config *pConfig = p->pConfig;
|
||||
|
||||
assert( p->rc==SQLITE_OK );
|
||||
assert( (iCol<0)==p->bDelete );
|
||||
|
||||
/* Add the entry to the main terms index. */
|
||||
rc = sqlite3Fts5HashWrite(
|
||||
@@ -4393,6 +4524,7 @@ int sqlite3Fts5IndexQuery(
|
||||
Fts5Index *p, /* FTS index to query */
|
||||
const char *pToken, int nToken, /* Token (or prefix) to query for */
|
||||
int flags, /* Mask of FTS5INDEX_QUERY_X flags */
|
||||
Fts5Colset *pColset, /* Match these columns only */
|
||||
Fts5IndexIter **ppIter /* OUT: New iterator object */
|
||||
){
|
||||
Fts5Config *pConfig = p->pConfig;
|
||||
@@ -4436,7 +4568,7 @@ int sqlite3Fts5IndexQuery(
|
||||
}else{
|
||||
int bDesc = (flags & FTS5INDEX_QUERY_DESC)!=0;
|
||||
buf.p[0] = FTS5_MAIN_PREFIX;
|
||||
fts5SetupPrefixIter(p, bDesc, buf.p, nToken+1, &pRet);
|
||||
fts5SetupPrefixIter(p, bDesc, buf.p, nToken+1, pColset, &pRet);
|
||||
}
|
||||
|
||||
if( p->rc ){
|
||||
@@ -4538,7 +4670,7 @@ int sqlite3Fts5IterPoslist(
|
||||
*pp = &pSeg->pLeaf->p[pSeg->iLeafOffset];
|
||||
}else{
|
||||
fts5BufferZero(&pIter->poslist);
|
||||
fts5SegiterPoslist(pIter->pIndex, pSeg, &pIter->poslist);
|
||||
fts5SegiterPoslist(pIter->pIndex, pSeg, 0, &pIter->poslist);
|
||||
*pp = pIter->poslist.p;
|
||||
}
|
||||
return fts5IndexReturn(pIter->pIndex);
|
||||
@@ -4551,10 +4683,10 @@ int sqlite3Fts5IterPoslist(
|
||||
*/
|
||||
int sqlite3Fts5IterPoslistBuffer(Fts5IndexIter *pIter, Fts5Buffer *pBuf){
|
||||
Fts5Index *p = pIter->pIndex;
|
||||
|
||||
Fts5SegIter *pSeg = &pIter->aSeg[ pIter->aFirst[1].iFirst ];
|
||||
assert( p->rc==SQLITE_OK );
|
||||
fts5BufferZero(pBuf);
|
||||
fts5MultiIterPoslist(p, pIter, 0, pBuf);
|
||||
fts5SegiterPoslist(p, pSeg, 0, pBuf);
|
||||
return fts5IndexReturn(p);
|
||||
}
|
||||
|
||||
@@ -4729,7 +4861,7 @@ static int fts5QueryCksum(
|
||||
){
|
||||
u64 cksum = *pCksum;
|
||||
Fts5IndexIter *pIdxIter = 0;
|
||||
int rc = sqlite3Fts5IndexQuery(p, z, n, flags, &pIdxIter);
|
||||
int rc = sqlite3Fts5IndexQuery(p, z, n, flags, 0, &pIdxIter);
|
||||
|
||||
while( rc==SQLITE_OK && 0==sqlite3Fts5IterEof(pIdxIter) ){
|
||||
i64 dummy;
|
||||
@@ -5103,7 +5235,7 @@ int sqlite3Fts5IndexIntegrityCheck(Fts5Index *p, u64 cksum){
|
||||
fts5TestTerm(p, &term, z, n, cksum2, &cksum3);
|
||||
|
||||
poslist.n = 0;
|
||||
fts5MultiIterPoslist(p, pIter, 0, &poslist);
|
||||
fts5SegiterPoslist(p, &pIter->aSeg[pIter->aFirst[1].iFirst] , 0, &poslist);
|
||||
while( 0==sqlite3Fts5PoslistNext64(poslist.p, poslist.n, &iOff, &iPos) ){
|
||||
int iCol = FTS5_POS2COLUMN(iPos);
|
||||
int iTokOff = FTS5_POS2OFFSET(iPos);
|
||||
@@ -5243,6 +5375,29 @@ static void fts5DecodeStructure(
|
||||
fts5StructureRelease(p);
|
||||
}
|
||||
|
||||
/*
|
||||
** This is part of the fts5_decode() debugging aid.
|
||||
**
|
||||
** Arguments pBlob/nBlob contain an "averages" record. This function
|
||||
** appends a human-readable representation of record to the buffer passed
|
||||
** as the second argument.
|
||||
*/
|
||||
static void fts5DecodeAverages(
|
||||
int *pRc, /* IN/OUT: error code */
|
||||
Fts5Buffer *pBuf,
|
||||
const u8 *pBlob, int nBlob
|
||||
){
|
||||
int i = 0;
|
||||
const char *zSpace = "";
|
||||
|
||||
while( i<nBlob ){
|
||||
u64 iVal;
|
||||
i += sqlite3Fts5GetVarint(&pBlob[i], &iVal);
|
||||
sqlite3Fts5BufferAppendPrintf(pRc, pBuf, "%s%d", zSpace, (int)iVal);
|
||||
zSpace = " ";
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Buffer (a/n) is assumed to contain a list of serialized varints. Read
|
||||
** each varint and append its string representation to buffer pBuf. Return
|
||||
@@ -5344,7 +5499,7 @@ static void fts5DecodeFunction(
|
||||
}
|
||||
}else if( iSegid==0 ){
|
||||
if( iRowid==FTS5_AVERAGES_ROWID ){
|
||||
/* todo */
|
||||
fts5DecodeAverages(&rc, &s, a, n);
|
||||
}else{
|
||||
fts5DecodeStructure(&rc, &s, a, n);
|
||||
}
|
||||
|
@@ -440,6 +440,19 @@ static int fts5CreateMethod(
|
||||
#define FTS5_PLAN_SCAN 5 /* No usable constraint */
|
||||
#define FTS5_PLAN_ROWID 6 /* (rowid = ?) */
|
||||
|
||||
/*
|
||||
** Set the SQLITE_INDEX_SCAN_UNIQUE flag in pIdxInfo->flags. Unless this
|
||||
** extension is currently being used by a version of SQLite too old to
|
||||
** support index-info flags. In that case this function is a no-op.
|
||||
*/
|
||||
static void fts5SetUniqueFlag(sqlite3_index_info *pIdxInfo){
|
||||
#if SQLITE_VERSION_NUMBER>=3008012
|
||||
if( sqlite3_libversion_number()>=3008012 ){
|
||||
pIdxInfo->idxFlags |= SQLITE_INDEX_SCAN_UNIQUE;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
** Implementation of the xBestIndex method for FTS5 tables. Within the
|
||||
** WHERE constraint, it searches for the following:
|
||||
@@ -492,8 +505,10 @@ static int fts5BestIndexMethod(sqlite3_vtab *pVTab, sqlite3_index_info *pInfo){
|
||||
int omit; /* True to omit this if found */
|
||||
int iConsIndex; /* Index in pInfo->aConstraint[] */
|
||||
} aConstraint[] = {
|
||||
{SQLITE_INDEX_CONSTRAINT_MATCH, FTS5_BI_MATCH, 1, 1, -1},
|
||||
{SQLITE_INDEX_CONSTRAINT_MATCH, FTS5_BI_RANK, 2, 1, -1},
|
||||
{SQLITE_INDEX_CONSTRAINT_MATCH|SQLITE_INDEX_CONSTRAINT_EQ,
|
||||
FTS5_BI_MATCH, 1, 1, -1},
|
||||
{SQLITE_INDEX_CONSTRAINT_MATCH|SQLITE_INDEX_CONSTRAINT_EQ,
|
||||
FTS5_BI_RANK, 2, 1, -1},
|
||||
{SQLITE_INDEX_CONSTRAINT_EQ, FTS5_BI_ROWID_EQ, 0, 0, -1},
|
||||
{SQLITE_INDEX_CONSTRAINT_LT|SQLITE_INDEX_CONSTRAINT_LE,
|
||||
FTS5_BI_ROWID_LE, 0, 0, -1},
|
||||
@@ -546,6 +561,7 @@ static int fts5BestIndexMethod(sqlite3_vtab *pVTab, sqlite3_index_info *pInfo){
|
||||
bHasMatch = BitFlagTest(idxFlags, FTS5_BI_MATCH);
|
||||
if( BitFlagTest(idxFlags, FTS5_BI_ROWID_EQ) ){
|
||||
pInfo->estimatedCost = bHasMatch ? 100.0 : 10.0;
|
||||
if( bHasMatch==0 ) fts5SetUniqueFlag(pInfo);
|
||||
}else if( BitFlagAllTest(idxFlags, FTS5_BI_ROWID_LE|FTS5_BI_ROWID_GE) ){
|
||||
pInfo->estimatedCost = bHasMatch ? 500.0 : 250000.0;
|
||||
}else if( BitFlagTest(idxFlags, FTS5_BI_ROWID_LE|FTS5_BI_ROWID_GE) ){
|
||||
@@ -1284,15 +1300,14 @@ static void fts5SetVtabError(Fts5Table *p, const char *zFormat, ...){
|
||||
*/
|
||||
static int fts5SpecialInsert(
|
||||
Fts5Table *pTab, /* Fts5 table object */
|
||||
sqlite3_value *pCmd, /* Value inserted into special column */
|
||||
const char *zCmd, /* Text inserted into table-name column */
|
||||
sqlite3_value *pVal /* Value inserted into rank column */
|
||||
){
|
||||
Fts5Config *pConfig = pTab->pConfig;
|
||||
const char *z = (const char*)sqlite3_value_text(pCmd);
|
||||
int rc = SQLITE_OK;
|
||||
int bError = 0;
|
||||
|
||||
if( 0==sqlite3_stricmp("delete-all", z) ){
|
||||
if( 0==sqlite3_stricmp("delete-all", zCmd) ){
|
||||
if( pConfig->eContent==FTS5_CONTENT_NORMAL ){
|
||||
fts5SetVtabError(pTab,
|
||||
"'delete-all' may only be used with a "
|
||||
@@ -1302,7 +1317,7 @@ static int fts5SpecialInsert(
|
||||
}else{
|
||||
rc = sqlite3Fts5StorageDeleteAll(pTab->pStorage);
|
||||
}
|
||||
}else if( 0==sqlite3_stricmp("rebuild", z) ){
|
||||
}else if( 0==sqlite3_stricmp("rebuild", zCmd) ){
|
||||
if( pConfig->eContent==FTS5_CONTENT_NONE ){
|
||||
fts5SetVtabError(pTab,
|
||||
"'rebuild' may not be used with a contentless fts5 table"
|
||||
@@ -1311,27 +1326,27 @@ static int fts5SpecialInsert(
|
||||
}else{
|
||||
rc = sqlite3Fts5StorageRebuild(pTab->pStorage);
|
||||
}
|
||||
}else if( 0==sqlite3_stricmp("optimize", z) ){
|
||||
}else if( 0==sqlite3_stricmp("optimize", zCmd) ){
|
||||
rc = sqlite3Fts5StorageOptimize(pTab->pStorage);
|
||||
}else if( 0==sqlite3_stricmp("merge", z) ){
|
||||
}else if( 0==sqlite3_stricmp("merge", zCmd) ){
|
||||
int nMerge = sqlite3_value_int(pVal);
|
||||
rc = sqlite3Fts5StorageMerge(pTab->pStorage, nMerge);
|
||||
}else if( 0==sqlite3_stricmp("integrity-check", z) ){
|
||||
}else if( 0==sqlite3_stricmp("integrity-check", zCmd) ){
|
||||
rc = sqlite3Fts5StorageIntegrity(pTab->pStorage);
|
||||
#ifdef SQLITE_DEBUG
|
||||
}else if( 0==sqlite3_stricmp("prefix-index", z) ){
|
||||
}else if( 0==sqlite3_stricmp("prefix-index", zCmd) ){
|
||||
pConfig->bPrefixIndex = sqlite3_value_int(pVal);
|
||||
#endif
|
||||
}else{
|
||||
rc = sqlite3Fts5IndexLoadConfig(pTab->pIndex);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3Fts5ConfigSetValue(pTab->pConfig, z, pVal, &bError);
|
||||
rc = sqlite3Fts5ConfigSetValue(pTab->pConfig, zCmd, pVal, &bError);
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
if( bError ){
|
||||
rc = SQLITE_ERROR;
|
||||
}else{
|
||||
rc = sqlite3Fts5StorageConfigValue(pTab->pStorage, z, pVal, 0);
|
||||
rc = sqlite3Fts5StorageConfigValue(pTab->pStorage, zCmd, pVal, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1352,10 +1367,35 @@ static int fts5SpecialDelete(
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void fts5StorageInsert(
|
||||
int *pRc,
|
||||
Fts5Table *pTab,
|
||||
sqlite3_value **apVal,
|
||||
i64 *piRowid
|
||||
){
|
||||
int rc = *pRc;
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3Fts5StorageContentInsert(pTab->pStorage, apVal, piRowid);
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3Fts5StorageIndexInsert(pTab->pStorage, apVal, *piRowid);
|
||||
}
|
||||
*pRc = rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** This function is the implementation of the xUpdate callback used by
|
||||
** FTS3 virtual tables. It is invoked by SQLite each time a row is to be
|
||||
** inserted, updated or deleted.
|
||||
**
|
||||
** A delete specifies a single argument - the rowid of the row to remove.
|
||||
**
|
||||
** Update and insert operations pass:
|
||||
**
|
||||
** 1. The "old" rowid, or NULL.
|
||||
** 2. The "new" rowid.
|
||||
** 3. Values for each of the nCol matchable columns.
|
||||
** 4. Values for the two hidden columns (<tablename> and "rank").
|
||||
*/
|
||||
static int fts5UpdateMethod(
|
||||
sqlite3_vtab *pVtab, /* Virtual table handle */
|
||||
@@ -1366,65 +1406,108 @@ static int fts5UpdateMethod(
|
||||
Fts5Table *pTab = (Fts5Table*)pVtab;
|
||||
Fts5Config *pConfig = pTab->pConfig;
|
||||
int eType0; /* value_type() of apVal[0] */
|
||||
int eConflict; /* ON CONFLICT for this DML */
|
||||
int rc = SQLITE_OK; /* Return code */
|
||||
|
||||
/* A transaction must be open when this is called. */
|
||||
assert( pTab->ts.eState==1 );
|
||||
|
||||
assert( pVtab->zErrMsg==0 );
|
||||
assert( nArg==1 || nArg==(2+pConfig->nCol+2) );
|
||||
assert( nArg==1
|
||||
|| sqlite3_value_type(apVal[1])==SQLITE_INTEGER
|
||||
|| sqlite3_value_type(apVal[1])==SQLITE_NULL
|
||||
);
|
||||
assert( pTab->pConfig->pzErrmsg==0 );
|
||||
pTab->pConfig->pzErrmsg = &pTab->base.zErrMsg;
|
||||
|
||||
/* A delete specifies a single argument - the rowid of the row to remove.
|
||||
** Update and insert operations pass:
|
||||
**
|
||||
** 1. The "old" rowid, or NULL.
|
||||
** 2. The "new" rowid.
|
||||
** 3. Values for each of the nCol matchable columns.
|
||||
** 4. Values for the two hidden columns (<tablename> and "rank").
|
||||
*/
|
||||
/* Put any active cursors into REQUIRE_SEEK state. */
|
||||
fts5TripCursors(pTab);
|
||||
|
||||
eType0 = sqlite3_value_type(apVal[0]);
|
||||
eConflict = sqlite3_vtab_on_conflict(pConfig->db);
|
||||
|
||||
assert( eType0==SQLITE_INTEGER || eType0==SQLITE_NULL );
|
||||
assert( pVtab->zErrMsg==0 );
|
||||
assert( (nArg==1 && eType0==SQLITE_INTEGER) || nArg==(2+pConfig->nCol+2) );
|
||||
|
||||
fts5TripCursors(pTab);
|
||||
if( eType0==SQLITE_INTEGER ){
|
||||
if( fts5IsContentless(pTab) ){
|
||||
pTab->base.zErrMsg = sqlite3_mprintf(
|
||||
"cannot %s contentless fts5 table: %s",
|
||||
(nArg>1 ? "UPDATE" : "DELETE from"), pConfig->zName
|
||||
);
|
||||
rc = SQLITE_ERROR;
|
||||
}else{
|
||||
i64 iDel = sqlite3_value_int64(apVal[0]); /* Rowid to delete */
|
||||
rc = sqlite3Fts5StorageDelete(pTab->pStorage, iDel);
|
||||
}
|
||||
}else{
|
||||
sqlite3_value *pCmd = apVal[2 + pConfig->nCol];
|
||||
assert( nArg>1 );
|
||||
if( SQLITE_NULL!=sqlite3_value_type(pCmd) ){
|
||||
const char *z = (const char*)sqlite3_value_text(pCmd);
|
||||
if( eType0==SQLITE_NULL
|
||||
&& sqlite3_value_type(apVal[2+pConfig->nCol])!=SQLITE_NULL
|
||||
){
|
||||
/* A "special" INSERT op. These are handled separately. */
|
||||
const char *z = (const char*)sqlite3_value_text(apVal[2+pConfig->nCol]);
|
||||
if( pConfig->eContent!=FTS5_CONTENT_NORMAL
|
||||
&& 0==sqlite3_stricmp("delete", z)
|
||||
){
|
||||
rc = fts5SpecialDelete(pTab, apVal, pRowid);
|
||||
}else{
|
||||
rc = fts5SpecialInsert(pTab, pCmd, apVal[2 + pConfig->nCol + 1]);
|
||||
rc = fts5SpecialInsert(pTab, z, apVal[2 + pConfig->nCol + 1]);
|
||||
}
|
||||
}else{
|
||||
/* A regular INSERT, UPDATE or DELETE statement. The trick here is that
|
||||
** any conflict on the rowid value must be detected before any
|
||||
** modifications are made to the database file. There are 4 cases:
|
||||
**
|
||||
** 1) DELETE
|
||||
** 2) UPDATE (rowid not modified)
|
||||
** 3) UPDATE (rowid modified)
|
||||
** 4) INSERT
|
||||
**
|
||||
** Cases 3 and 4 may violate the rowid constraint.
|
||||
*/
|
||||
int eConflict = sqlite3_vtab_on_conflict(pConfig->db);
|
||||
|
||||
assert( eType0==SQLITE_INTEGER || eType0==SQLITE_NULL );
|
||||
assert( nArg!=1 || eType0==SQLITE_INTEGER );
|
||||
|
||||
/* Filter out attempts to run UPDATE or DELETE on contentless tables.
|
||||
** This is not suported. */
|
||||
if( eType0==SQLITE_INTEGER && fts5IsContentless(pTab) ){
|
||||
pTab->base.zErrMsg = sqlite3_mprintf(
|
||||
"cannot %s contentless fts5 table: %s",
|
||||
(nArg>1 ? "UPDATE" : "DELETE from"), pConfig->zName
|
||||
);
|
||||
rc = SQLITE_ERROR;
|
||||
}
|
||||
|
||||
/* Case 1: DELETE */
|
||||
else if( nArg==1 ){
|
||||
i64 iDel = sqlite3_value_int64(apVal[0]); /* Rowid to delete */
|
||||
rc = sqlite3Fts5StorageDelete(pTab->pStorage, iDel);
|
||||
}
|
||||
|
||||
/* Case 2: INSERT */
|
||||
else if( eType0!=SQLITE_INTEGER ){
|
||||
/* If this is a REPLACE, first remove the current entry (if any) */
|
||||
if( eConflict==SQLITE_REPLACE
|
||||
&& sqlite3_value_type(apVal[1])==SQLITE_INTEGER
|
||||
){
|
||||
i64 iNew = sqlite3_value_int64(apVal[1]); /* Rowid to delete */
|
||||
rc = sqlite3Fts5StorageDelete(pTab->pStorage, iNew);
|
||||
}
|
||||
fts5StorageInsert(&rc, pTab, apVal, pRowid);
|
||||
}
|
||||
|
||||
/* Case 2: UPDATE */
|
||||
else{
|
||||
i64 iOld = sqlite3_value_int64(apVal[0]); /* Old rowid */
|
||||
i64 iNew = sqlite3_value_int64(apVal[1]); /* New rowid */
|
||||
if( iOld!=iNew ){
|
||||
if( eConflict==SQLITE_REPLACE ){
|
||||
rc = sqlite3Fts5StorageDelete(pTab->pStorage, iOld);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3Fts5StorageDelete(pTab->pStorage, iNew);
|
||||
}
|
||||
fts5StorageInsert(&rc, pTab, apVal, pRowid);
|
||||
}else{
|
||||
rc = sqlite3Fts5StorageContentInsert(pTab->pStorage, apVal, pRowid);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3Fts5StorageDelete(pTab->pStorage, iOld);
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3Fts5StorageIndexInsert(pTab->pStorage, apVal, *pRowid);
|
||||
}
|
||||
}
|
||||
}else{
|
||||
rc = sqlite3Fts5StorageDelete(pTab->pStorage, iOld);
|
||||
fts5StorageInsert(&rc, pTab, apVal, pRowid);
|
||||
}
|
||||
goto update_method_out;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if( rc==SQLITE_OK && nArg>1 ){
|
||||
rc = sqlite3Fts5StorageInsert(pTab->pStorage, apVal, eConflict, pRowid);
|
||||
}
|
||||
|
||||
update_method_out:
|
||||
pTab->pConfig->pzErrmsg = 0;
|
||||
return rc;
|
||||
}
|
||||
|
@@ -392,7 +392,7 @@ static int fts5StorageDeleteFromIndex(Fts5Storage *p, i64 iDel){
|
||||
Fts5InsertCtx ctx;
|
||||
ctx.pStorage = p;
|
||||
ctx.iCol = -1;
|
||||
rc = sqlite3Fts5IndexBeginWrite(p->pIndex, iDel);
|
||||
rc = sqlite3Fts5IndexBeginWrite(p->pIndex, 1, iDel);
|
||||
for(iCol=1; rc==SQLITE_OK && iCol<=pConfig->nCol; iCol++){
|
||||
if( pConfig->abUnindexed[iCol-1] ) continue;
|
||||
ctx.szCol = 0;
|
||||
@@ -549,7 +549,7 @@ int sqlite3Fts5StorageSpecialDelete(
|
||||
ctx.pStorage = p;
|
||||
ctx.iCol = -1;
|
||||
|
||||
rc = sqlite3Fts5IndexBeginWrite(p->pIndex, iDel);
|
||||
rc = sqlite3Fts5IndexBeginWrite(p->pIndex, 1, iDel);
|
||||
for(iCol=0; rc==SQLITE_OK && iCol<pConfig->nCol; iCol++){
|
||||
if( pConfig->abUnindexed[iCol] ) continue;
|
||||
ctx.szCol = 0;
|
||||
@@ -639,7 +639,7 @@ int sqlite3Fts5StorageRebuild(Fts5Storage *p){
|
||||
i64 iRowid = sqlite3_column_int64(pScan, 0);
|
||||
|
||||
sqlite3Fts5BufferZero(&buf);
|
||||
rc = sqlite3Fts5IndexBeginWrite(p->pIndex, iRowid);
|
||||
rc = sqlite3Fts5IndexBeginWrite(p->pIndex, 0, iRowid);
|
||||
for(ctx.iCol=0; rc==SQLITE_OK && ctx.iCol<pConfig->nCol; ctx.iCol++){
|
||||
ctx.szCol = 0;
|
||||
if( pConfig->abUnindexed[ctx.iCol]==0 ){
|
||||
@@ -705,27 +705,17 @@ static int fts5StorageNewRowid(Fts5Storage *p, i64 *piRowid){
|
||||
}
|
||||
|
||||
/*
|
||||
** Insert a new row into the FTS table.
|
||||
** Insert a new row into the FTS content table.
|
||||
*/
|
||||
int sqlite3Fts5StorageInsert(
|
||||
Fts5Storage *p, /* Storage module to write to */
|
||||
sqlite3_value **apVal, /* Array of values passed to xUpdate() */
|
||||
int eConflict, /* on conflict clause */
|
||||
i64 *piRowid /* OUT: rowid of new record */
|
||||
int sqlite3Fts5StorageContentInsert(
|
||||
Fts5Storage *p,
|
||||
sqlite3_value **apVal,
|
||||
i64 *piRowid
|
||||
){
|
||||
Fts5Config *pConfig = p->pConfig;
|
||||
int rc = SQLITE_OK; /* Return code */
|
||||
sqlite3_stmt *pInsert = 0; /* Statement used to write %_content table */
|
||||
int eStmt = 0; /* Type of statement used on %_content */
|
||||
int i; /* Counter variable */
|
||||
Fts5InsertCtx ctx; /* Tokenization callback context object */
|
||||
Fts5Buffer buf; /* Buffer used to build up %_docsize blob */
|
||||
|
||||
memset(&buf, 0, sizeof(Fts5Buffer));
|
||||
rc = fts5StorageLoadTotals(p, 1);
|
||||
int rc = SQLITE_OK;
|
||||
|
||||
/* Insert the new row into the %_content table. */
|
||||
if( rc==SQLITE_OK ){
|
||||
if( pConfig->eContent!=FTS5_CONTENT_NORMAL ){
|
||||
if( sqlite3_value_type(apVal[1])==SQLITE_INTEGER ){
|
||||
*piRowid = sqlite3_value_int64(apVal[1]);
|
||||
@@ -733,14 +723,18 @@ int sqlite3Fts5StorageInsert(
|
||||
rc = fts5StorageNewRowid(p, piRowid);
|
||||
}
|
||||
}else{
|
||||
sqlite3_stmt *pInsert = 0; /* Statement to write %_content table */
|
||||
int i; /* Counter variable */
|
||||
#if 0
|
||||
if( eConflict==SQLITE_REPLACE ){
|
||||
eStmt = FTS5_STMT_REPLACE_CONTENT;
|
||||
rc = fts5StorageDeleteFromIndex(p, sqlite3_value_int64(apVal[1]));
|
||||
}else{
|
||||
eStmt = FTS5_STMT_INSERT_CONTENT;
|
||||
}
|
||||
#endif
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = fts5StorageGetStmt(p, eStmt, &pInsert, 0);
|
||||
rc = fts5StorageGetStmt(p, FTS5_STMT_INSERT_CONTENT, &pInsert, 0);
|
||||
}
|
||||
for(i=1; rc==SQLITE_OK && i<=pConfig->nCol+1; i++){
|
||||
rc = sqlite3_bind_value(pInsert, i, apVal[i]);
|
||||
@@ -751,12 +745,29 @@ int sqlite3Fts5StorageInsert(
|
||||
}
|
||||
*piRowid = sqlite3_last_insert_rowid(pConfig->db);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Add new entries to the FTS index */
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3Fts5IndexBeginWrite(p->pIndex, *piRowid);
|
||||
/*
|
||||
** Insert new entries into the FTS index and %_docsize table.
|
||||
*/
|
||||
int sqlite3Fts5StorageIndexInsert(
|
||||
Fts5Storage *p,
|
||||
sqlite3_value **apVal,
|
||||
i64 iRowid
|
||||
){
|
||||
Fts5Config *pConfig = p->pConfig;
|
||||
int rc = SQLITE_OK; /* Return code */
|
||||
Fts5InsertCtx ctx; /* Tokenization callback context object */
|
||||
Fts5Buffer buf; /* Buffer used to build up %_docsize blob */
|
||||
|
||||
memset(&buf, 0, sizeof(Fts5Buffer));
|
||||
ctx.pStorage = p;
|
||||
rc = fts5StorageLoadTotals(p, 1);
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3Fts5IndexBeginWrite(p->pIndex, 0, iRowid);
|
||||
}
|
||||
for(ctx.iCol=0; rc==SQLITE_OK && ctx.iCol<pConfig->nCol; ctx.iCol++){
|
||||
ctx.szCol = 0;
|
||||
@@ -776,7 +787,7 @@ int sqlite3Fts5StorageInsert(
|
||||
|
||||
/* Write the %_docsize record */
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = fts5StorageInsertDocsize(p, *piRowid, &buf);
|
||||
rc = fts5StorageInsertDocsize(p, iRowid, &buf);
|
||||
}
|
||||
sqlite3_free(buf.p);
|
||||
|
||||
|
@@ -402,7 +402,7 @@ static int fts5VocabFilterMethod(
|
||||
const int flags = FTS5INDEX_QUERY_SCAN;
|
||||
|
||||
fts5VocabResetCursor(pCsr);
|
||||
rc = sqlite3Fts5IndexQuery(pCsr->pIndex, 0, 0, flags, &pCsr->pIter);
|
||||
rc = sqlite3Fts5IndexQuery(pCsr->pIndex, 0, 0, flags, 0, &pCsr->pIter);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = fts5VocabNextMethod(pCursor);
|
||||
}
|
||||
|
@@ -101,9 +101,9 @@ cnearset(A) ::= colset(X) COLON nearset(Y). {
|
||||
A = sqlite3Fts5ParseNode(pParse, FTS5_STRING, 0, 0, Y);
|
||||
}
|
||||
|
||||
%type colset {Fts5ExprColset*}
|
||||
%type colset {Fts5Colset*}
|
||||
%destructor colset { sqlite3_free($$); }
|
||||
%type colsetlist {Fts5ExprColset*}
|
||||
%type colsetlist {Fts5Colset*}
|
||||
%destructor colsetlist { sqlite3_free($$); }
|
||||
|
||||
colset(A) ::= LCP colsetlist(X) RCP. { A = X; }
|
||||
|
@@ -251,7 +251,6 @@ do_execsql_test 4.3.1 {
|
||||
INSERT INTO t3 VALUES('a five');
|
||||
INSERT INTO t3(t3, rank) VALUES('rank', 'bm25()');
|
||||
}
|
||||
breakpoint
|
||||
|
||||
do_execsql_test 4.3.2 {
|
||||
SELECT * FROM t3
|
||||
@@ -260,6 +259,7 @@ do_execsql_test 4.3.2 {
|
||||
} {
|
||||
{a four} {a one} {a five} {a two} {a three}
|
||||
}
|
||||
|
||||
do_execsql_test 4.3.3 {
|
||||
SELECT *, rank FROM t3
|
||||
WHERE t3 MATCH 'a' AND rank MATCH 'rowidmod(3)'
|
||||
@@ -268,6 +268,18 @@ do_execsql_test 4.3.3 {
|
||||
{a three} 0 {a one} 1 {a four} 1 {a two} 2 {a five} 2
|
||||
}
|
||||
|
||||
do_execsql_test 4.3.4 {
|
||||
SELECT * FROM t3('a', 'rowidmod(4)') ORDER BY rank ASC;
|
||||
} {
|
||||
{a four} {a one} {a five} {a two} {a three}
|
||||
}
|
||||
|
||||
do_execsql_test 4.3.5 {
|
||||
SELECT *, rank FROM t3('a', 'rowidmod(3)') ORDER BY rank ASC
|
||||
} {
|
||||
{a three} 0 {a one} 1 {a four} 1 {a two} 2 {a five} 2
|
||||
}
|
||||
|
||||
do_catchsql_test 4.4.3 {
|
||||
SELECT *, rank FROM t3 WHERE t3 MATCH 'a' AND rank MATCH 'xyz(3)'
|
||||
} {1 {no such function: xyz}}
|
||||
|
181
ext/fts5/test/fts5onepass.test
Normal file
181
ext/fts5/test/fts5onepass.test
Normal file
@@ -0,0 +1,181 @@
|
||||
# 2015 Sep 27
|
||||
#
|
||||
# The author disclaims copyright to this source code. In place of
|
||||
# a legal notice, here is a blessing:
|
||||
#
|
||||
# May you do good and not evil.
|
||||
# May you find forgiveness for yourself and forgive others.
|
||||
# May you share freely, never taking more than you give.
|
||||
#
|
||||
#*************************************************************************
|
||||
#
|
||||
|
||||
source [file join [file dirname [info script]] fts5_common.tcl]
|
||||
set testprefix fts5onepass
|
||||
|
||||
# If SQLITE_ENABLE_FTS3 is defined, omit this file.
|
||||
ifcapable !fts5 {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
do_execsql_test 1.0 {
|
||||
CREATE VIRTUAL TABLE ft USING fts5(content);
|
||||
INSERT INTO ft(rowid, content) VALUES(1, '1 2 3');
|
||||
INSERT INTO ft(rowid, content) VALUES(2, '4 5 6');
|
||||
INSERT INTO ft(rowid, content) VALUES(3, '7 8 9');
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Check that UPDATE and DELETE statements that feature "WHERE rowid=?" or
|
||||
# or "WHERE rowid=?" clauses do not use statement journals. But that other
|
||||
# DELETE and UPDATE statements do.
|
||||
#
|
||||
# Note: "MATCH ? AND rowid=?" does use a statement journal.
|
||||
#
|
||||
foreach {tn sql uses} {
|
||||
1.1 { DELETE FROM ft } 1
|
||||
1.2 { DELETE FROM ft WHERE rowid=? } 0
|
||||
1.3 { DELETE FROM ft WHERE rowid=? } 0
|
||||
1.4 { DELETE FROM ft WHERE ft MATCH '1' } 1
|
||||
1.5 { DELETE FROM ft WHERE ft MATCH '1' AND rowid=? } 1
|
||||
1.6 { DELETE FROM ft WHERE ft MATCH '1' AND rowid=? } 1
|
||||
|
||||
2.1 { UPDATE ft SET content='a b c' } 1
|
||||
2.2 { UPDATE ft SET content='a b c' WHERE rowid=? } 0
|
||||
2.3 { UPDATE ft SET content='a b c' WHERE rowid=? } 0
|
||||
2.4 { UPDATE ft SET content='a b c' WHERE ft MATCH '1' } 1
|
||||
2.5 { UPDATE ft SET content='a b c' WHERE ft MATCH '1' AND rowid=? } 1
|
||||
2.6 { UPDATE ft SET content='a b c' WHERE ft MATCH '1' AND rowid=? } 1
|
||||
} {
|
||||
do_test 1.$tn { sql_uses_stmt db $sql } $uses
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Check that putting a "DELETE/UPDATE ... WHERE rowid=?" statement in a
|
||||
# trigger program does not prevent the VM from using a statement
|
||||
# transaction. Even if the calling statement cannot hit a constraint.
|
||||
#
|
||||
do_execsql_test 2.0 {
|
||||
CREATE TABLE t1(x);
|
||||
|
||||
CREATE TRIGGER t1_ai AFTER INSERT ON t1 BEGIN
|
||||
DELETE FROM ft WHERE rowid=new.x;
|
||||
END;
|
||||
|
||||
CREATE TRIGGER t1_ad AFTER DELETE ON t1 BEGIN
|
||||
UPDATE ft SET content = 'a b c' WHERE rowid=old.x;
|
||||
END;
|
||||
|
||||
CREATE TRIGGER t1_bu BEFORE UPDATE ON t1 BEGIN
|
||||
DELETE FROM ft WHERE rowid=old.x;
|
||||
END;
|
||||
}
|
||||
|
||||
foreach {tn sql uses} {
|
||||
1 { INSERT INTO t1 VALUES(1) } 1
|
||||
2 { DELETE FROM t1 WHERE x=4 } 1
|
||||
3 { UPDATE t1 SET x=10 WHERE x=11 } 1
|
||||
} {
|
||||
do_test 2.$tn { sql_uses_stmt db $sql } $uses
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Test that an "UPDATE ... WHERE rowid=?" works and does not corrupt the
|
||||
# index when it strikes a constraint. Both inside and outside a
|
||||
# transaction.
|
||||
#
|
||||
foreach {tn tcl1 tcl2} {
|
||||
1 {} {}
|
||||
|
||||
2 {
|
||||
execsql BEGIN
|
||||
} {
|
||||
if {[sqlite3_get_autocommit db]==1} { error "transaction rolled back!" }
|
||||
execsql COMMIT
|
||||
}
|
||||
} {
|
||||
|
||||
do_execsql_test 3.$tn.0 {
|
||||
DROP TABLE IF EXISTS ft2;
|
||||
CREATE VIRTUAL TABLE ft2 USING fts5(content);
|
||||
INSERT INTO ft2(rowid, content) VALUES(1, 'a b c');
|
||||
INSERT INTO ft2(rowid, content) VALUES(2, 'a b d');
|
||||
INSERT INTO ft2(rowid, content) VALUES(3, 'a b e');
|
||||
}
|
||||
|
||||
eval $tcl1
|
||||
foreach {tn2 sql content} {
|
||||
1 { UPDATE ft2 SET rowid=2 WHERE rowid=1 }
|
||||
{ 1 {a b c} 2 {a b d} 3 {a b e} }
|
||||
|
||||
2 {
|
||||
INSERT INTO ft2(rowid, content) VALUES(4, 'a b f');
|
||||
UPDATE ft2 SET rowid=5 WHERE rowid=4;
|
||||
UPDATE ft2 SET rowid=3 WHERE rowid=5;
|
||||
} { 1 {a b c} 2 {a b d} 3 {a b e} 5 {a b f} }
|
||||
|
||||
3 {
|
||||
UPDATE ft2 SET rowid=3 WHERE rowid=4; -- matches 0 rows
|
||||
UPDATE ft2 SET rowid=2 WHERE rowid=3;
|
||||
} { 1 {a b c} 2 {a b d} 3 {a b e} 5 {a b f} }
|
||||
|
||||
4 {
|
||||
INSERT INTO ft2(rowid, content) VALUES(4, 'a b g');
|
||||
UPDATE ft2 SET rowid=-1 WHERE rowid=4;
|
||||
UPDATE ft2 SET rowid=3 WHERE rowid=-1;
|
||||
} {-1 {a b g} 1 {a b c} 2 {a b d} 3 {a b e} 5 {a b f} }
|
||||
|
||||
5 {
|
||||
DELETE FROM ft2 WHERE rowid=451;
|
||||
DELETE FROM ft2 WHERE rowid=-1;
|
||||
UPDATE ft2 SET rowid = 2 WHERE rowid = 1;
|
||||
} {1 {a b c} 2 {a b d} 3 {a b e} 5 {a b f} }
|
||||
} {
|
||||
do_catchsql_test 3.$tn.$tn2.a $sql {1 {constraint failed}}
|
||||
do_execsql_test 3.$tn.$tn2.b { SELECT rowid, content FROM ft2 } $content
|
||||
|
||||
do_execsql_test 3.$tn.$tn2.c {
|
||||
INSERT INTO ft2(ft2) VALUES('integrity-check');
|
||||
}
|
||||
}
|
||||
eval $tcl2
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Check that DELETE and UPDATE operations can be done without flushing
|
||||
# the in-memory hash table to disk.
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 4.1.1 {
|
||||
CREATE VIRTUAL TABLE ttt USING fts5(x);
|
||||
BEGIN;
|
||||
INSERT INTO ttt(rowid, x) VALUES(1, 'a b c');
|
||||
INSERT INTO ttt(rowid, x) VALUES(2, 'a b c');
|
||||
INSERT INTO ttt(rowid, x) VALUES(3, 'a b c');
|
||||
COMMIT
|
||||
}
|
||||
do_test 4.1.2 { fts5_level_segs ttt } {1}
|
||||
|
||||
do_execsql_test 4.2.1 {
|
||||
BEGIN;
|
||||
DELETE FROM ttt WHERE rowid=1;
|
||||
DELETE FROM ttt WHERE rowid=3;
|
||||
INSERT INTO ttt(rowid, x) VALUES(4, 'd e f');
|
||||
INSERT INTO ttt(rowid, x) VALUES(5, 'd e f');
|
||||
COMMIT;
|
||||
} {}
|
||||
do_test 4.2.2 { fts5_level_segs ttt } {2}
|
||||
|
||||
|
||||
do_execsql_test 4.3.1 {
|
||||
BEGIN;
|
||||
UPDATE ttt SET x = 'd e f' WHERE rowid = 2;
|
||||
UPDATE ttt SET x = 'A B C' WHERE rowid = 4;
|
||||
INSERT INTO ttt(rowid, x) VALUES(6, 'd e f');
|
||||
COMMIT;
|
||||
} {}
|
||||
do_test 4.2.2 { fts5_level_segs ttt } {3}
|
||||
|
||||
finish_test
|
||||
|
119
ext/fts5/test/fts5phrase.test
Normal file
119
ext/fts5/test/fts5phrase.test
Normal file
@@ -0,0 +1,119 @@
|
||||
# 2014 Jan 08
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
# Tests focused on phrase queries.
|
||||
#
|
||||
|
||||
source [file join [file dirname [info script]] fts5_common.tcl]
|
||||
set testprefix fts5phrase
|
||||
|
||||
# If SQLITE_ENABLE_FTS5 is defined, omit this file.
|
||||
ifcapable !fts5 {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
do_execsql_test 1.0 {
|
||||
CREATE VIRTUAL TABLE t3 USING fts5(a, b, c);
|
||||
INSERT INTO t3 VALUES('d e a', 'd i j j f', 'i j i e b f h'); -- 1
|
||||
INSERT INTO t3 VALUES('g a e', 'f g i g a', 'h d g i g h c'); -- 2
|
||||
INSERT INTO t3 VALUES('e a d', 'e i h a f', 'c e h i f b i'); -- 3
|
||||
INSERT INTO t3 VALUES('a g c', 'd j d j c', 'c d f j i g j'); -- 4
|
||||
INSERT INTO t3 VALUES('b c b', 'j g c d f', 'j c j d g f b'); -- 5
|
||||
INSERT INTO t3 VALUES('j a d', 'e b i h h', 'c c f g d i d'); -- 6
|
||||
INSERT INTO t3 VALUES('a d f', 'h g i i i', 'e a g c i f b'); -- 7
|
||||
INSERT INTO t3 VALUES('g f d', 'f c g b j', 'b b h h h j j'); -- 8
|
||||
INSERT INTO t3 VALUES('f h g', 'c j f g j', 'd h d f e b h'); -- 9
|
||||
INSERT INTO t3 VALUES('f h d', 'c i a d b', 'g b j b a d e'); -- 10
|
||||
INSERT INTO t3 VALUES('j h h', 'j i h a g', 'd e i e a g j'); -- 11
|
||||
INSERT INTO t3 VALUES('a b e', 'h g a g c', 'h c a a d e g'); -- 12
|
||||
INSERT INTO t3 VALUES('a j g', 'i h i f i', 'a g h j g i b'); -- 13
|
||||
INSERT INTO t3 VALUES('j h e', 'f e d i e', 'i d c f e d c'); -- 14
|
||||
INSERT INTO t3 VALUES('d j d', 'd b i a c', 'g d h i d b e'); -- 15
|
||||
INSERT INTO t3 VALUES('h j e', 'e b b c f', 'j a f g h d j'); -- 16
|
||||
INSERT INTO t3 VALUES('c b j', 'c a b a i', 'h f i d a d c'); -- 17
|
||||
INSERT INTO t3 VALUES('e e d', 'i d f c c', 'g i d a f e a'); -- 18
|
||||
INSERT INTO t3 VALUES('e i g', 'e a b i h', 'i f d d a d f'); -- 19
|
||||
INSERT INTO t3 VALUES('h g f', 'b h h j d', 'i f d e g j a'); -- 20
|
||||
INSERT INTO t3 VALUES('e h f', 'j c b c f', 'j a j g h a c'); -- 21
|
||||
INSERT INTO t3 VALUES('d c h', 'b g i c e', 'i i c d e h i'); -- 22
|
||||
INSERT INTO t3 VALUES('a h i', 'a g d f f', 'e f i i b b h'); -- 23
|
||||
INSERT INTO t3 VALUES('d d g', 'c c b c g', 'g c h e b c e'); -- 24
|
||||
INSERT INTO t3 VALUES('a b b', 'b f a d i', 'd a h a b c i'); -- 25
|
||||
INSERT INTO t3 VALUES('a f d', 'a j e a h', 'j i h j a i f'); -- 26
|
||||
INSERT INTO t3 VALUES('d j d', 'h a d i a', 'h h f j h g a'); -- 27
|
||||
INSERT INTO t3 VALUES('g a e', 'd g f a g', 'i d b c g g j'); -- 28
|
||||
INSERT INTO t3 VALUES('j e h', 'g h j h g', 'd a e j a a h'); -- 29
|
||||
INSERT INTO t3 VALUES('e j e', 'g e j g c', 'f c e b e e a'); -- 30
|
||||
INSERT INTO t3 VALUES('h f f', 'i j g e c', 'j j f c a i j'); -- 31
|
||||
INSERT INTO t3 VALUES('a g c', 'c g d b i', 'g h c b a a f'); -- 32
|
||||
INSERT INTO t3 VALUES('c h i', 'j d h e e', 'a h i d c c j'); -- 33
|
||||
INSERT INTO t3 VALUES('d a c', 'e d d b j', 'c e b b h i h'); -- 34
|
||||
INSERT INTO t3 VALUES('d f h', 'c a f c c', 'j b b c c j f'); -- 35
|
||||
INSERT INTO t3 VALUES('b g h', 'g c c c f', 'c g c f h e e'); -- 36
|
||||
INSERT INTO t3 VALUES('f e a', 'b h f j h', 'j g h f d g f'); -- 37
|
||||
INSERT INTO t3 VALUES('h f a', 'a e i j g', 'f d a f d f c'); -- 38
|
||||
INSERT INTO t3 VALUES('f i c', 'f i i i i', 'e c f d h j f'); -- 39
|
||||
INSERT INTO t3 VALUES('h h d', 'd i e d i', 'd f e i a h a'); -- 40
|
||||
INSERT INTO t3 VALUES('f g c', 'd a f c h', 'b b g j c e g'); -- 41
|
||||
INSERT INTO t3 VALUES('h i h', 'h d j d e', 'e d b b i e g'); -- 42
|
||||
INSERT INTO t3 VALUES('b h i', 'j e i d a', 'j j h e e c a'); -- 43
|
||||
INSERT INTO t3 VALUES('g i g', 'f c c f d', 'a c i c a d a'); -- 44
|
||||
INSERT INTO t3 VALUES('c c f', 'a b j d b', 'c a e g f e c'); -- 45
|
||||
INSERT INTO t3 VALUES('d h j', 'g c b j d', 'e a h f h j g'); -- 46
|
||||
INSERT INTO t3 VALUES('a a d', 'j e j a i', 'i d c f f f b'); -- 47
|
||||
INSERT INTO t3 VALUES('b g j', 'e c i h f', 'd d h b g a d'); -- 48
|
||||
INSERT INTO t3 VALUES('c i a', 'a c c c c', 'e h i e h i e'); -- 49
|
||||
INSERT INTO t3 VALUES('f f c', 'f f b i i', 'f f a j e c i'); -- 50
|
||||
}
|
||||
|
||||
proc pmatch {col expr} {
|
||||
return [expr {[string first $expr $col]>=0}]
|
||||
}
|
||||
db func pmatch pmatch
|
||||
|
||||
foreach {tn cols tokens} {
|
||||
1 a "c c"
|
||||
2 b "c c"
|
||||
3 c "c c"
|
||||
4 {a b c} "c c"
|
||||
5 {a b c} "b h"
|
||||
6 {a b} "b h"
|
||||
7 {a c} "b h"
|
||||
8 {c a} "b h"
|
||||
9 {c} "i e"
|
||||
10 {b} "i e"
|
||||
11 {a} "i e"
|
||||
} {
|
||||
set fts "{$cols}:[join $tokens +]"
|
||||
set where [list]
|
||||
foreach c $cols { lappend where "pmatch($c, '$tokens')" }
|
||||
set where [join $where " OR "]
|
||||
|
||||
set res [db eval "SELECT rowid FROM t3 WHERE $where"]
|
||||
do_execsql_test "1.$tn.$fts->([llength $res] rows)" {
|
||||
SELECT rowid FROM t3($fts)
|
||||
} $res
|
||||
}
|
||||
|
||||
do_execsql_test 2.0 {
|
||||
SELECT rowid,
|
||||
highlight(t3, 0, '*', '*'),
|
||||
highlight(t3, 1, '*', '*'),
|
||||
highlight(t3, 2, '*', '*')
|
||||
FROM t3('a:f+f')
|
||||
} {
|
||||
31 {h *f f*} {i j g e c} {j j f c a i j}
|
||||
50 {*f f* c} {f f b i i} {f f a j e c i}
|
||||
}
|
||||
|
||||
finish_test
|
||||
|
@@ -62,6 +62,140 @@ foreach {tn q res} {
|
||||
do_execsql_test 2.3.$tn $q $res
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Check that prefix queries with:
|
||||
#
|
||||
# * a column filter, and
|
||||
# * no prefix index.
|
||||
#
|
||||
# work Ok.
|
||||
#
|
||||
do_execsql_test 3.0 {
|
||||
CREATE VIRTUAL TABLE t3 USING fts5(a, b, c);
|
||||
INSERT INTO t3(t3, rank) VALUES('pgsz', 32);
|
||||
BEGIN;
|
||||
INSERT INTO t3 VALUES('acb ccc bba', 'cca bba bca', 'bbc ccc bca'); -- 1
|
||||
INSERT INTO t3 VALUES('cbb cac cab', 'abb aac bba', 'aab ccc cac'); -- 2
|
||||
INSERT INTO t3 VALUES('aac bcb aac', 'acb bcb caa', 'aca bab bca'); -- 3
|
||||
INSERT INTO t3 VALUES('aab ccb ccc', 'aca cba cca', 'aca aac cbb'); -- 4
|
||||
INSERT INTO t3 VALUES('bac aab bab', 'ccb bac cba', 'acb aba abb'); -- 5
|
||||
INSERT INTO t3 VALUES('bab abc ccb', 'acb cba abb', 'cbb aaa cab'); -- 6
|
||||
INSERT INTO t3 VALUES('cbb bbc baa', 'aab aca baa', 'bcc cca aca'); -- 7
|
||||
INSERT INTO t3 VALUES('abc bba abb', 'cac abc cba', 'acc aac cac'); -- 8
|
||||
INSERT INTO t3 VALUES('bbc bbc cab', 'bcb ccb cba', 'bcc cac acb'); -- 9
|
||||
COMMIT;
|
||||
}
|
||||
|
||||
foreach {tn match res} {
|
||||
1 "a : c*" {1 2 4 6 7 9}
|
||||
2 "b : c*" {1 3 4 5 6 8 9}
|
||||
3 "c : c*" {1 2 4 6 7 8 9}
|
||||
4 "a : b*" {1 3 5 6 7 8 9}
|
||||
5 "b : b*" {1 2 3 5 7 9}
|
||||
6 "c : b*" {1 3 7 9}
|
||||
7 "a : a*" {1 3 4 5 6 8}
|
||||
8 "b : a*" {2 3 4 6 7 8}
|
||||
9 "c : a*" {2 3 4 5 6 7 8 9}
|
||||
} {
|
||||
do_execsql_test 3.1.$tn {
|
||||
SELECT rowid FROM t3($match)
|
||||
} $res
|
||||
}
|
||||
|
||||
do_test 3.2 {
|
||||
expr srand(0)
|
||||
execsql { DELETE FROM t3 }
|
||||
for {set i 0} {$i < 1000} {incr i} {
|
||||
set a [fts5_rnddoc 3]
|
||||
set b [fts5_rnddoc 8]
|
||||
set c [fts5_rnddoc 20]
|
||||
execsql { INSERT INTO t3 VALUES($a, $b, $c) }
|
||||
}
|
||||
execsql { INSERT INTO t3(t3) VALUES('integrity-check') }
|
||||
} {}
|
||||
|
||||
proc gmatch {col pattern} {
|
||||
expr {[lsearch -glob $col $pattern]>=0}
|
||||
}
|
||||
db func gmatch gmatch
|
||||
|
||||
proc ghl {col pattern} {
|
||||
foreach t $col {
|
||||
if {[string match $pattern $t]} {
|
||||
lappend res "*$t*"
|
||||
} else {
|
||||
lappend res $t
|
||||
}
|
||||
}
|
||||
set res
|
||||
}
|
||||
db func ghl ghl
|
||||
|
||||
set COLS(a) 0
|
||||
set COLS(b) 1
|
||||
set COLS(c) 2
|
||||
|
||||
for {set x 0} {$x<2} {incr x} {
|
||||
foreach {tn pattern} {
|
||||
1 {xa*}
|
||||
2 {xb*}
|
||||
3 {xc*}
|
||||
4 {xd*}
|
||||
5 {xe*}
|
||||
6 {xf*}
|
||||
7 {xg*}
|
||||
8 {xh*}
|
||||
9 {xi*}
|
||||
10 {xj*}
|
||||
} {
|
||||
foreach col {a b c} {
|
||||
|
||||
# Check that the list of returned rowids is correct.
|
||||
#
|
||||
set res [db eval "SELECT rowid FROM t3 WHERE gmatch($col, '$pattern')"]
|
||||
set query "$col : $pattern"
|
||||
do_execsql_test 3.3.$x.$tn.$col.rowid {
|
||||
SELECT rowid FROM t3($query);
|
||||
} $res
|
||||
|
||||
# Check that the highlight() function works.
|
||||
#
|
||||
set res [db eval \
|
||||
"SELECT ghl($col, '$pattern') FROM t3 WHERE gmatch($col, '$pattern')"
|
||||
]
|
||||
set idx $COLS($col)
|
||||
do_execsql_test 3.3.$x.$tn.$col.highlight {
|
||||
SELECT highlight(t3, $idx, '*', '*') FROM t3($query);
|
||||
} $res
|
||||
}
|
||||
|
||||
foreach colset {{a b} {b c} {c a} {a c} {b a}} {
|
||||
# Check that the list of returned rowids is correct.
|
||||
#
|
||||
foreach {col1 col2} $colset {}
|
||||
set expr "gmatch($col1, '$pattern') OR gmatch($col2, '$pattern')"
|
||||
set res [db eval "SELECT rowid FROM t3 WHERE $expr"]
|
||||
set query "{$colset} : $pattern"
|
||||
do_execsql_test 3.3.$x.$tn.{$colset}.rowid {
|
||||
SELECT rowid FROM t3($query);
|
||||
} $res
|
||||
|
||||
set resq "SELECT ghl($col1, '$pattern'), ghl($col2, '$pattern')"
|
||||
append resq " FROM t3 WHERE $expr"
|
||||
set res [db eval $resq]
|
||||
set idx1 $COLS($col1)
|
||||
set idx2 $COLS($col2)
|
||||
do_execsql_test 3.3.$x.$tn.{$colset}.highlight {
|
||||
SELECT highlight(t3, $idx1, '*', '*'), highlight(t3, $idx2, '*', '*')
|
||||
FROM t3($query)
|
||||
} $res
|
||||
}
|
||||
}
|
||||
execsql { INSERT INTO t3(t3) VALUES('optimize') }
|
||||
execsql { INSERT INTO t3(t3) VALUES('integrity-check') }
|
||||
}
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
|
@@ -19,7 +19,6 @@ ifcapable !fts5 {
|
||||
return
|
||||
}
|
||||
|
||||
if 1 {
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
set doc "x x [string repeat {y } 50]z z"
|
||||
@@ -137,8 +136,6 @@ do_execsql_test 5.4 {
|
||||
SELECT rowid FROM tt WHERE tt MATCH 'a*';
|
||||
} {1 2}
|
||||
|
||||
}
|
||||
|
||||
do_execsql_test 5.5 {
|
||||
DELETE FROM tt;
|
||||
BEGIN;
|
||||
@@ -184,6 +181,106 @@ do_catchsql_test 6.3 {
|
||||
SELECT * FROM xyz WHERE xyz MATCH NULL
|
||||
} {1 {fts5: syntax error near ""}}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
|
||||
do_execsql_test 7.1 {
|
||||
CREATE VIRTUAL TABLE ft2 USING fts5(content);
|
||||
INSERT INTO ft2(rowid, content) VALUES(1, 'a b c');
|
||||
INSERT INTO ft2(rowid, content) VALUES(2, 'a b d');
|
||||
}
|
||||
|
||||
do_catchsql_test 7.2 {
|
||||
BEGIN;
|
||||
UPDATE ft2 SET rowid=2 WHERE rowid=1;
|
||||
} {1 {constraint failed}}
|
||||
|
||||
do_execsql_test 7.3 {
|
||||
COMMIT;
|
||||
INSERT INTO ft2(ft2) VALUES('integrity-check');
|
||||
} {}
|
||||
|
||||
do_execsql_test 7.4 {
|
||||
SELECT * FROM ft2;
|
||||
} {{a b c} {a b d}}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 8.1 {
|
||||
CREATE VIRTUAL TABLE ft2 USING fts5(content);
|
||||
INSERT INTO ft2(rowid, content) VALUES(1, 'a b');
|
||||
}
|
||||
|
||||
do_execsql_test 8.2 {
|
||||
BEGIN;
|
||||
INSERT INTO ft2(rowid, content) VALUES(4, 'a x');
|
||||
}
|
||||
|
||||
do_execsql_test 8.3 {
|
||||
INSERT INTO ft2(ft2) VALUES('integrity-check');
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Check that the "table function" syntax works.
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 9.1 {
|
||||
CREATE VIRTUAL TABLE ft2 USING fts5(content);
|
||||
INSERT INTO ft2(rowid, content) VALUES(1, 'a b');
|
||||
INSERT INTO ft2(rowid, content) VALUES(2, 'a b c d');
|
||||
INSERT INTO ft2(rowid, content) VALUES(3, 'c d e f');
|
||||
}
|
||||
|
||||
do_execsql_test 9.2 {
|
||||
SELECT rowid FROM ft2('a');
|
||||
} {1 2}
|
||||
|
||||
do_execsql_test 9.3 {
|
||||
SELECT rowid FROM ft2('b AND c');
|
||||
} {2}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
do_execsql_test 10.0 {
|
||||
CREATE VIRTUAL TABLE t3 USING fts5(a, b, c);
|
||||
INSERT INTO t3 VALUES('bac aab bab', 'c bac c', 'acb aba abb'); -- 1
|
||||
INSERT INTO t3 VALUES('bab abc c', 'acb c abb', 'c aaa c'); -- 2
|
||||
}
|
||||
|
||||
do_execsql_test 10.1 {
|
||||
SELECT rowid FROM t3('c: c*');
|
||||
} {2}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Test that character 0x1A is allowed in fts5 barewords.
|
||||
#
|
||||
do_test 11.0 {
|
||||
execsql "CREATE VIRTUAL TABLE t4 USING fts5(x, tokenize=\"ascii tokenchars '\x1A'\")"
|
||||
execsql "
|
||||
INSERT INTO t4 VALUES('a b c \x1A');
|
||||
INSERT INTO t4 VALUES('a b c d\x1A');
|
||||
INSERT INTO t4 VALUES('a b c \x1Ad');
|
||||
INSERT INTO t4 VALUES('a b c d');
|
||||
"
|
||||
} {}
|
||||
|
||||
do_test 11.1 {
|
||||
execsql "SELECT rowid FROM t4('\x1A')"
|
||||
} {1}
|
||||
do_test 11.2 {
|
||||
execsql "SELECT rowid FROM t4('\x1A*')"
|
||||
} {1 3}
|
||||
do_test 11.3 {
|
||||
execsql "SELECT rowid FROM t4('d\x1A')"
|
||||
} {2}
|
||||
|
||||
do_test 11.4 {
|
||||
catchsql "SELECT rowid FROM t4('d\x1B')"
|
||||
} {/fts5: syntax error/}
|
||||
do_test 11.5 {
|
||||
catchsql "SELECT rowid FROM t4('d\x19')"
|
||||
} {/fts5: syntax error/}
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
|
135
ext/fts5/tool/fts5txt2db.tcl
Normal file
135
ext/fts5/tool/fts5txt2db.tcl
Normal file
@@ -0,0 +1,135 @@
|
||||
|
||||
|
||||
proc usage {} {
|
||||
puts stderr "$::argv0 ?OPTIONS? DATABASE FILE1..."
|
||||
puts stderr ""
|
||||
puts stderr "Options are"
|
||||
puts stderr " -fts5"
|
||||
puts stderr " -fts4"
|
||||
puts stderr " -colsize <list of column sizes>"
|
||||
puts stderr {
|
||||
This script is designed to create fts4/5 tables with more than one column.
|
||||
The -colsize option should be set to a Tcl list of integer values, one for
|
||||
each column in the table. Each value is the number of tokens that will be
|
||||
inserted into the column value for each row. For example, setting the -colsize
|
||||
option to "5 10" creates an FTS table with 2 columns, with roughly 5 and 10
|
||||
tokens per row in each, respectively.
|
||||
|
||||
Each "FILE" argument should be a text file. The contents of these text files is
|
||||
split on whitespace characters to form a list of tokens. The first N1 tokens
|
||||
are used for the first column of the first row, where N1 is the first element
|
||||
of the -colsize list. The next N2 are used for the second column of the first
|
||||
row, and so on. Rows are added to the table until the entire list of tokens
|
||||
is exhausted.
|
||||
}
|
||||
exit -1
|
||||
}
|
||||
|
||||
set O(aColsize) [list 10 10 10]
|
||||
set O(tblname) t1
|
||||
set O(fts) fts5
|
||||
|
||||
|
||||
set options_with_values {-colsize}
|
||||
|
||||
for {set i 0} {$i < [llength $argv]} {incr i} {
|
||||
set opt [lindex $argv $i]
|
||||
if {[string range $opt 0 0]!="-"} break
|
||||
|
||||
if {[lsearch $options_with_values $opt]>=0} {
|
||||
incr i
|
||||
if {$i==[llength $argv]} usage
|
||||
set val [lindex $argv $i]
|
||||
}
|
||||
|
||||
switch -- $opt {
|
||||
-colsize {
|
||||
set O(aColSize) $val
|
||||
}
|
||||
|
||||
-fts4 {
|
||||
set O(fts) fts4
|
||||
}
|
||||
|
||||
-fts5 {
|
||||
set O(fts) fts5
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if {$i > [llength $argv]-2} usage
|
||||
set O(db) [lindex $argv $i]
|
||||
set O(files) [lrange $argv [expr $i+1] end]
|
||||
|
||||
foreach {k v} [lrange $argv 0 end-2] {
|
||||
switch -- $k {
|
||||
-colsize {
|
||||
set O(aColSize) $v
|
||||
}
|
||||
|
||||
-colsize {
|
||||
set O(aColSize) $v
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
sqlite3 db $O(db)
|
||||
load_static_extension db fts5
|
||||
|
||||
|
||||
# Create the FTS table in the db. Return a list of the table columns.
|
||||
#
|
||||
proc create_table {} {
|
||||
global O
|
||||
set cols [list a b c d e f g h i j k l m n o p q r s t u v w x y z]
|
||||
|
||||
set nCol [llength $O(aColsize)]
|
||||
set cols [lrange $cols 0 [expr $nCol-1]]
|
||||
|
||||
set sql "CREATE VIRTUAL TABLE IF NOT EXISTS $O(tblname) USING $O(fts) ("
|
||||
append sql [join $cols ,]
|
||||
append sql ");"
|
||||
|
||||
db eval $sql
|
||||
return $cols
|
||||
}
|
||||
|
||||
# Return a list of tokens from the named file.
|
||||
#
|
||||
proc readfile {file} {
|
||||
set fd [open $file]
|
||||
set data [read $fd]
|
||||
close $fd
|
||||
split $data
|
||||
}
|
||||
|
||||
|
||||
# Load all the data into a big list of tokens.
|
||||
#
|
||||
set tokens [list]
|
||||
foreach f $O(files) {
|
||||
set tokens [concat $tokens [readfile $f]]
|
||||
}
|
||||
|
||||
set N [llength $tokens]
|
||||
set i 0
|
||||
set cols [create_table]
|
||||
set sql "INSERT INTO $O(tblname) VALUES(\$[lindex $cols 0]"
|
||||
foreach c [lrange $cols 1 end] {
|
||||
append sql ", \$$c"
|
||||
}
|
||||
append sql ")"
|
||||
|
||||
db eval BEGIN
|
||||
while {$i < $N} {
|
||||
foreach c $cols s $O(aColsize) {
|
||||
set $c [lrange $tokens $i [expr $i+$s-1]]
|
||||
incr i $s
|
||||
}
|
||||
db eval $sql
|
||||
}
|
||||
db eval COMMIT
|
||||
|
||||
|
||||
|
@@ -34,6 +34,11 @@ set testprefix rtree1
|
||||
#
|
||||
# rtree-12.*: Test that on-conflict clauses are supported.
|
||||
# rtree-13.*: Test that bug [d2889096e7bdeac6d] has been fixed.
|
||||
# rtree-14.*: Test if a non-integer is inserted into the PK column of an
|
||||
# r-tree table, it is converted to an integer before being
|
||||
# inserted. Also that if a non-numeric is inserted into one
|
||||
# of the min/max dimension columns, it is converted to the
|
||||
# required type before being inserted.
|
||||
#
|
||||
|
||||
ifcapable !rtree {
|
||||
@@ -535,4 +540,54 @@ do_execsql_test 13.2 {
|
||||
SELECT * FROM r CROSS JOIN t9 WHERE id=x;
|
||||
} {1 1 0.0 0.0 2 2 0.0 0.0}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Test if a non-integer is inserted into the PK column of an r-tree
|
||||
# table, it is converted to an integer before being inserted. Also
|
||||
# that if a non-numeric is inserted into one of the min/max dimension
|
||||
# columns, it is converted to the required type before being inserted.
|
||||
#
|
||||
do_execsql_test 14.1 {
|
||||
CREATE VIRTUAL TABLE t10 USING rtree(ii, x1, x2);
|
||||
}
|
||||
|
||||
do_execsql_test 14.2 {
|
||||
INSERT INTO t10 VALUES(NULL, 1, 2);
|
||||
INSERT INTO t10 VALUES(NULL, 2, 3);
|
||||
INSERT INTO t10 VALUES('4xxx', 3, 4);
|
||||
INSERT INTO t10 VALUES(5.0, 4, 5);
|
||||
INSERT INTO t10 VALUES(6.4, 5, 6);
|
||||
}
|
||||
do_execsql_test 14.3 {
|
||||
SELECT * FROM t10;
|
||||
} {
|
||||
1 1.0 2.0 2 2.0 3.0 4 3.0 4.0 5 4.0 5.0 6 5.0 6.0
|
||||
}
|
||||
|
||||
do_execsql_test 14.4 {
|
||||
DELETE FROM t10;
|
||||
INSERT INTO t10 VALUES(1, 'one', 'two');
|
||||
INSERT INTO t10 VALUES(2, '52xyz', '81...');
|
||||
}
|
||||
do_execsql_test 14.5 {
|
||||
SELECT * FROM t10;
|
||||
} {
|
||||
1 0.0 0.0
|
||||
2 52.0 81.0
|
||||
}
|
||||
|
||||
do_execsql_test 14.4 {
|
||||
DROP TABLE t10;
|
||||
CREATE VIRTUAL TABLE t10 USING rtree_i32(ii, x1, x2);
|
||||
INSERT INTO t10 VALUES(1, 'one', 'two');
|
||||
INSERT INTO t10 VALUES(2, '52xyz', '81...');
|
||||
INSERT INTO t10 VALUES(3, 42.3, 49.9);
|
||||
}
|
||||
do_execsql_test 14.5 {
|
||||
SELECT * FROM t10;
|
||||
} {
|
||||
1 0 0
|
||||
2 52 81
|
||||
3 42 49
|
||||
}
|
||||
|
||||
finish_test
|
||||
|
67
manifest
67
manifest
@@ -1,5 +1,5 @@
|
||||
C Changes\sto\sthe\ssesssions\smodule\sensure\sthat\stables\sappear\swithin\schangesets\sand\spatchsets\sin\sthe\ssame\sorder\sthat\sthey\swere\sattached\sto\sthe\ssessions\sobject.
|
||||
D 2015-10-01T16:35:57.175
|
||||
C Merge\sthe\s3.8.12\sbeta\schanges\sfrom\strunk.
|
||||
D 2015-10-08T14:55:30.090
|
||||
F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
|
||||
F Makefile.in fdcfdc361f0a3723da9b48b967f259f7aaff3ad5
|
||||
F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
|
||||
@@ -82,7 +82,7 @@ F ext/fts3/fts3.c e028eb13432f108d2e22cded019fc980700e4e00
|
||||
F ext/fts3/fts3.h 3a10a0af180d502cecc50df77b1b22df142817fe
|
||||
F ext/fts3/fts3Int.h c84125c666ee54cef6efce6ff64abb0d0e2f4535
|
||||
F ext/fts3/fts3_aux.c 9edc3655fcb287f0467d0a4b886a01c6185fe9f1
|
||||
F ext/fts3/fts3_expr.c 71c063da9c2a4167fb54aec089dd5ef33a58c9cb
|
||||
F ext/fts3/fts3_expr.c dfd571a24412779ac01f25c01d888c6ef7b2d0ef
|
||||
F ext/fts3/fts3_hash.c 29b986e43f4e9dd40110eafa377dc0d63c422c60
|
||||
F ext/fts3/fts3_hash.h 39cf6874dc239d6b4e30479b1975fe5b22a3caaf
|
||||
F ext/fts3/fts3_icu.c deb46f7020d87ea7a14a433fb7a7f4bef42a9652
|
||||
@@ -96,7 +96,7 @@ F ext/fts3/fts3_tokenizer.h 64c6ef6c5272c51ebe60fc607a896e84288fcbc3
|
||||
F ext/fts3/fts3_tokenizer1.c 5c98225a53705e5ee34824087478cf477bdb7004
|
||||
F ext/fts3/fts3_unicode.c a93f5edc0aff44ef8b06d7cb55b52026541ca145
|
||||
F ext/fts3/fts3_unicode2.c c3d01968d497bd7001e7dc774ba75b372738c057
|
||||
F ext/fts3/fts3_write.c 6f7233a06df17084d5cd968899053731bf953f94
|
||||
F ext/fts3/fts3_write.c f442223e4a1914dc1fc12b65af7e4f2c255fa47c
|
||||
F ext/fts3/fts3speed.tcl b54caf6a18d38174f1a6e84219950d85e98bb1e9
|
||||
F ext/fts3/mkfts3amal.tcl 252ecb7fe6467854f2aa237bf2c390b74e71f100
|
||||
F ext/fts3/tool/fts3view.c 8e53d0190a7b3443764bbd32ad47be2bd852026d
|
||||
@@ -106,22 +106,22 @@ F ext/fts3/unicode/mkunicode.tcl 95cf7ec186e48d4985e433ff8a1c89090a774252
|
||||
F ext/fts3/unicode/parseunicode.tcl da577d1384810fb4e2b209bf3313074353193e95
|
||||
F ext/fts5/extract_api_docs.tcl a36e54ec777172ddd3f9a88daf593b00848368e0
|
||||
F ext/fts5/fts5.h 98f802fe41481f9d797fce496f0fefcad72c7782
|
||||
F ext/fts5/fts5Int.h 666aba8432940a8449a3bd4636e898fe906ed95d
|
||||
F ext/fts5/fts5Int.h ed6c05b803e0bacf85228a8d255853e89796f6f5
|
||||
F ext/fts5/fts5_aux.c 7a307760a9c57c750d043188ec0bad59f5b5ec7e
|
||||
F ext/fts5/fts5_buffer.c 64dcaf36a3ebda9e84b7c3b8788887ec325e12a4
|
||||
F ext/fts5/fts5_buffer.c b2fb69c1ee3378956c0d9ee964d61b59d296afaf
|
||||
F ext/fts5/fts5_config.c 57ee5fe71578cb494574fc0e6e51acb9a22a8695
|
||||
F ext/fts5/fts5_expr.c 667faaf14a69a5683ac383acdc8d942cf32c3f93
|
||||
F ext/fts5/fts5_expr.c 2054e550e75cffa117557c9416210c425934436d
|
||||
F ext/fts5/fts5_hash.c 4bf4b99708848357b8a2b5819e509eb6d3df9246
|
||||
F ext/fts5/fts5_index.c c77882ab38d698d5147cef96fa67a2121d77c0b3
|
||||
F ext/fts5/fts5_main.c 53116cffeb26898832ff7700cc5ebac5fe085d32
|
||||
F ext/fts5/fts5_storage.c 120f7b143688b5b7710dacbd48cff211609b8059
|
||||
F ext/fts5/fts5_index.c e03217c37f344f79673be385de6b03f732291000
|
||||
F ext/fts5/fts5_main.c aa96828990274927e2b404e0b6e60f9ae1274254
|
||||
F ext/fts5/fts5_storage.c df061a5caf9e50fbbd43113009b5b248362f4995
|
||||
F ext/fts5/fts5_tcl.c 6da58d6e8f42a93c4486b5ba9b187a7f995dee37
|
||||
F ext/fts5/fts5_test_mi.c e96be827aa8f571031e65e481251dc1981d608bf
|
||||
F ext/fts5/fts5_tokenize.c f380f46f341af9c9a9908e1aade685ba1eaa157a
|
||||
F ext/fts5/fts5_unicode2.c 78273fbd588d1d9bd0a7e4e0ccc9207348bae33c
|
||||
F ext/fts5/fts5_varint.c 3f86ce09cab152e3d45490d7586b7ed2e40c13f1
|
||||
F ext/fts5/fts5_vocab.c 4622e0b7d84a488a1585aaa56eb214ee67a988bc
|
||||
F ext/fts5/fts5parse.y 833db1101b78c0c47686ab1b84918e38c36e9452
|
||||
F ext/fts5/fts5_vocab.c 17320c476a5296ee475ab616d95fd10515bacfec
|
||||
F ext/fts5/fts5parse.y 38ab0ea7280a122f86f53b2264741422dd2424a0
|
||||
F ext/fts5/mkportersteps.tcl 5acf962d2e0074f701620bb5308155fa1e4a63ba
|
||||
F ext/fts5/test/fts5_common.tcl b6e6a40ef5d069c8e86ca4fbad491e1195485dbc
|
||||
F ext/fts5/test/fts5aa.test 4804f237005bb4ba8ea4a76120d8011ebcb5d611
|
||||
@@ -135,7 +135,7 @@ F ext/fts5/test/fts5ah.test e592c4978622dbc4de552cd0f9395df60ac5d54c
|
||||
F ext/fts5/test/fts5ai.test f20e53bbf0c55bc596f1fd47f2740dae028b8f37
|
||||
F ext/fts5/test/fts5aj.test 05b569f5c16ea3098fb1984eec5cf50dbdaae5d8
|
||||
F ext/fts5/test/fts5ak.test 7b8c5df96df599293f920b7e5521ebc79f647592
|
||||
F ext/fts5/test/fts5al.test 5c79525671862861906fa0a848da462a8473eafb
|
||||
F ext/fts5/test/fts5al.test a1b7b6393376bc2adc216527a28f5ae5594069df
|
||||
F ext/fts5/test/fts5alter.test 6022c61467a82aa11c70822ccad22b328dcf0d04
|
||||
F ext/fts5/test/fts5auto.test caa5bcf917db11944655a2a9bd38c67c520376ca
|
||||
F ext/fts5/test/fts5aux.test 8c687c948cc98e9a94be014df7d518acc1b3b74f
|
||||
@@ -164,16 +164,18 @@ F ext/fts5/test/fts5integrity.test 29f41d2c7126c6122fbb5d54e556506456876145
|
||||
F ext/fts5/test/fts5matchinfo.test 2163b0013e824bba65499da9e34ea4da41349cc2
|
||||
F ext/fts5/test/fts5merge.test 8f3cdba2ec9c5e7e568246e81b700ad37f764367
|
||||
F ext/fts5/test/fts5near.test b214cddb1c1f1bddf45c75af768f20145f7e71cc
|
||||
F ext/fts5/test/fts5onepass.test 7ed9608e258132cb8d55e7c479b08676ad68810c
|
||||
F ext/fts5/test/fts5optimize.test 42741e7c085ee0a1276140a752d4407d97c2c9f5
|
||||
F ext/fts5/test/fts5phrase.test f6d1d464da5beb25dc56277aa4f1d6102f0d9a2f
|
||||
F ext/fts5/test/fts5plan.test 6a55ecbac9890765b0e16f8c421c7e0888cfe436
|
||||
F ext/fts5/test/fts5porter.test 7cdc07bef301d70eebbfa75dcaf45c3680e1d0e1
|
||||
F ext/fts5/test/fts5porter2.test 2e65633d58a1c525d5af0f6c01e5a59155bb3487
|
||||
F ext/fts5/test/fts5prefix.test 552a462f0e8595676611f41643de217fb4ac2808
|
||||
F ext/fts5/test/fts5prefix.test ad3069f099ff593a2196422244fa218f8942dfb9
|
||||
F ext/fts5/test/fts5rank.test 11dcebba31d822f7e99685b4ea2c2ae3ec0b16f1
|
||||
F ext/fts5/test/fts5rebuild.test 03935f617ace91ed23a6099c7c74d905227ff29b
|
||||
F ext/fts5/test/fts5restart.test c17728fdea26e7d0f617d22ad5b4b2862b994c17
|
||||
F ext/fts5/test/fts5rowid.test 400384798349d658eaf06aefa1e364957d5d4821
|
||||
F ext/fts5/test/fts5simple.test 967b7144644ad4b40b2526160a5adfa896864c55
|
||||
F ext/fts5/test/fts5simple.test 85bbb268e01d2e3527d70a7fa511ddc3bba2ccc0
|
||||
F ext/fts5/test/fts5synonym.test cf88c0a56d5ea9591e3939ef1f6e294f7f2d0671
|
||||
F ext/fts5/test/fts5tokenizer.test ea4df698b35cc427ebf2ba22829d0e28386d8c89
|
||||
F ext/fts5/test/fts5unicode.test fbef8d8a3b4b88470536cc57604a82ca52e51841
|
||||
@@ -182,6 +184,7 @@ F ext/fts5/test/fts5unicode3.test 35c3d02aa7acf7d43d8de3bfe32c15ba96e8928e
|
||||
F ext/fts5/test/fts5unindexed.test e9539d5b78c677315e7ed8ea911d4fd25437c680
|
||||
F ext/fts5/test/fts5version.test 978f59541d8cef7e8591f8be2115ec5ccb863e2e
|
||||
F ext/fts5/test/fts5vocab.test cdf97b9678484e9bad5062edf9c9106e5c3b0c5c
|
||||
F ext/fts5/tool/fts5txt2db.tcl 3d19fb8ffb234031d33d7d2151acfbc55e9cfcc4
|
||||
F ext/fts5/tool/loadfts5.tcl 58e90407cc5c2b1770460119488fd7c0090d4dd3
|
||||
F ext/fts5/tool/mkfts5c.tcl 5745072c7de346e18c7f491e4c3281fe8a1cfe51
|
||||
F ext/fts5/tool/showfts5.tcl 9eaf6c3df352f98a2ab5ce1921dd94128ab1381d
|
||||
@@ -235,7 +238,7 @@ F ext/rbu/test_rbu.c 2a3652241fa45d5eaa141775e4ae68c1d3582c03
|
||||
F ext/rtree/README 6315c0d73ebf0ec40dedb5aa0e942bc8b54e3761
|
||||
F ext/rtree/rtree.c 0f9b595bd0debcbedf1d7a63d0e0678d619e6c9c
|
||||
F ext/rtree/rtree.h 834dbcb82dc85b2481cde6a07cdadfddc99e9b9e
|
||||
F ext/rtree/rtree1.test 541bbcab74613907fea08b2ecdcdd5b7aa724cc9
|
||||
F ext/rtree/rtree1.test 96a80c08440c932cd72aac50660e7af2612d2cda
|
||||
F ext/rtree/rtree2.test acbb3a4ce0f4fbc2c304d2b4b784cfa161856bba
|
||||
F ext/rtree/rtree3.test a494da55c30ee0bc9b01a91c80c81b387b22d2dc
|
||||
F ext/rtree/rtree4.test c8fe384f60ebd49540a5fecc990041bf452eb6e0
|
||||
@@ -338,7 +341,7 @@ F src/mutex.c 8e45800ee78e0cd1f1f3fe8e398853307f4a085c
|
||||
F src/mutex.h 779d588e3b7756ec3ecf7d78cde1d84aba414f85
|
||||
F src/mutex_noop.c 9d4309c075ba9cc7249e19412d3d62f7f94839c4
|
||||
F src/mutex_unix.c a94b46f3f7beba307dde1b298b0f99f9c3677a93
|
||||
F src/mutex_w32.c b483d3e5914b84c82516a6a9919582f12ef3b838
|
||||
F src/mutex_w32.c 5e6fe1c298fb5a8a15aaed4161d5759311431c17
|
||||
F src/notify.c 9711a7575036f0d3040ba61bc6e217f13a9888e7
|
||||
F src/os.c 8fd25588eeba74068d41102d26810e216999b6c8
|
||||
F src/os.h 3e57a24e2794a94d3cf2342c6d9a884888cd96bf
|
||||
@@ -353,19 +356,19 @@ F src/parse.y f599aa5e871a493330d567ced93de696f61f48f7
|
||||
F src/pcache.c 24be750c79272e0ca7b6e007bc94999700f3e5ef
|
||||
F src/pcache.h 9968603796240cdf83da7e7bef76edf90619cea9
|
||||
F src/pcache1.c e822007159d53a7ea7aa040d6e28964ddb6de083
|
||||
F src/pragma.c 234814978bcd35bce6e2874dfb2f5b5e28e7fb38
|
||||
F src/pragma.c dcfe3a35d2de935feeaba1455528b4a5c4f1208c
|
||||
F src/pragma.h 631a91c8b0e6ca8f051a1d8a4a0da4150e04620a
|
||||
F src/prepare.c 82e5db1013846a819f198336fed72c44c974e7b1
|
||||
F src/printf.c 0c4bcdd1c2e2521024f0a69cb5eb334f86b3652a
|
||||
F src/random.c ba2679f80ec82c4190062d756f22d0c358180696
|
||||
F src/resolve.c 1954a0f01bf65d78d7d559aea3d5c67f33376d91
|
||||
F src/rowset.c eccf6af6d620aaa4579bd3b72c1b6395d9e9fa1e
|
||||
F src/select.c e49f4af9748c9e0cc1bf864b4190aa94841c8409
|
||||
F src/select.c 2c4bfdf7c797df9b43121ed7850bf939b6f27405
|
||||
F src/shell.c f38cfe6a0b971d50158e71880852119bdca89ce9
|
||||
F src/sqlite.h.in 8f4deb5874227c7635300fb75105ff6e92131fb5
|
||||
F src/sqlite3.rc 992c9f5fb8285ae285d6be28240a7e8d3a7f2bad
|
||||
F src/sqlite3ext.h 64350bf36833a56ad675e27392a913f417c5c308
|
||||
F src/sqliteInt.h a6edcbabcd78cfd888f74aaedb2160241a38294f
|
||||
F src/sqliteInt.h 4150e72a668204fbcbdfbcc5f44c9aa5151663f8
|
||||
F src/sqliteLimit.h 216557999cb45f2e3578ed53ebefe228d779cb46
|
||||
F src/status.c f266ad8a2892d659b74f0f50cb6a88b6e7c12179
|
||||
F src/table.c 51b46b2a62d1b3a959633d593b89bab5e2c9155e
|
||||
@@ -423,7 +426,7 @@ F src/update.c 9e102cc3d8732aeb6b977569c8ce1f73fb0031bd
|
||||
F src/utf.c fc6b889ba0779b7722634cdeaa25f1930d93820c
|
||||
F src/util.c fc612367108b74573c5fd13a85d0a23027f438bd
|
||||
F src/vacuum.c 2ddd5cad2a7b9cef7f9e431b8c7771634c6b1701
|
||||
F src/vdbe.c b61897b3e827e600f2a773031326471e49205fa5
|
||||
F src/vdbe.c fd0fba1ba0d117a65f16252d38083fa17eb9c44a
|
||||
F src/vdbe.h 67151895e779b35475c6c11b16be2ceb839066c8
|
||||
F src/vdbeInt.h 42fa34502937071aabd3c0596575ba9776547353
|
||||
F src/vdbeapi.c f5eda36a5c85ef578957ab4311e8d9b1f51a3552
|
||||
@@ -432,7 +435,7 @@ F src/vdbeblob.c b400c25ac822af3c507ef84b5cd93c1583a70321
|
||||
F src/vdbemem.c 28ab8455ac490373798cf2c21def2c1287942551
|
||||
F src/vdbesort.c f5009e7a35e3065635d8918b9a31f498a499976b
|
||||
F src/vdbetrace.c 8befe829faff6d9e6f6e4dee5a7d3f85cc85f1a0
|
||||
F src/vtab.c 9a6d8818c8a2477ce547f064701b5e955b25d894
|
||||
F src/vtab.c 2a8b44aa372c33f6154208e7a7f6c44254549806
|
||||
F src/vxworks.h c18586c8edc1bddbc15c004fa16aeb1e1342b4fb
|
||||
F src/wal.c 18b0ed49830cf04fe2d68224b41838a73ac6cd24
|
||||
F src/wal.h df01efe09c5cb8c8e391ff1715cca294f89668a4
|
||||
@@ -440,7 +443,7 @@ F src/walker.c 2e14d17f592d176b6dc879c33fbdec4fbccaa2ba
|
||||
F src/where.c 4c4646675e794ac71e701289edefd7cd81bac844
|
||||
F src/whereInt.h 7892bb54cf9ca0ae5c7e6094491b94c9286dc647
|
||||
F src/wherecode.c a32bf1f304f6328e3eefcb82e70bd86836cff343
|
||||
F src/whereexpr.c 2473e4350e30f9b55d1c6a8f66ca23c689f23f1d
|
||||
F src/whereexpr.c e63244ca06c503e5f3c5b7f3c9aea0db826089ed
|
||||
F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2
|
||||
F test/affinity2.test a6d901b436328bd67a79b41bb0ac2663918fe3bd
|
||||
F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2
|
||||
@@ -722,7 +725,7 @@ F test/fts3drop.test 1b906e293d6773812587b3dc458cb9e8f3f0c297
|
||||
F test/fts3e.test 1f6c6ac9cc8b772ca256e6b22aaeed50c9350851
|
||||
F test/fts3expr.test 3401d47b229c4504424caf362cc4ff704cad4162
|
||||
F test/fts3expr2.test 18da930352e5693eaa163a3eacf96233b7290d1a
|
||||
F test/fts3expr3.test 9e91b8edbcb197bf2e92161aa7696446d96dce5f
|
||||
F test/fts3expr3.test c4d4a7d6327418428c96e0a3a1137c251b8dfbf8
|
||||
F test/fts3expr4.test e1be1248566f43c252d4404d52914f1fc4bfa065
|
||||
F test/fts3expr5.test f9abfffbf5e53d48a33e12a1e8f8ba2c551c9b49
|
||||
F test/fts3fault.test da49627b280b210ebc6657f76344c7851f10ce66
|
||||
@@ -918,6 +921,7 @@ F test/notify3.test 10ff25cde502e72a92053a2f215d64bece4ef934
|
||||
F test/notnull.test f8fcf58669ddba79274daa2770d61dfad8274f62
|
||||
F test/null.test 0dcce4f04284ec66108c503327ad6d224c0752b3
|
||||
F test/numcast.test 5d126f7f581432e86a90d1e35cac625164aec4a1
|
||||
F test/offset1.test f06b83657bcf26f9ce805e67450e189e282143b2
|
||||
F test/openv2.test 0d3040974bf402e19b7df4b783e447289d7ab394
|
||||
F test/orderby1.test 870e150450437d3980badbde3d0166b81d9e33f6
|
||||
F test/orderby2.test bc11009f7cd99d96b1b11e57b199b00633eb5b04
|
||||
@@ -1050,7 +1054,7 @@ F test/speed4p.explain 6b5f104ebeb34a038b2f714150f51d01143e59aa
|
||||
F test/speed4p.test 0e51908951677de5a969b723e03a27a1c45db38b
|
||||
F test/speedtest1.c 857439869d1cb4db35e1c720ee9c2756eb9ea2a0
|
||||
F test/spellfix.test 0597065ff57042df1f138e6a2611ae19c2698135
|
||||
F test/spellfix2.test 1ff48bb65b6198d21674ae24d19bb136e547585a
|
||||
F test/spellfix2.test dfc8f519a3fc204cb2dfa8b4f29821ae90f6f8c3
|
||||
F test/sqldiff1.test 8f6bc7c6a5b3585d350d779c6078869ba402f8f5
|
||||
F test/sqllimits1.test 89b3d5aad05b99f707ee3786bdd4416dccf83304
|
||||
F test/stat.test 8de91498c99f5298b303f70f1d1f3b9557af91bf
|
||||
@@ -1261,7 +1265,7 @@ F test/unique2.test 3674e9f2a3f1fbbfd4772ac74b7a97090d0f77d2
|
||||
F test/unixexcl.test cd6c765f75e50e8e2c2ba763149e5d340ea19825
|
||||
F test/unordered.test ca7adce0419e4ca0c50f039885e76ed2c531eda8
|
||||
F test/update.test 6c68446b8a0a33d522a7c72b320934596a2d7d32
|
||||
F test/uri.test 23662b7b61958b0f0e47082de7d06341ccf85d5b
|
||||
F test/uri.test 6630ecbdea2aac10df3c89dbae2243f4c2c353e4
|
||||
F test/userauth01.test e740a2697a7b40d7c5003a7d7edaee16acd349a9
|
||||
F test/utf16align.test 54cd35a27c005a9b6e7815d887718780b6a462ae
|
||||
F test/vacuum.test ce91c39f7f91a4273bf620efad21086b5aa6ef1d
|
||||
@@ -1357,7 +1361,6 @@ F test/zerodamage.test cf6748bad89553cc1632be51a6f54e487e4039ac
|
||||
F tool/build-all-msvc.bat 761d8c82a1a529261291812732a853a1b4256d85 x
|
||||
F tool/build-shell.sh 950f47c6174f1eea171319438b93ba67ff5bf367
|
||||
F tool/checkSpacing.c 810e51703529a204fc4e1eb060e9ab663e3c06d2
|
||||
F tool/diffdb.c 7524b1b5df217c20cd0431f6789851a4e0cb191b
|
||||
F tool/extract.c 054069d81b095fbdc189a6f5d4466e40380505e2
|
||||
F tool/fast_vacuum.c 5ba0d6f5963a0a63bdc42840f678bad75b2ebce1
|
||||
F tool/fragck.tcl 5265a95126abcf6ab357f7efa544787e5963f439
|
||||
@@ -1381,7 +1384,6 @@ F tool/mksqlite3internalh.tcl eb994013e833359137eb53a55acdad0b5ae1049b
|
||||
F tool/mkvsix.tcl bbe57cd9ae11c6cc70319241101ef8d2b8c3765b
|
||||
F tool/offsets.c fe4262fdfa378e8f5499a42136d17bf3b98f6091
|
||||
F tool/omittest.tcl 34d7ac01fe4fd18e3637f64abe12c40eca0f6b97
|
||||
F tool/opcodeDoc.awk b3a2a3d5d3075b8bd90b7afe24283efdd586659c
|
||||
F tool/pagesig.c ff0ca355fd3c2398e933da5e22439bbff89b803b
|
||||
F tool/restore_jrnl.tcl 6957a34f8f1f0f8285e07536225ec3b292a9024a
|
||||
F tool/rollback-test.c 9fc98427d1e23e84429d7e6d07d9094fbdec65a5
|
||||
@@ -1391,7 +1393,6 @@ F tool/showlocks.c 9920bcc64f58378ff1118caead34147201f48c68
|
||||
F tool/showstat4.c 9515faa8ec176599d4a8288293ba8ec61f7b728a
|
||||
F tool/showwal.c 85cb36d4fe3e93e2fbd63e786e0d1ce42d0c4fad
|
||||
F tool/soak1.tcl 8d407956e1a45b485a8e072470a3e629a27037fe
|
||||
F tool/space_used.tcl f714c41a59e326b8b9042f415b628b561bafa06b
|
||||
F tool/spaceanal.tcl 93c1fdc9733c525b17a2024c7df193daa002e037
|
||||
F tool/speedtest.tcl 06c76698485ccf597b9e7dbb1ac70706eb873355
|
||||
F tool/speedtest16.c c8a9c793df96db7e4933f0852abb7a03d48f2e81
|
||||
@@ -1410,7 +1411,7 @@ F tool/vdbe_profile.tcl 67746953071a9f8f2f668b73fe899074e2c6d8c1
|
||||
F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4
|
||||
F tool/warnings.sh 48bd54594752d5be3337f12c72f28d2080cb630b
|
||||
F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f
|
||||
P b2face9aa95ade96a5666c70b6b31064c1ad0977
|
||||
R 67f4bc03b9ceb2707f867778f6790d70
|
||||
U dan
|
||||
Z c3622f418cca20fcda99ae888e36e276
|
||||
P 7695890230dc1e0c6db9b7aa509db2039c7f7239 77b707b77496a08703fe9405e8e4521a4e5b419e
|
||||
R 484e6e15f7bc3708de45fd25742a1667
|
||||
U drh
|
||||
Z 02c522f97e4db3ce71ca22576dc5b408
|
||||
|
@@ -1 +1 @@
|
||||
7695890230dc1e0c6db9b7aa509db2039c7f7239
|
||||
35b1b8d4b97715030700e37b292bb4f1bb3f44d6
|
@@ -87,7 +87,10 @@ void sqlite3MemoryBarrier(void){
|
||||
SQLITE_MEMORY_BARRIER;
|
||||
#elif defined(__GNUC__)
|
||||
__sync_synchronize();
|
||||
#else
|
||||
#elif !defined(SQLITE_DISABLE_INTRINSIC) && \
|
||||
defined(_MSC_VER) && _MSC_VER>=1300
|
||||
_ReadWriteBarrier();
|
||||
#elif defined(MemoryBarrier)
|
||||
MemoryBarrier();
|
||||
#endif
|
||||
}
|
||||
|
11
src/pragma.c
11
src/pragma.c
@@ -1361,8 +1361,9 @@ void sqlite3Pragma(
|
||||
*/
|
||||
static const int iLn = VDBE_OFFSET_LINENO(2);
|
||||
static const VdbeOpList endCode[] = {
|
||||
{ OP_IfNeg, 1, 0, 0}, /* 0 */
|
||||
{ OP_String8, 0, 3, 0}, /* 1 */
|
||||
{ OP_AddImm, 1, 0, 0}, /* 0 */
|
||||
{ OP_If, 1, 0, 0}, /* 1 */
|
||||
{ OP_String8, 0, 3, 0}, /* 2 */
|
||||
{ OP_ResultRow, 3, 1, 0},
|
||||
};
|
||||
|
||||
@@ -1563,9 +1564,9 @@ void sqlite3Pragma(
|
||||
}
|
||||
}
|
||||
addr = sqlite3VdbeAddOpList(v, ArraySize(endCode), endCode, iLn);
|
||||
sqlite3VdbeChangeP3(v, addr, -mxErr);
|
||||
sqlite3VdbeJumpHere(v, addr);
|
||||
sqlite3VdbeChangeP4(v, addr+1, "ok", P4_STATIC);
|
||||
sqlite3VdbeChangeP2(v, addr, -mxErr);
|
||||
sqlite3VdbeJumpHere(v, addr+1);
|
||||
sqlite3VdbeChangeP4(v, addr+2, "ok", P4_STATIC);
|
||||
}
|
||||
break;
|
||||
#endif /* SQLITE_OMIT_INTEGRITY_CHECK */
|
||||
|
24
src/select.c
24
src/select.c
@@ -579,7 +579,7 @@ static void pushOntoSorter(
|
||||
}else{
|
||||
iLimit = pSelect->iLimit;
|
||||
}
|
||||
addr = sqlite3VdbeAddOp3(v, OP_IfNotZero, iLimit, 0, -1); VdbeCoverage(v);
|
||||
addr = sqlite3VdbeAddOp3(v, OP_IfNotZero, iLimit, 0, 1); VdbeCoverage(v);
|
||||
sqlite3VdbeAddOp1(v, OP_Last, pSort->iECursor);
|
||||
sqlite3VdbeAddOp1(v, OP_Delete, pSort->iECursor);
|
||||
sqlite3VdbeJumpHere(v, addr);
|
||||
@@ -595,11 +595,8 @@ static void codeOffset(
|
||||
int iContinue /* Jump here to skip the current record */
|
||||
){
|
||||
if( iOffset>0 ){
|
||||
int addr;
|
||||
addr = sqlite3VdbeAddOp3(v, OP_IfNeg, iOffset, 0, -1); VdbeCoverage(v);
|
||||
sqlite3VdbeGoto(v, iContinue);
|
||||
VdbeComment((v, "skip OFFSET records"));
|
||||
sqlite3VdbeJumpHere(v, addr);
|
||||
sqlite3VdbeAddOp3(v, OP_IfPos, iOffset, iContinue, 1); VdbeCoverage(v);
|
||||
VdbeComment((v, "OFFSET"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1815,7 +1812,7 @@ static void computeLimitRegisters(Parse *pParse, Select *p, int iBreak){
|
||||
Vdbe *v = 0;
|
||||
int iLimit = 0;
|
||||
int iOffset;
|
||||
int addr1, n;
|
||||
int n;
|
||||
if( p->iLimit ) return;
|
||||
|
||||
/*
|
||||
@@ -1850,14 +1847,10 @@ static void computeLimitRegisters(Parse *pParse, Select *p, int iBreak){
|
||||
sqlite3ExprCode(pParse, p->pOffset, iOffset);
|
||||
sqlite3VdbeAddOp1(v, OP_MustBeInt, iOffset); VdbeCoverage(v);
|
||||
VdbeComment((v, "OFFSET counter"));
|
||||
addr1 = sqlite3VdbeAddOp1(v, OP_IfPos, iOffset); VdbeCoverage(v);
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, 0, iOffset);
|
||||
sqlite3VdbeJumpHere(v, addr1);
|
||||
sqlite3VdbeAddOp3(v, OP_SetIfNotPos, iOffset, iOffset, 0);
|
||||
sqlite3VdbeAddOp3(v, OP_Add, iLimit, iOffset, iOffset+1);
|
||||
VdbeComment((v, "LIMIT+OFFSET"));
|
||||
addr1 = sqlite3VdbeAddOp1(v, OP_IfPos, iLimit); VdbeCoverage(v);
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, -1, iOffset+1);
|
||||
sqlite3VdbeJumpHere(v, addr1);
|
||||
sqlite3VdbeAddOp3(v, OP_SetIfNotPos, iLimit, iOffset+1, -1);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2273,6 +2266,11 @@ static int multiSelect(
|
||||
if( p->iLimit ){
|
||||
addr = sqlite3VdbeAddOp1(v, OP_IfNot, p->iLimit); VdbeCoverage(v);
|
||||
VdbeComment((v, "Jump ahead if LIMIT reached"));
|
||||
if( p->iOffset ){
|
||||
sqlite3VdbeAddOp3(v, OP_SetIfNotPos, p->iOffset, p->iOffset, 0);
|
||||
sqlite3VdbeAddOp3(v, OP_Add, p->iLimit, p->iOffset, p->iOffset+1);
|
||||
sqlite3VdbeAddOp3(v, OP_SetIfNotPos, p->iLimit, p->iOffset+1, -1);
|
||||
}
|
||||
}
|
||||
explainSetInteger(iSub2, pParse->iNextSelectId);
|
||||
rc = sqlite3Select(pParse, p, &dest);
|
||||
|
@@ -196,6 +196,7 @@
|
||||
# include <intrin.h>
|
||||
# pragma intrinsic(_byteswap_ushort)
|
||||
# pragma intrinsic(_byteswap_ulong)
|
||||
# pragma intrinsic(_ReadWriteBarrier)
|
||||
# else
|
||||
# include <cmnintrin.h>
|
||||
# endif
|
||||
|
40
src/vdbe.c
40
src/vdbe.c
@@ -5732,12 +5732,12 @@ case OP_MemMax: { /* in2 */
|
||||
}
|
||||
#endif /* SQLITE_OMIT_AUTOINCREMENT */
|
||||
|
||||
/* Opcode: IfPos P1 P2 * * *
|
||||
** Synopsis: if r[P1]>0 goto P2
|
||||
/* Opcode: IfPos P1 P2 P3 * *
|
||||
** Synopsis: if r[P1]>0 then r[P1]-=P3, goto P2
|
||||
**
|
||||
** Register P1 must contain an integer.
|
||||
** If the value of register P1 is 1 or greater, jump to P2 and
|
||||
** add the literal value P3 to register P1.
|
||||
** If the value of register P1 is 1 or greater, subtract P3 from the
|
||||
** value in P1 and jump to P2.
|
||||
**
|
||||
** If the initial value of register P1 is less than 1, then the
|
||||
** value is unchanged and control passes through to the next instruction.
|
||||
@@ -5746,38 +5746,44 @@ case OP_IfPos: { /* jump, in1 */
|
||||
pIn1 = &aMem[pOp->p1];
|
||||
assert( pIn1->flags&MEM_Int );
|
||||
VdbeBranchTaken( pIn1->u.i>0, 2);
|
||||
if( pIn1->u.i>0 ) goto jump_to_p2;
|
||||
if( pIn1->u.i>0 ){
|
||||
pIn1->u.i -= pOp->p3;
|
||||
goto jump_to_p2;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
/* Opcode: IfNeg P1 P2 P3 * *
|
||||
** Synopsis: r[P1]+=P3, if r[P1]<0 goto P2
|
||||
/* Opcode: SetIfNotPos P1 P2 P3 * *
|
||||
** Synopsis: if r[P1]<=0 then r[P2]=P3
|
||||
**
|
||||
** Register P1 must contain an integer. Add literal P3 to the value in
|
||||
** register P1 then if the value of register P1 is less than zero, jump to P2.
|
||||
** Register P1 must contain an integer.
|
||||
** If the value of register P1 is not positive (if it is less than 1) then
|
||||
** set the value of register P2 to be the integer P3.
|
||||
*/
|
||||
case OP_IfNeg: { /* jump, in1 */
|
||||
case OP_SetIfNotPos: { /* in1, in2 */
|
||||
pIn1 = &aMem[pOp->p1];
|
||||
assert( pIn1->flags&MEM_Int );
|
||||
pIn1->u.i += pOp->p3;
|
||||
VdbeBranchTaken(pIn1->u.i<0, 2);
|
||||
if( pIn1->u.i<0 ) goto jump_to_p2;
|
||||
if( pIn1->u.i<=0 ){
|
||||
pOut = out2Prerelease(p, pOp);
|
||||
pOut->u.i = pOp->p3;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
/* Opcode: IfNotZero P1 P2 P3 * *
|
||||
** Synopsis: if r[P1]!=0 then r[P1]+=P3, goto P2
|
||||
** Synopsis: if r[P1]!=0 then r[P1]-=P3, goto P2
|
||||
**
|
||||
** Register P1 must contain an integer. If the content of register P1 is
|
||||
** initially nonzero, then add P3 to P1 and jump to P2. If register P1 is
|
||||
** initially zero, leave it unchanged and fall through.
|
||||
** initially nonzero, then subtract P3 from the value in register P1 and
|
||||
** jump to P2. If register P1 is initially zero, leave it unchanged
|
||||
** and fall through.
|
||||
*/
|
||||
case OP_IfNotZero: { /* jump, in1 */
|
||||
pIn1 = &aMem[pOp->p1];
|
||||
assert( pIn1->flags&MEM_Int );
|
||||
VdbeBranchTaken(pIn1->u.i<0, 2);
|
||||
if( pIn1->u.i ){
|
||||
pIn1->u.i += pOp->p3;
|
||||
pIn1->u.i -= pOp->p3;
|
||||
goto jump_to_p2;
|
||||
}
|
||||
break;
|
||||
|
@@ -1145,7 +1145,7 @@ int sqlite3VtabEponymousTableInit(Parse *pParse, Module *pMod){
|
||||
*/
|
||||
void sqlite3VtabEponymousTableClear(sqlite3 *db, Module *pMod){
|
||||
Table *pTab = pMod->pEpoTab;
|
||||
if( (pTab = pMod->pEpoTab)!=0 ){
|
||||
if( pTab!=0 ){
|
||||
sqlite3DeleteColumnNames(db, pTab);
|
||||
sqlite3VtabClear(db, pTab);
|
||||
sqlite3DbFree(db, pTab);
|
||||
|
@@ -950,7 +950,6 @@ static void exprAnalyze(
|
||||
pNew = pTerm;
|
||||
}
|
||||
exprCommute(pParse, pDup);
|
||||
pLeft = sqlite3ExprSkipCollate(pDup->pLeft);
|
||||
pNew->leftCursor = iCur;
|
||||
pNew->u.leftColumn = iColumn;
|
||||
testcase( (prereqLeft | extraRight) != prereqLeft );
|
||||
|
@@ -122,6 +122,8 @@ proc balanced_andor_tree {nEntry} {
|
||||
return $tree
|
||||
}
|
||||
|
||||
if 1 {
|
||||
|
||||
# Test that queries like "1 AND 2 AND 3 AND 4..." are transformed to
|
||||
# balanced trees by FTS.
|
||||
#
|
||||
@@ -202,5 +204,35 @@ do_faultsim_test fts3expr3-fault-1 -faults oom-* -body {
|
||||
faultsim_test_result [list 0 $::result]
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
foreach {tn expr res} {
|
||||
1 {1 OR 2 OR 3 OR 4} {OR {OR {P 1} {P 2}} {OR {P 3} {P 4}}}
|
||||
2 {1 OR (2 AND 3 AND 4 AND 5)}
|
||||
{OR {P 1} {AND {AND {P 2} {P 3}} {AND {P 4} {P 5}}}}
|
||||
3 {(2 AND 3 AND 4 AND 5) OR 1}
|
||||
{OR {AND {AND {P 2} {P 3}} {AND {P 4} {P 5}}} {P 1}}
|
||||
|
||||
4 {1 AND (2 OR 3 OR 4 OR 5)}
|
||||
{AND {P 1} {OR {OR {P 2} {P 3}} {OR {P 4} {P 5}}}}
|
||||
5 {(2 OR 3 OR 4 OR 5) AND 1}
|
||||
{AND {OR {OR {P 2} {P 3}} {OR {P 4} {P 5}}} {P 1}}
|
||||
|
||||
6 {(2 OR 3 OR 4 OR 5) NOT 1}
|
||||
{NOT {OR {OR {P 2} {P 3}} {OR {P 4} {P 5}}} {P 1}}
|
||||
|
||||
7 {1 NOT (2 OR 3 OR 4 OR 5)}
|
||||
{NOT {P 1} {OR {OR {P 2} {P 3}} {OR {P 4} {P 5}}}}
|
||||
|
||||
8 {(1 OR 2 OR 3 OR 4) NOT (5 AND 6 AND 7 AND 8)}
|
||||
{NOT {OR {OR {P 1} {P 2}} {OR {P 3} {P 4}}} {AND {AND {P 5} {P 6}} {AND {P 7} {P 8}}}}
|
||||
} {
|
||||
do_test 5.1.$tn {
|
||||
test_fts3expr2 $expr
|
||||
} $res
|
||||
}
|
||||
|
||||
set sqlite_fts3_enable_parentheses 0
|
||||
finish_test
|
||||
|
161
test/offset1.test
Normal file
161
test/offset1.test
Normal file
@@ -0,0 +1,161 @@
|
||||
# 2015-10-06
|
||||
#
|
||||
# The author disclaims copyright to this source code. In place of
|
||||
# a legal notice, here is a blessing:
|
||||
#
|
||||
# May you do good and not evil.
|
||||
# May you find forgiveness for yourself and forgive others.
|
||||
# May you share freely, never taking more than you give.
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
# This file implements test cases for the [b65cb2c8d91f6685841d7d1e13b6]
|
||||
# bug: Correct handling of LIMIT and OFFSET on a UNION ALL query where
|
||||
# the right-hand SELECT contains an ORDER BY in a subquery.
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
|
||||
ifcapable !compound {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
do_execsql_test offset1-1.1 {
|
||||
CREATE TABLE t1(a,b);
|
||||
INSERT INTO t1 VALUES(1,'a'),(2,'b'),(3,'c'),(4,'d'),(5,'e');
|
||||
CREATE TABLE t2(x,y);
|
||||
INSERT INTO t2 VALUES(8,'y'),(9,'z'),(6,'w'),(7,'x');
|
||||
SELECT count(*) FROM t1, t2;
|
||||
} {20}
|
||||
|
||||
do_execsql_test offset1-1.2.0 {
|
||||
SELECT a, b FROM t1
|
||||
UNION ALL
|
||||
SELECT * FROM (SELECT x, y FROM t2 ORDER BY y)
|
||||
LIMIT 3 OFFSET 0;
|
||||
} {1 a 2 b 3 c}
|
||||
do_execsql_test offset1-1.2.1 {
|
||||
SELECT a, b FROM t1
|
||||
UNION ALL
|
||||
SELECT * FROM (SELECT x, y FROM t2 ORDER BY y)
|
||||
LIMIT 3 OFFSET 1;
|
||||
} {2 b 3 c 4 d}
|
||||
do_execsql_test offset1-1.2.2 {
|
||||
SELECT a, b FROM t1
|
||||
UNION ALL
|
||||
SELECT * FROM (SELECT x, y FROM t2 ORDER BY y)
|
||||
LIMIT 3 OFFSET 2;
|
||||
} {3 c 4 d 5 e}
|
||||
do_execsql_test offset1-1.2.3 {
|
||||
SELECT a, b FROM t1
|
||||
UNION ALL
|
||||
SELECT * FROM (SELECT x, y FROM t2 ORDER BY y)
|
||||
LIMIT 3 OFFSET 3;
|
||||
} {4 d 5 e 6 w}
|
||||
do_execsql_test offset1-1.2.4 {
|
||||
SELECT a, b FROM t1
|
||||
UNION ALL
|
||||
SELECT * FROM (SELECT x, y FROM t2 ORDER BY y)
|
||||
LIMIT 3 OFFSET 4;
|
||||
} {5 e 6 w 7 x}
|
||||
do_execsql_test offset1-1.2.5 {
|
||||
SELECT a, b FROM t1
|
||||
UNION ALL
|
||||
SELECT * FROM (SELECT x, y FROM t2 ORDER BY y)
|
||||
LIMIT 3 OFFSET 5;
|
||||
} {6 w 7 x 8 y}
|
||||
do_execsql_test offset1-1.2.6 {
|
||||
SELECT a, b FROM t1
|
||||
UNION ALL
|
||||
SELECT * FROM (SELECT x, y FROM t2 ORDER BY y)
|
||||
LIMIT 3 OFFSET 6;
|
||||
} {7 x 8 y 9 z}
|
||||
do_execsql_test offset1-1.2.7 {
|
||||
SELECT a, b FROM t1
|
||||
UNION ALL
|
||||
SELECT * FROM (SELECT x, y FROM t2 ORDER BY y)
|
||||
LIMIT 3 OFFSET 7;
|
||||
} {8 y 9 z}
|
||||
do_execsql_test offset1-1.2.8 {
|
||||
SELECT a, b FROM t1
|
||||
UNION ALL
|
||||
SELECT * FROM (SELECT x, y FROM t2 ORDER BY y)
|
||||
LIMIT 3 OFFSET 8;
|
||||
} {9 z}
|
||||
do_execsql_test offset1-1.2.9 {
|
||||
SELECT a, b FROM t1
|
||||
UNION ALL
|
||||
SELECT * FROM (SELECT x, y FROM t2 ORDER BY y)
|
||||
LIMIT 3 OFFSET 9;
|
||||
} {}
|
||||
|
||||
do_execsql_test offset1-1.3.0 {
|
||||
SELECT * FROM t1 LIMIT 0;
|
||||
} {}
|
||||
|
||||
do_execsql_test offset1-1.4.0 {
|
||||
SELECT a, b FROM t1
|
||||
UNION ALL
|
||||
SELECT * FROM (SELECT x, y FROM t2 ORDER BY y)
|
||||
LIMIT 0 OFFSET 1;
|
||||
} {}
|
||||
do_execsql_test offset1-1.4.1 {
|
||||
SELECT a, b FROM t1
|
||||
UNION ALL
|
||||
SELECT * FROM (SELECT x, y FROM t2 ORDER BY y)
|
||||
LIMIT 1 OFFSET 1;
|
||||
} {2 b}
|
||||
do_execsql_test offset1-1.4.2 {
|
||||
SELECT a, b FROM t1
|
||||
UNION ALL
|
||||
SELECT * FROM (SELECT x, y FROM t2 ORDER BY y)
|
||||
LIMIT 2 OFFSET 1;
|
||||
} {2 b 3 c}
|
||||
do_execsql_test offset1-1.4.3 {
|
||||
SELECT a, b FROM t1
|
||||
UNION ALL
|
||||
SELECT * FROM (SELECT x, y FROM t2 ORDER BY y)
|
||||
LIMIT 3 OFFSET 1;
|
||||
} {2 b 3 c 4 d}
|
||||
do_execsql_test offset1-1.4.4 {
|
||||
SELECT a, b FROM t1
|
||||
UNION ALL
|
||||
SELECT * FROM (SELECT x, y FROM t2 ORDER BY y)
|
||||
LIMIT 4 OFFSET 1;
|
||||
} {2 b 3 c 4 d 5 e}
|
||||
do_execsql_test offset1-1.4.5 {
|
||||
SELECT a, b FROM t1
|
||||
UNION ALL
|
||||
SELECT * FROM (SELECT x, y FROM t2 ORDER BY y)
|
||||
LIMIT 5 OFFSET 1;
|
||||
} {2 b 3 c 4 d 5 e 6 w}
|
||||
do_execsql_test offset1-1.4.6 {
|
||||
SELECT a, b FROM t1
|
||||
UNION ALL
|
||||
SELECT * FROM (SELECT x, y FROM t2 ORDER BY y)
|
||||
LIMIT 6 OFFSET 1;
|
||||
} {2 b 3 c 4 d 5 e 6 w 7 x}
|
||||
do_execsql_test offset1-1.4.7 {
|
||||
SELECT a, b FROM t1
|
||||
UNION ALL
|
||||
SELECT * FROM (SELECT x, y FROM t2 ORDER BY y)
|
||||
LIMIT 7 OFFSET 1;
|
||||
} {2 b 3 c 4 d 5 e 6 w 7 x 8 y}
|
||||
do_execsql_test offset1-1.4.8 {
|
||||
SELECT a, b FROM t1
|
||||
UNION ALL
|
||||
SELECT * FROM (SELECT x, y FROM t2 ORDER BY y)
|
||||
LIMIT 8 OFFSET 1;
|
||||
} {2 b 3 c 4 d 5 e 6 w 7 x 8 y 9 z}
|
||||
do_execsql_test offset1-1.4.9 {
|
||||
SELECT a, b FROM t1
|
||||
UNION ALL
|
||||
SELECT * FROM (SELECT x, y FROM t2 ORDER BY y)
|
||||
LIMIT 9 OFFSET 1;
|
||||
} {2 b 3 c 4 d 5 e 6 w 7 x 8 y 9 z}
|
||||
|
||||
|
||||
|
||||
finish_test
|
@@ -76,40 +76,42 @@ do_execsql_test 1.5 {
|
||||
|
||||
do_execsql_test 1.6 {
|
||||
SELECT word, distance, matchlen FROM demo
|
||||
WHERE word MATCH 'amstedam*' AND distance <= 100;
|
||||
WHERE word MATCH 'amstedam*' AND distance <= 100
|
||||
ORDER BY distance, word;
|
||||
} {
|
||||
amsterdam 100 9 amsterdamh 100 9
|
||||
amsterdamm 100 9 amsterdamn 100 9
|
||||
amsterdama 100 9 amsterdame 100 9
|
||||
amsterdami 100 9 amsterdamo 100 9
|
||||
amsterdamu 100 9 amsterdamy 100 9
|
||||
amsterdammetje 100 9 amsterdamania 100 9
|
||||
amsterdamb 100 9 amsterdamf 100 9
|
||||
amsterdamp 100 9 amsterdamv 100 9
|
||||
amsterdamw 100 9 amsterdamweg 100 9
|
||||
amsterdamc 100 9 amsterdamg 100 9
|
||||
amsterdamj 100 9 amsterdamk 100 9
|
||||
amsterdamq 100 9 amsterdams 100 9
|
||||
amsterdamx 100 9 amsterdamz 100 9
|
||||
amsterdamsestraat 100 9 amsterdamd 100 9
|
||||
amsterdamt 100 9 amsterdaml 100 9
|
||||
amsterdamlaan 100 9 amsterdamr 100 9
|
||||
amsterdam 100 9 amsterdama 100 9
|
||||
amsterdamania 100 9 amsterdamb 100 9
|
||||
amsterdamc 100 9 amsterdamd 100 9
|
||||
amsterdame 100 9 amsterdamf 100 9
|
||||
amsterdamg 100 9 amsterdamh 100 9
|
||||
amsterdami 100 9 amsterdamj 100 9
|
||||
amsterdamk 100 9 amsterdaml 100 9
|
||||
amsterdamlaan 100 9 amsterdamm 100 9
|
||||
amsterdammetje 100 9 amsterdamn 100 9
|
||||
amsterdamo 100 9 amsterdamp 100 9
|
||||
amsterdamq 100 9 amsterdamr 100 9
|
||||
amsterdams 100 9 amsterdamsestraat 100 9
|
||||
amsterdamt 100 9 amsterdamu 100 9
|
||||
amsterdamv 100 9 amsterdamw 100 9
|
||||
amsterdamweg 100 9 amsterdamx 100 9
|
||||
amsterdamy 100 9 amsterdamz 100 9
|
||||
}
|
||||
|
||||
do_execsql_test 1.7 {
|
||||
SELECT word, distance, matchlen FROM demo
|
||||
WHERE word MATCH 'amstedam*' AND distance <= 100 AND top=20;
|
||||
WHERE word MATCH 'amstedam*' AND distance <= 100 AND top=20
|
||||
ORDER BY distance, word;
|
||||
} {
|
||||
amsterdam 100 9 amsterdamh 100 9
|
||||
amsterdamm 100 9 amsterdamn 100 9
|
||||
amsterdama 100 9 amsterdame 100 9
|
||||
amsterdami 100 9 amsterdamo 100 9
|
||||
amsterdamu 100 9 amsterdamy 100 9
|
||||
amsterdammetje 100 9 amsterdamania 100 9
|
||||
amsterdamb 100 9 amsterdamf 100 9
|
||||
amsterdamp 100 9 amsterdamv 100 9
|
||||
amsterdamw 100 9 amsterdamweg 100 9
|
||||
amsterdamc 100 9 amsterdamg 100 9
|
||||
amsterdam 100 9 amsterdama 100 9
|
||||
amsterdamania 100 9 amsterdamb 100 9
|
||||
amsterdamc 100 9 amsterdame 100 9
|
||||
amsterdamf 100 9 amsterdamg 100 9
|
||||
amsterdamh 100 9 amsterdami 100 9
|
||||
amsterdamm 100 9 amsterdammetje 100 9
|
||||
amsterdamn 100 9 amsterdamo 100 9
|
||||
amsterdamp 100 9 amsterdamu 100 9
|
||||
amsterdamv 100 9 amsterdamw 100 9
|
||||
amsterdamweg 100 9 amsterdamy 100 9
|
||||
}
|
||||
|
||||
|
||||
|
@@ -63,6 +63,14 @@ foreach {tn uri file} {
|
||||
#
|
||||
if {$tn>14} break
|
||||
|
||||
#
|
||||
# NOTE: When running on Tcl 8.6 (or higher?) on Windows, a colon within
|
||||
# the file name no longer tries to access an alternate data stream
|
||||
# (ADS) named "test.db" for the "http" file, causing some spurious
|
||||
# failures of this test.
|
||||
#
|
||||
if {$tn==12 && $::tcl_version>=8.6} continue
|
||||
|
||||
#
|
||||
# NOTE: On Windows, we need to account for the fact that the current
|
||||
# directory does not start with a forward slash.
|
||||
|
@@ -1,44 +0,0 @@
|
||||
/*
|
||||
** A utility for printing the differences between two SQLite database files.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <ctype.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
|
||||
#define PAGESIZE 1024
|
||||
static int db1 = -1;
|
||||
static int db2 = -1;
|
||||
|
||||
int main(int argc, char **argv){
|
||||
int iPg;
|
||||
unsigned char a1[PAGESIZE], a2[PAGESIZE];
|
||||
if( argc!=3 ){
|
||||
fprintf(stderr,"Usage: %s FILENAME FILENAME\n", argv[0]);
|
||||
exit(1);
|
||||
}
|
||||
db1 = open(argv[1], O_RDONLY);
|
||||
if( db1<0 ){
|
||||
fprintf(stderr,"%s: can't open %s\n", argv[0], argv[1]);
|
||||
exit(1);
|
||||
}
|
||||
db2 = open(argv[2], O_RDONLY);
|
||||
if( db2<0 ){
|
||||
fprintf(stderr,"%s: can't open %s\n", argv[0], argv[2]);
|
||||
exit(1);
|
||||
}
|
||||
iPg = 1;
|
||||
while( read(db1, a1, PAGESIZE)==PAGESIZE && read(db2,a2,PAGESIZE)==PAGESIZE ){
|
||||
if( memcmp(a1,a2,PAGESIZE) ){
|
||||
printf("Page %d\n", iPg);
|
||||
}
|
||||
iPg++;
|
||||
}
|
||||
printf("%d pages checked\n", iPg-1);
|
||||
close(db1);
|
||||
close(db2);
|
||||
}
|
@@ -1,23 +0,0 @@
|
||||
#
|
||||
# Extract opcode documentation for sqliteVdbe.c and generate HTML
|
||||
#
|
||||
BEGIN {
|
||||
print "<html><body bgcolor=white>"
|
||||
print "<h1>SQLite Virtual Database Engine Opcodes</h1>"
|
||||
print "<table>"
|
||||
}
|
||||
/ Opcode: /,/\*\// {
|
||||
if( $2=="Opcode:" ){
|
||||
printf "<tr><td>%s %s %s %s</td>\n<td>\n", $3, $4, $5, $6
|
||||
}else if( $1=="*/" ){
|
||||
printf "</td></tr>\n"
|
||||
}else if( NF>1 ){
|
||||
sub(/^ *\*\* /,"")
|
||||
gsub(/</,"<")
|
||||
gsub(/&/,"&")
|
||||
print
|
||||
}
|
||||
}
|
||||
END {
|
||||
print "</table></body></html>"
|
||||
}
|
@@ -1,111 +0,0 @@
|
||||
# Run this TCL script using "testfixture" in order get a report that shows
|
||||
# how much disk space is used by a particular data to actually store data
|
||||
# versus how much space is unused.
|
||||
#
|
||||
|
||||
# Get the name of the database to analyze
|
||||
#
|
||||
if {[llength $argv]!=1} {
|
||||
puts stderr "Usage: $argv0 database-name"
|
||||
exit 1
|
||||
}
|
||||
set file_to_analyze [lindex $argv 0]
|
||||
|
||||
# Open the database
|
||||
#
|
||||
sqlite db [lindex $argv 0]
|
||||
set DB [btree_open [lindex $argv 0]]
|
||||
|
||||
# Output the schema for the generated report
|
||||
#
|
||||
puts \
|
||||
{BEGIN;
|
||||
CREATE TABLE space_used(
|
||||
name clob, -- Name of a table or index in the database file
|
||||
is_index boolean, -- TRUE if it is an index, false for a table
|
||||
payload int, -- Total amount of data stored in this table or index
|
||||
pri_pages int, -- Number of primary pages used
|
||||
ovfl_pages int, -- Number of overflow pages used
|
||||
pri_unused int, -- Number of unused bytes on primary pages
|
||||
ovfl_unused int -- Number of unused bytes on overflow pages
|
||||
);}
|
||||
|
||||
# This query will be used to find the root page number for every index and
|
||||
# table in the database.
|
||||
#
|
||||
set sql {
|
||||
SELECT name, type, rootpage FROM sqlite_master
|
||||
UNION ALL
|
||||
SELECT 'sqlite_master', 'table', 2
|
||||
ORDER BY 1
|
||||
}
|
||||
|
||||
# Initialize variables used for summary statistics.
|
||||
#
|
||||
set total_size 0
|
||||
set total_primary 0
|
||||
set total_overflow 0
|
||||
set total_unused_primary 0
|
||||
set total_unused_ovfl 0
|
||||
|
||||
# Analyze every table in the database, one at a time.
|
||||
#
|
||||
foreach {name type rootpage} [db eval $sql] {
|
||||
set cursor [btree_cursor $DB $rootpage 0]
|
||||
set go [btree_first $cursor]
|
||||
set size 0
|
||||
catch {unset pg_used}
|
||||
set unused_ovfl 0
|
||||
set n_overflow 0
|
||||
while {$go==0} {
|
||||
set payload [btree_payload_size $cursor]
|
||||
incr size $payload
|
||||
set stat [btree_cursor_dump $cursor]
|
||||
set pgno [lindex $stat 0]
|
||||
set freebytes [lindex $stat 4]
|
||||
set pg_used($pgno) $freebytes
|
||||
if {$payload>238} {
|
||||
set n [expr {($payload-238+1019)/1020}]
|
||||
incr n_overflow $n
|
||||
incr unused_ovfl [expr {$n*1020+238-$payload}]
|
||||
}
|
||||
set go [btree_next $cursor]
|
||||
}
|
||||
btree_close_cursor $cursor
|
||||
set n_primary [llength [array names pg_used]]
|
||||
set unused_primary 0
|
||||
foreach x [array names pg_used] {incr unused_primary $pg_used($x)}
|
||||
regsub -all ' $name '' name
|
||||
puts -nonewline "INSERT INTO space_used VALUES('$name'"
|
||||
puts -nonewline ",[expr {$type=="index"}]"
|
||||
puts ",$size,$n_primary,$n_overflow,$unused_primary,$unused_ovfl);"
|
||||
incr total_size $size
|
||||
incr total_primary $n_primary
|
||||
incr total_overflow $n_overflow
|
||||
incr total_unused_primary $unused_primary
|
||||
incr total_unused_ovfl $unused_ovfl
|
||||
}
|
||||
|
||||
# Output summary statistics:
|
||||
#
|
||||
puts "-- Total payload size: $total_size"
|
||||
puts "-- Total pages used: $total_primary primary and $total_overflow overflow"
|
||||
set file_pgcnt [expr {[file size [lindex $argv 0]]/1024}]
|
||||
puts -nonewline "-- Total unused bytes on primary pages: $total_unused_primary"
|
||||
if {$total_primary>0} {
|
||||
set upp [expr {$total_unused_primary/$total_primary}]
|
||||
puts " (avg $upp bytes/page)"
|
||||
} else {
|
||||
puts ""
|
||||
}
|
||||
puts -nonewline "-- Total unused bytes on overflow pages: $total_unused_ovfl"
|
||||
if {$total_overflow>0} {
|
||||
set upp [expr {$total_unused_ovfl/$total_overflow}]
|
||||
puts " (avg $upp bytes/page)"
|
||||
} else {
|
||||
puts ""
|
||||
}
|
||||
set n_free [expr {$file_pgcnt-$total_primary-$total_overflow}]
|
||||
if {$n_free>0} {incr n_free -1}
|
||||
puts "-- Total pages on freelist: $n_free"
|
||||
puts "COMMIT;"
|
Reference in New Issue
Block a user