Add support for prefix queries to fts5.
FossilOrigin-Name: 75ebd3cd5904a4f89f7f3a9b25d32b2a42a31310
This commit is contained in:
@@ -96,6 +96,33 @@ void sqlite3Fts5BufferAppendPrintf(int *, Fts5Buffer*, char *zFmt, ...);
|
||||
#define fts5BufferAppendBlob(a,b,c,d) sqlite3Fts5BufferAppendBlob(a,b,c,d)
|
||||
#define fts5BufferSet(a,b,c,d) sqlite3Fts5BufferSet(a,b,c,d)
|
||||
|
||||
typedef struct Fts5PoslistReader Fts5PoslistReader;
|
||||
struct Fts5PoslistReader {
|
||||
/* Variables used only by sqlite3Fts5PoslistIterXXX() functions. */
|
||||
int iCol; /* If (iCol>=0), this column only */
|
||||
const u8 *a; /* Position list to iterate through */
|
||||
int n; /* Size of buffer at a[] in bytes */
|
||||
int i; /* Current offset in a[] */
|
||||
|
||||
/* Output variables */
|
||||
int bEof; /* Set to true at EOF */
|
||||
i64 iPos; /* (iCol<<32) + iPos */
|
||||
};
|
||||
int sqlite3Fts5PoslistReaderInit(
|
||||
int iCol, /* If (iCol>=0), this column only */
|
||||
const u8 *a, int n, /* Poslist buffer to iterate through */
|
||||
Fts5PoslistReader *pIter /* Iterator object to initialize */
|
||||
);
|
||||
int sqlite3Fts5PoslistReaderNext(Fts5PoslistReader*);
|
||||
|
||||
typedef struct Fts5PoslistWriter Fts5PoslistWriter;
|
||||
struct Fts5PoslistWriter {
|
||||
int iCol;
|
||||
int iOff;
|
||||
};
|
||||
int sqlite3Fts5PoslistWriterAppend(Fts5Buffer*, Fts5PoslistWriter*, i64);
|
||||
|
||||
|
||||
/*
|
||||
** End of interface to code in fts5_buffer.c.
|
||||
**************************************************************************/
|
||||
|
||||
@@ -137,3 +137,63 @@ void sqlite3Fts5BufferSet(
|
||||
pBuf->n = 0;
|
||||
sqlite3Fts5BufferAppendBlob(pRc, pBuf, nData, pData);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Advance the iterator object passed as the only argument. Return true
|
||||
** if the iterator reaches EOF, or false otherwise.
|
||||
*/
|
||||
int sqlite3Fts5PoslistReaderNext(Fts5PoslistReader *pIter){
|
||||
if( pIter->i>=pIter->n ){
|
||||
pIter->bEof = 1;
|
||||
}else{
|
||||
int iVal;
|
||||
pIter->i += getVarint32(&pIter->a[pIter->i], iVal);
|
||||
if( iVal==1 ){
|
||||
pIter->i += getVarint32(&pIter->a[pIter->i], iVal);
|
||||
if( pIter->iCol>=0 && iVal>pIter->iCol ){
|
||||
pIter->bEof = 1;
|
||||
}else{
|
||||
pIter->iPos = ((u64)iVal << 32);
|
||||
pIter->i += getVarint32(&pIter->a[pIter->i], iVal);
|
||||
}
|
||||
}
|
||||
pIter->iPos += (iVal-2);
|
||||
}
|
||||
return pIter->bEof;
|
||||
}
|
||||
|
||||
int sqlite3Fts5PoslistReaderInit(
|
||||
int iCol, /* If (iCol>=0), this column only */
|
||||
const u8 *a, int n, /* Poslist buffer to iterate through */
|
||||
Fts5PoslistReader *pIter /* Iterator object to initialize */
|
||||
){
|
||||
memset(pIter, 0, sizeof(*pIter));
|
||||
pIter->a = a;
|
||||
pIter->n = n;
|
||||
pIter->iCol = iCol;
|
||||
do {
|
||||
sqlite3Fts5PoslistReaderNext(pIter);
|
||||
}while( pIter->bEof==0 && (pIter->iPos >> 32)<iCol );
|
||||
return pIter->bEof;
|
||||
}
|
||||
|
||||
int sqlite3Fts5PoslistWriterAppend(
|
||||
Fts5Buffer *pBuf,
|
||||
Fts5PoslistWriter *pWriter,
|
||||
i64 iPos
|
||||
){
|
||||
int rc = SQLITE_OK;
|
||||
int iCol = (int)(iPos >> 32);
|
||||
int iOff = (iPos & 0x7FFFFFFF);
|
||||
|
||||
if( iCol!=pWriter->iCol ){
|
||||
fts5BufferAppendVarint(&rc, pBuf, 1);
|
||||
fts5BufferAppendVarint(&rc, pBuf, iCol);
|
||||
pWriter->iCol = iCol;
|
||||
pWriter->iOff = 0;
|
||||
}
|
||||
fts5BufferAppendVarint(&rc, pBuf, (iOff - pWriter->iOff) + 2);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
+16
-93
@@ -95,83 +95,6 @@ struct Fts5Parse {
|
||||
Fts5ExprNode *pExpr; /* Result of a successful parse */
|
||||
};
|
||||
|
||||
/*************************************************************************
|
||||
*/
|
||||
typedef struct Fts5PoslistIter Fts5PoslistIter;
|
||||
struct Fts5PoslistIter {
|
||||
int iCol; /* If (iCol>=0), this column only */
|
||||
const u8 *a; /* Position list to iterate through */
|
||||
int n; /* Size of buffer at a[] in bytes */
|
||||
int i; /* Current offset in a[] */
|
||||
|
||||
/* Output variables */
|
||||
int bEof; /* Set to true at EOF */
|
||||
i64 iPos; /* (iCol<<32) + iPos */
|
||||
};
|
||||
|
||||
static int fts5PoslistIterNext(Fts5PoslistIter *pIter){
|
||||
if( pIter->i>=pIter->n ){
|
||||
pIter->bEof = 1;
|
||||
}else{
|
||||
int iVal;
|
||||
pIter->i += getVarint32(&pIter->a[pIter->i], iVal);
|
||||
if( iVal==1 ){
|
||||
pIter->i += getVarint32(&pIter->a[pIter->i], iVal);
|
||||
if( pIter->iCol>=0 && iVal>pIter->iCol ){
|
||||
pIter->bEof = 1;
|
||||
}else{
|
||||
pIter->iPos = ((u64)iVal << 32);
|
||||
pIter->i += getVarint32(&pIter->a[pIter->i], iVal);
|
||||
}
|
||||
}
|
||||
pIter->iPos += (iVal-2);
|
||||
}
|
||||
return pIter->bEof;
|
||||
}
|
||||
|
||||
static int fts5PoslistIterInit(
|
||||
int iCol, /* If (iCol>=0), this column only */
|
||||
const u8 *a, int n, /* Poslist buffer to iterate through */
|
||||
Fts5PoslistIter *pIter /* Iterator object to initialize */
|
||||
){
|
||||
memset(pIter, 0, sizeof(*pIter));
|
||||
pIter->a = a;
|
||||
pIter->n = n;
|
||||
pIter->iCol = iCol;
|
||||
do {
|
||||
fts5PoslistIterNext(pIter);
|
||||
}while( pIter->bEof==0 && (pIter->iPos >> 32)<iCol );
|
||||
return pIter->bEof;
|
||||
}
|
||||
|
||||
typedef struct Fts5PoslistWriter Fts5PoslistWriter;
|
||||
struct Fts5PoslistWriter {
|
||||
int iCol;
|
||||
int iOff;
|
||||
};
|
||||
|
||||
static int fts5PoslistWriterAppend(
|
||||
Fts5Buffer *pBuf,
|
||||
Fts5PoslistWriter *pWriter,
|
||||
i64 iPos
|
||||
){
|
||||
int rc = SQLITE_OK;
|
||||
int iCol = (int)(iPos >> 32);
|
||||
int iOff = (iPos & 0x7FFFFFFF);
|
||||
|
||||
if( iCol!=pWriter->iCol ){
|
||||
fts5BufferAppendVarint(&rc, pBuf, 1);
|
||||
fts5BufferAppendVarint(&rc, pBuf, iCol);
|
||||
pWriter->iCol = iCol;
|
||||
pWriter->iOff = 0;
|
||||
}
|
||||
fts5BufferAppendVarint(&rc, pBuf, (iOff - pWriter->iOff) + 2);
|
||||
|
||||
return rc;
|
||||
}
|
||||
/*
|
||||
*************************************************************************/
|
||||
|
||||
void sqlite3Fts5ParseError(Fts5Parse *pParse, const char *zFmt, ...){
|
||||
if( pParse->rc==SQLITE_OK ){
|
||||
va_list ap;
|
||||
@@ -335,8 +258,8 @@ static int fts5ExprPhraseIsMatch(
|
||||
int *pbMatch /* OUT: Set to true if really a match */
|
||||
){
|
||||
Fts5PoslistWriter writer = {0, 0};
|
||||
Fts5PoslistIter aStatic[4];
|
||||
Fts5PoslistIter *aIter = aStatic;
|
||||
Fts5PoslistReader aStatic[4];
|
||||
Fts5PoslistReader *aIter = aStatic;
|
||||
int i;
|
||||
int rc = SQLITE_OK;
|
||||
|
||||
@@ -345,8 +268,8 @@ static int fts5ExprPhraseIsMatch(
|
||||
/* If the aStatic[] array is not large enough, allocate a large array
|
||||
** using sqlite3_malloc(). This approach could be improved upon. */
|
||||
if( pPhrase->nTerm>(sizeof(aStatic) / sizeof(aStatic[0])) ){
|
||||
int nByte = sizeof(Fts5PoslistIter) * pPhrase->nTerm;
|
||||
aIter = (Fts5PoslistIter*)sqlite3_malloc(nByte);
|
||||
int nByte = sizeof(Fts5PoslistReader) * pPhrase->nTerm;
|
||||
aIter = (Fts5PoslistReader*)sqlite3_malloc(nByte);
|
||||
if( !aIter ) return SQLITE_NOMEM;
|
||||
}
|
||||
|
||||
@@ -354,7 +277,7 @@ static int fts5ExprPhraseIsMatch(
|
||||
for(i=0; i<pPhrase->nTerm; i++){
|
||||
int n;
|
||||
const u8 *a = sqlite3Fts5IterPoslist(pPhrase->aTerm[i].pIter, &n);
|
||||
if( fts5PoslistIterInit(iCol, a, n, &aIter[i]) ) goto ismatch_out;
|
||||
if( sqlite3Fts5PoslistReaderInit(iCol, a, n, &aIter[i]) ) goto ismatch_out;
|
||||
}
|
||||
|
||||
while( 1 ){
|
||||
@@ -363,12 +286,12 @@ static int fts5ExprPhraseIsMatch(
|
||||
do {
|
||||
bMatch = 1;
|
||||
for(i=0; i<pPhrase->nTerm; i++){
|
||||
Fts5PoslistIter *pPos = &aIter[i];
|
||||
Fts5PoslistReader *pPos = &aIter[i];
|
||||
i64 iAdj = iPos + i;
|
||||
if( pPos->iPos!=iAdj ){
|
||||
bMatch = 0;
|
||||
while( pPos->iPos<iAdj ){
|
||||
if( fts5PoslistIterNext(pPos) ) goto ismatch_out;
|
||||
if( sqlite3Fts5PoslistReaderNext(pPos) ) goto ismatch_out;
|
||||
}
|
||||
if( pPos->iPos>iAdj ) iPos = pPos->iPos-i;
|
||||
}
|
||||
@@ -376,11 +299,11 @@ static int fts5ExprPhraseIsMatch(
|
||||
}while( bMatch==0 );
|
||||
|
||||
/* Append position iPos to the output */
|
||||
rc = fts5PoslistWriterAppend(&pPhrase->poslist, &writer, iPos);
|
||||
rc = sqlite3Fts5PoslistWriterAppend(&pPhrase->poslist, &writer, iPos);
|
||||
if( rc!=SQLITE_OK ) goto ismatch_out;
|
||||
|
||||
for(i=0; i<pPhrase->nTerm; i++){
|
||||
if( fts5PoslistIterNext(&aIter[i]) ) goto ismatch_out;
|
||||
if( sqlite3Fts5PoslistReaderNext(&aIter[i]) ) goto ismatch_out;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -408,8 +331,8 @@ static int fts5ExprPhraseIsMatch(
|
||||
** a set of intances that collectively matches the NEAR constraint.
|
||||
*/
|
||||
static int fts5ExprNearIsMatch(Fts5ExprNearset *pNear, int *pbMatch){
|
||||
Fts5PoslistIter aStatic[4];
|
||||
Fts5PoslistIter *aIter = aStatic;
|
||||
Fts5PoslistReader aStatic[4];
|
||||
Fts5PoslistReader *aIter = aStatic;
|
||||
int i;
|
||||
int rc = SQLITE_OK;
|
||||
int bMatch;
|
||||
@@ -420,27 +343,27 @@ static int fts5ExprNearIsMatch(Fts5ExprNearset *pNear, int *pbMatch){
|
||||
/* If the aStatic[] array is not large enough, allocate a large array
|
||||
** using sqlite3_malloc(). This approach could be improved upon. */
|
||||
if( pNear->nPhrase>(sizeof(aStatic) / sizeof(aStatic[0])) ){
|
||||
int nByte = sizeof(Fts5PoslistIter) * pNear->nPhrase;
|
||||
aIter = (Fts5PoslistIter*)sqlite3_malloc(nByte);
|
||||
int nByte = sizeof(Fts5PoslistReader) * pNear->nPhrase;
|
||||
aIter = (Fts5PoslistReader*)sqlite3_malloc(nByte);
|
||||
if( !aIter ) return SQLITE_NOMEM;
|
||||
}
|
||||
|
||||
/* Initialize a term iterator for each phrase */
|
||||
for(i=0; i<pNear->nPhrase; i++){
|
||||
Fts5Buffer *pPoslist = &pNear->apPhrase[i]->poslist;
|
||||
fts5PoslistIterInit(-1, pPoslist->p, pPoslist->n, &aIter[i]);
|
||||
sqlite3Fts5PoslistReaderInit(-1, pPoslist->p, pPoslist->n, &aIter[i]);
|
||||
}
|
||||
|
||||
iMax = aIter[0].iPos;
|
||||
do {
|
||||
bMatch = 1;
|
||||
for(i=0; i<pNear->nPhrase; i++){
|
||||
Fts5PoslistIter *pPos = &aIter[i];
|
||||
Fts5PoslistReader *pPos = &aIter[i];
|
||||
i64 iMin = iMax - pNear->apPhrase[i]->nTerm - pNear->nNear;
|
||||
if( pPos->iPos<iMin || pPos->iPos>iMax ){
|
||||
bMatch = 0;
|
||||
while( pPos->iPos<iMin ){
|
||||
if( fts5PoslistIterNext(pPos) ) goto ismatch_out;
|
||||
if( sqlite3Fts5PoslistReaderNext(pPos) ) goto ismatch_out;
|
||||
}
|
||||
if( pPos->iPos>iMax ) iMax = pPos->iPos;
|
||||
}
|
||||
|
||||
+388
-131
@@ -263,6 +263,7 @@ typedef struct Fts5PendingDoclist Fts5PendingDoclist;
|
||||
typedef struct Fts5PendingPoslist Fts5PendingPoslist;
|
||||
typedef struct Fts5PosIter Fts5PosIter;
|
||||
typedef struct Fts5SegIter Fts5SegIter;
|
||||
typedef struct Fts5DoclistIter Fts5DoclistIter;
|
||||
typedef struct Fts5SegWriter Fts5SegWriter;
|
||||
typedef struct Fts5Structure Fts5Structure;
|
||||
typedef struct Fts5StructureLevel Fts5StructureLevel;
|
||||
@@ -297,10 +298,22 @@ struct Fts5Index {
|
||||
sqlite3_stmt *pDeleter; /* "DELETE FROM %_data ... id>=? AND id<=?" */
|
||||
};
|
||||
|
||||
struct Fts5DoclistIter {
|
||||
u8 *a;
|
||||
int n;
|
||||
int i;
|
||||
|
||||
/* Output variables. aPoslist==0 at EOF */
|
||||
i64 iRowid;
|
||||
u8 *aPoslist;
|
||||
int nPoslist;
|
||||
};
|
||||
|
||||
struct Fts5IndexIter {
|
||||
Fts5Index *pIndex;
|
||||
Fts5Structure *pStruct;
|
||||
Fts5MultiSegIter *pMulti;
|
||||
Fts5DoclistIter *pDoclist;
|
||||
Fts5Buffer poslist; /* Buffer containing current poslist */
|
||||
};
|
||||
|
||||
@@ -424,14 +437,17 @@ struct Fts5MultiSegIter {
|
||||
** Leaf page number containing the last term read from the segment. And
|
||||
** the offset immediately following the term data.
|
||||
**
|
||||
** bOneTerm:
|
||||
** If true, set the iterator to point to EOF after the current doclist has
|
||||
** been exhausted. Do not proceed to the next term in the segment.
|
||||
** flags:
|
||||
** Mask of FTS5_SEGITER_XXX values. Interpreted as follows:
|
||||
**
|
||||
** FTS5_SEGITER_ONETERM:
|
||||
** If set, set the iterator to point to EOF after the current doclist
|
||||
** has been exhausted. Do not proceed to the next term in the segment.
|
||||
*/
|
||||
struct Fts5SegIter {
|
||||
Fts5StructureSegment *pSeg; /* Segment to iterate through */
|
||||
int iIdx; /* Byte offset within current leaf */
|
||||
int bOneTerm; /* If true, iterate through single doclist */
|
||||
int flags; /* Mask of configuration flags */
|
||||
int iLeafPgno; /* Current leaf page number */
|
||||
Fts5Data *pLeaf; /* Current leaf data */
|
||||
int iLeafOffset; /* Byte offset within current leaf */
|
||||
@@ -444,6 +460,9 @@ struct Fts5SegIter {
|
||||
i64 iRowid; /* Current rowid */
|
||||
};
|
||||
|
||||
#define FTS5_SEGITER_ONETERM 0x01
|
||||
|
||||
|
||||
/*
|
||||
** Object for iterating through paginated data.
|
||||
*/
|
||||
@@ -458,7 +477,7 @@ struct Fts5ChunkIter {
|
||||
};
|
||||
|
||||
/*
|
||||
** Object for iterating through a single position list.
|
||||
** Object for iterating through a single position list on disk.
|
||||
*/
|
||||
struct Fts5PosIter {
|
||||
Fts5ChunkIter chunk; /* Current chunk of data */
|
||||
@@ -566,6 +585,17 @@ static int fts5BufferCompareBlob(
|
||||
return (res==0 ? (pLeft->n - nRight) : res);
|
||||
}
|
||||
|
||||
#if 0
|
||||
static int fts5CompareBlob(
|
||||
const u8 *pLeft, int nLeft,
|
||||
const u8 *pRight, int nRight
|
||||
){
|
||||
int nCmp = MIN(nLeft, nRight);
|
||||
int res = memcmp(pLeft, pRight, nCmp);
|
||||
return (res==0 ? (nLeft - nRight) : res);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Compare the contents of the two buffers using memcmp(). If one buffer
|
||||
** is a prefix of the other, it is considered the lesser.
|
||||
@@ -669,6 +699,7 @@ static void fts5DataBuffer(Fts5Index *p, Fts5Buffer *pBuf, i64 iRowid){
|
||||
*/
|
||||
static void fts5DataRelease(Fts5Data *pData){
|
||||
if( pData ){
|
||||
assert( pData->nRef>0 );
|
||||
pData->nRef--;
|
||||
if( pData->nRef==0 ) sqlite3_free(pData);
|
||||
}
|
||||
@@ -1045,95 +1076,6 @@ static void fts5SegIterInit(
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Initialize the object pIter to point to term pTerm/nTerm within segment
|
||||
** pSeg, index iIdx. If there is no such term in the index, the iterator
|
||||
** is set to EOF.
|
||||
**
|
||||
** If an error occurs, Fts5Index.rc is set to an appropriate error code. If
|
||||
** an error has already occurred when this function is called, it is a no-op.
|
||||
*/
|
||||
static void fts5SegIterSeekInit(
|
||||
Fts5Index *p, /* FTS5 backend */
|
||||
int iIdx, /* Config.aHash[] index of FTS index */
|
||||
const u8 *pTerm, int nTerm, /* Term to seek to */
|
||||
Fts5StructureSegment *pSeg, /* Description of segment */
|
||||
Fts5SegIter *pIter /* Object to populate */
|
||||
){
|
||||
int iPg = 1;
|
||||
int h;
|
||||
|
||||
assert( pTerm && nTerm );
|
||||
memset(pIter, 0, sizeof(*pIter));
|
||||
pIter->pSeg = pSeg;
|
||||
pIter->iIdx = iIdx;
|
||||
pIter->bOneTerm = 1;
|
||||
|
||||
for(h=pSeg->nHeight-1; h>0; h--){
|
||||
Fts5NodeIter node; /* For iterating through internal nodes */
|
||||
i64 iRowid = FTS5_SEGMENT_ROWID(iIdx, pSeg->iSegid, h, iPg);
|
||||
Fts5Data *pNode = fts5DataRead(p, iRowid);
|
||||
if( pNode==0 ) break;
|
||||
|
||||
fts5NodeIterInit(pNode->p, pNode->n, &node);
|
||||
assert( node.term.n==0 );
|
||||
|
||||
iPg = node.iChild;
|
||||
for(fts5NodeIterNext(&p->rc, &node);
|
||||
node.aData && fts5BufferCompareBlob(&node.term, pTerm, nTerm)<=0;
|
||||
fts5NodeIterNext(&p->rc, &node)
|
||||
){
|
||||
iPg = node.iChild;
|
||||
}
|
||||
fts5NodeIterFree(&node);
|
||||
fts5DataRelease(pNode);
|
||||
}
|
||||
|
||||
if( iPg>=pSeg->pgnoFirst ){
|
||||
int res;
|
||||
pIter->iLeafPgno = iPg - 1;
|
||||
fts5SegIterNextPage(p, pIter);
|
||||
if( pIter->pLeaf ){
|
||||
u8 *a = pIter->pLeaf->p;
|
||||
int n = pIter->pLeaf->n;
|
||||
|
||||
pIter->iLeafOffset = fts5GetU16(&a[2]);
|
||||
fts5SegIterLoadTerm(p, pIter, 0);
|
||||
|
||||
while( (res = fts5BufferCompareBlob(&pIter->term, pTerm, nTerm)) ){
|
||||
if( res<0 && pIter->iLeafPgno==iPg ){
|
||||
int iOff = pIter->iLeafOffset;
|
||||
while( iOff<n ){
|
||||
int nPos;
|
||||
iOff += getVarint32(&a[iOff], nPos);
|
||||
iOff += nPos;
|
||||
if( iOff<n ){
|
||||
u64 rowid;
|
||||
iOff += getVarint(&a[iOff], &rowid);
|
||||
if( rowid==0 ) break;
|
||||
}
|
||||
}
|
||||
|
||||
/* If the iterator is not yet at the end of the next page, load
|
||||
** the next term and jump to the next iteration of the while()
|
||||
** loop. */
|
||||
if( iOff<n ){
|
||||
int nKeep;
|
||||
pIter->iLeafOffset = iOff + getVarint32(&a[iOff], nKeep);
|
||||
fts5SegIterLoadTerm(p, pIter, nKeep);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
/* No matching term on this page. Set the iterator to EOF. */
|
||||
fts5DataRelease(pIter->pLeaf);
|
||||
pIter->pLeaf = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Advance iterator pIter to the next entry.
|
||||
**
|
||||
@@ -1198,7 +1140,7 @@ static void fts5SegIterNext(
|
||||
|
||||
/* Check if the iterator is now at EOF. If so, return early. */
|
||||
if( pIter->pLeaf && bNewTerm ){
|
||||
if( pIter->bOneTerm ){
|
||||
if( pIter->flags & FTS5_SEGITER_ONETERM ){
|
||||
fts5DataRelease(pIter->pLeaf);
|
||||
pIter->pLeaf = 0;
|
||||
}else{
|
||||
@@ -1208,6 +1150,79 @@ static void fts5SegIterNext(
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Initialize the object pIter to point to term pTerm/nTerm within segment
|
||||
** pSeg, index iIdx. If there is no such term in the index, the iterator
|
||||
** is set to EOF.
|
||||
**
|
||||
** If an error occurs, Fts5Index.rc is set to an appropriate error code. If
|
||||
** an error has already occurred when this function is called, it is a no-op.
|
||||
*/
|
||||
static void fts5SegIterSeekInit(
|
||||
Fts5Index *p, /* FTS5 backend */
|
||||
int iIdx, /* Config.aHash[] index of FTS index */
|
||||
const u8 *pTerm, int nTerm, /* Term to seek to */
|
||||
int bGe, /* If true seek for >=. If false, == */
|
||||
Fts5StructureSegment *pSeg, /* Description of segment */
|
||||
Fts5SegIter *pIter /* Object to populate */
|
||||
){
|
||||
int iPg = 1;
|
||||
int h;
|
||||
|
||||
assert( pTerm && nTerm );
|
||||
memset(pIter, 0, sizeof(*pIter));
|
||||
pIter->pSeg = pSeg;
|
||||
pIter->iIdx = iIdx;
|
||||
|
||||
/* This block sets stack variable iPg to the leaf page number that may
|
||||
** contain term (pTerm/nTerm), if it is present in the segment. */
|
||||
for(h=pSeg->nHeight-1; h>0; h--){
|
||||
Fts5NodeIter node; /* For iterating through internal nodes */
|
||||
i64 iRowid = FTS5_SEGMENT_ROWID(iIdx, pSeg->iSegid, h, iPg);
|
||||
Fts5Data *pNode = fts5DataRead(p, iRowid);
|
||||
if( pNode==0 ) break;
|
||||
|
||||
fts5NodeIterInit(pNode->p, pNode->n, &node);
|
||||
assert( node.term.n==0 );
|
||||
|
||||
iPg = node.iChild;
|
||||
for(fts5NodeIterNext(&p->rc, &node);
|
||||
node.aData && fts5BufferCompareBlob(&node.term, pTerm, nTerm)<=0;
|
||||
fts5NodeIterNext(&p->rc, &node)
|
||||
){
|
||||
iPg = node.iChild;
|
||||
}
|
||||
fts5NodeIterFree(&node);
|
||||
fts5DataRelease(pNode);
|
||||
}
|
||||
|
||||
if( iPg<pSeg->pgnoFirst ){
|
||||
iPg = pSeg->pgnoFirst;
|
||||
}
|
||||
|
||||
pIter->iLeafPgno = iPg - 1;
|
||||
fts5SegIterNextPage(p, pIter);
|
||||
|
||||
if( pIter->pLeaf ){
|
||||
int res;
|
||||
pIter->iLeafOffset = fts5GetU16(&pIter->pLeaf->p[2]);
|
||||
fts5SegIterLoadTerm(p, pIter, 0);
|
||||
do {
|
||||
res = fts5BufferCompareBlob(&pIter->term, pTerm, nTerm);
|
||||
if( res>=0 ) break;
|
||||
fts5SegIterNext(p, pIter);
|
||||
}while( pIter->pLeaf );
|
||||
|
||||
if( bGe==0 && res ){
|
||||
/* Set iterator to point to EOF */
|
||||
fts5DataRelease(pIter->pLeaf);
|
||||
pIter->pLeaf = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if( bGe==0 ) pIter->flags |= FTS5_SEGITER_ONETERM;
|
||||
}
|
||||
|
||||
/*
|
||||
** Zero the iterator passed as the only argument.
|
||||
*/
|
||||
@@ -1327,6 +1342,7 @@ static void fts5MultiIterNew(
|
||||
Fts5Index *p, /* FTS5 backend to iterate within */
|
||||
Fts5Structure *pStruct, /* Structure of specific index */
|
||||
int iIdx, /* Config.aHash[] index of FTS index */
|
||||
int bGe, /* True for >= */
|
||||
const u8 *pTerm, int nTerm, /* Term to seek to (or NULL/0) */
|
||||
int iLevel, /* Level to iterate (-1 for all) */
|
||||
int nSegment, /* Number of segments to merge (iLevel>=0) */
|
||||
@@ -1363,11 +1379,12 @@ static void fts5MultiIterNew(
|
||||
Fts5StructureLevel *pEnd = &pStruct->aLevel[pStruct->nLevel];
|
||||
for(pLvl=&pStruct->aLevel[0]; pLvl<pEnd; pLvl++){
|
||||
for(iSeg=pLvl->nSeg-1; iSeg>=0; iSeg--){
|
||||
Fts5StructureSegment *pSeg = &pLvl->aSeg[iSeg];
|
||||
Fts5SegIter *pIter = &pNew->aSeg[iIter++];
|
||||
if( pTerm==0 ){
|
||||
fts5SegIterInit(p, iIdx, &pLvl->aSeg[iSeg], pIter);
|
||||
fts5SegIterInit(p, iIdx, pSeg, pIter);
|
||||
}else{
|
||||
fts5SegIterSeekInit(p, iIdx, pTerm, nTerm, &pLvl->aSeg[iSeg], pIter);
|
||||
fts5SegIterSeekInit(p, iIdx, pTerm, nTerm, bGe, pSeg, pIter);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2245,7 +2262,7 @@ fprintf(stdout, "merging %d segments from level %d!", nInput, iLvl);
|
||||
fflush(stdout);
|
||||
#endif
|
||||
|
||||
for(fts5MultiIterNew(p, pStruct, iIdx, 0, 0, iLvl, nInput, &pIter);
|
||||
for(fts5MultiIterNew(p, pStruct, iIdx, 0, 0, 0, iLvl, nInput, &pIter);
|
||||
fts5MultiIterEof(p, pIter)==0;
|
||||
fts5MultiIterNext(p, pIter)
|
||||
){
|
||||
@@ -2740,7 +2757,7 @@ int sqlite3Fts5IndexIntegrityCheck(Fts5Index *p, u64 cksum){
|
||||
for(iIdx=0; iIdx<=pConfig->nPrefix; iIdx++){
|
||||
Fts5MultiSegIter *pIter;
|
||||
Fts5Structure *pStruct = fts5StructureRead(p, iIdx);
|
||||
for(fts5MultiIterNew(p, pStruct, iIdx, 0, 0, -1, 0, &pIter);
|
||||
for(fts5MultiIterNew(p, pStruct, iIdx, 0, 0, 0, -1, 0, &pIter);
|
||||
fts5MultiIterEof(p, pIter)==0;
|
||||
fts5MultiIterNext(p, pIter)
|
||||
){
|
||||
@@ -3022,6 +3039,232 @@ void sqlite3Fts5IndexPgsz(Fts5Index *p, int pgsz){
|
||||
p->pgsz = pgsz;
|
||||
}
|
||||
|
||||
/*
|
||||
** Iterator pMulti currently points to a valid entry (not EOF). This
|
||||
** function appends a copy of the position-list of the entry pMulti
|
||||
** currently points to to buffer pBuf.
|
||||
**
|
||||
** 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(
|
||||
Fts5Index *p,
|
||||
Fts5MultiSegIter *pMulti,
|
||||
int bSz,
|
||||
Fts5Buffer *pBuf
|
||||
){
|
||||
Fts5ChunkIter iter;
|
||||
Fts5SegIter *pSeg = &pMulti->aSeg[ pMulti->aFirst[1] ];
|
||||
assert( fts5MultiIterEof(p, pMulti)==0 );
|
||||
fts5ChunkIterInit(p, pSeg, &iter);
|
||||
if( fts5ChunkIterEof(p, &iter)==0 ){
|
||||
if( bSz ){
|
||||
fts5BufferAppendVarint(&p->rc, pBuf, iter.nRem);
|
||||
}
|
||||
while( fts5ChunkIterEof(p, &iter)==0 ){
|
||||
fts5BufferAppendBlob(&p->rc, pBuf, iter.n, iter.p);
|
||||
fts5ChunkIterNext(p, &iter);
|
||||
}
|
||||
}
|
||||
fts5ChunkIterRelease(&iter);
|
||||
}
|
||||
|
||||
static void fts5DoclistIterNext(Fts5DoclistIter *pIter){
|
||||
if( pIter->i<pIter->n ){
|
||||
if( pIter->i ){
|
||||
i64 iDelta;
|
||||
pIter->i += getVarint(&pIter->a[pIter->i], (u64*)&iDelta);
|
||||
pIter->iRowid -= iDelta;
|
||||
}else{
|
||||
pIter->i += getVarint(&pIter->a[pIter->i], (u64*)&pIter->iRowid);
|
||||
}
|
||||
pIter->i += getVarint32(&pIter->a[pIter->i], pIter->nPoslist);
|
||||
pIter->aPoslist = &pIter->a[pIter->i];
|
||||
pIter->i += pIter->nPoslist;
|
||||
}else{
|
||||
pIter->aPoslist = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void fts5DoclistIterInit(Fts5Buffer *pBuf, Fts5DoclistIter *pIter){
|
||||
memset(pIter, 0, sizeof(*pIter));
|
||||
pIter->a = pBuf->p;
|
||||
pIter->n = pBuf->n;
|
||||
fts5DoclistIterNext(pIter);
|
||||
}
|
||||
|
||||
/*
|
||||
** Append a doclist to buffer pBuf.
|
||||
*/
|
||||
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, *piLastRowid - iRowid);
|
||||
}
|
||||
*piLastRowid = iRowid;
|
||||
}
|
||||
|
||||
/*
|
||||
** Buffers p1 and p2 contain doclists. This function merges the content
|
||||
** of the two doclists together and sets buffer p1 to the result before
|
||||
** returning.
|
||||
**
|
||||
** If an error occurs, an error code is left in p->rc. If an error has
|
||||
** already occurred, this function is a no-op.
|
||||
*/
|
||||
static void fts5MergePrefixLists(
|
||||
Fts5Index *p, /* FTS5 backend object */
|
||||
Fts5Buffer *p1, /* First list to merge */
|
||||
Fts5Buffer *p2 /* Second list to merge */
|
||||
){
|
||||
if( p2->n ){
|
||||
i64 iLastRowid = 0;
|
||||
Fts5DoclistIter i1;
|
||||
Fts5DoclistIter i2;
|
||||
Fts5Buffer out;
|
||||
Fts5Buffer tmp;
|
||||
memset(&out, 0, sizeof(out));
|
||||
memset(&tmp, 0, sizeof(tmp));
|
||||
|
||||
fts5DoclistIterInit(p1, &i1);
|
||||
fts5DoclistIterInit(p2, &i2);
|
||||
while( 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);
|
||||
fts5BufferAppendVarint(&p->rc, &out, i1.nPoslist);
|
||||
fts5BufferAppendBlob(&p->rc, &out, i1.nPoslist, i1.aPoslist);
|
||||
fts5DoclistIterNext(&i1);
|
||||
}
|
||||
else if( i1.aPoslist==0 || i2.iRowid>i1.iRowid ){
|
||||
/* Copy entry from i2 */
|
||||
fts5MergeAppendDocid(&p->rc, &out, &iLastRowid, i2.iRowid);
|
||||
fts5BufferAppendVarint(&p->rc, &out, i2.nPoslist);
|
||||
fts5BufferAppendBlob(&p->rc, &out, i2.nPoslist, i2.aPoslist);
|
||||
fts5DoclistIterNext(&i2);
|
||||
}
|
||||
else{
|
||||
Fts5PoslistReader r1;
|
||||
Fts5PoslistReader r2;
|
||||
Fts5PoslistWriter writer;
|
||||
|
||||
memset(&writer, 0, sizeof(writer));
|
||||
|
||||
/* Merge the two position lists. */
|
||||
fts5MergeAppendDocid(&p->rc, &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) ){
|
||||
i64 iNew;
|
||||
if( r2.bEof || (r1.bEof==0 && r1.iPos<r2.iPos) ){
|
||||
iNew = r1.iPos;
|
||||
sqlite3Fts5PoslistReaderNext(&r1);
|
||||
}else{
|
||||
iNew = r2.iPos;
|
||||
sqlite3Fts5PoslistReaderNext(&r2);
|
||||
if( r1.iPos==r2.iPos ) sqlite3Fts5PoslistReaderNext(&r1);
|
||||
}
|
||||
p->rc = sqlite3Fts5PoslistWriterAppend(&tmp, &writer, iNew);
|
||||
}
|
||||
|
||||
fts5BufferAppendVarint(&p->rc, &out, tmp.n);
|
||||
fts5BufferAppendBlob(&p->rc, &out, tmp.n, tmp.p);
|
||||
fts5DoclistIterNext(&i1);
|
||||
fts5DoclistIterNext(&i2);
|
||||
}
|
||||
}
|
||||
|
||||
fts5BufferSet(&p->rc, p1, out.n, out.p);
|
||||
fts5BufferFree(&tmp);
|
||||
fts5BufferFree(&out);
|
||||
}
|
||||
}
|
||||
|
||||
static void fts5BufferSwap(Fts5Buffer *p1, Fts5Buffer *p2){
|
||||
Fts5Buffer tmp = *p1;
|
||||
*p1 = *p2;
|
||||
*p2 = tmp;
|
||||
}
|
||||
|
||||
static void fts5SetupPrefixIter(
|
||||
Fts5Index *p, /* Index to read from */
|
||||
const u8 *pToken, /* Buffer containing prefix to match */
|
||||
int nToken, /* Size of buffer pToken in bytes */
|
||||
Fts5IndexIter *pIter /* Populate this object */
|
||||
){
|
||||
Fts5Structure *pStruct;
|
||||
Fts5Buffer *aBuf;
|
||||
const int nBuf = 32;
|
||||
|
||||
|
||||
aBuf = (Fts5Buffer*)fts5IdxMalloc(p, sizeof(Fts5Buffer)*nBuf);
|
||||
pStruct = fts5StructureRead(p, 0);
|
||||
|
||||
if( aBuf && pStruct ){
|
||||
Fts5DoclistIter *pDoclist;
|
||||
int i;
|
||||
i64 iLastRowid;
|
||||
Fts5MultiSegIter *p1 = 0; /* Iterator used to gather data from index */
|
||||
Fts5Buffer doclist;
|
||||
|
||||
memset(&doclist, 0, sizeof(doclist));
|
||||
for(fts5MultiIterNew(p, pStruct, 0, 1, pToken, nToken, -1, 0, &p1);
|
||||
fts5MultiIterEof(p, p1)==0;
|
||||
fts5MultiIterNext(p, p1)
|
||||
){
|
||||
i64 iRowid = fts5MultiIterRowid(p1);
|
||||
int nTerm;
|
||||
const u8 *pTerm = fts5MultiIterTerm(p1, &nTerm);
|
||||
assert( memcmp(pToken, pTerm, MIN(nToken, nTerm))<=0 );
|
||||
if( nTerm<nToken || memcmp(pToken, pTerm, nToken) ) break;
|
||||
|
||||
if( doclist.n>0 && iRowid>=iLastRowid ){
|
||||
for(i=0; doclist.n && p->rc==SQLITE_OK; i++){
|
||||
assert( i<nBuf );
|
||||
if( aBuf[i].n==0 ){
|
||||
fts5BufferSwap(&doclist, &aBuf[i]);
|
||||
fts5BufferZero(&doclist);
|
||||
}else{
|
||||
fts5MergePrefixLists(p, &doclist, &aBuf[i]);
|
||||
fts5BufferZero(&aBuf[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
if( doclist.n==0 ){
|
||||
fts5BufferAppendVarint(&p->rc, &doclist, iRowid);
|
||||
}else{
|
||||
fts5BufferAppendVarint(&p->rc, &doclist, iLastRowid - iRowid);
|
||||
}
|
||||
iLastRowid = iRowid;
|
||||
fts5MultiIterPoslist(p, p1, 1, &doclist);
|
||||
}
|
||||
|
||||
for(i=0; i<nBuf; i++){
|
||||
fts5MergePrefixLists(p, &doclist, &aBuf[i]);
|
||||
fts5BufferFree(&aBuf[i]);
|
||||
}
|
||||
fts5MultiIterFree(p, p1);
|
||||
|
||||
pDoclist = (Fts5DoclistIter*)fts5IdxMalloc(p, sizeof(Fts5DoclistIter));
|
||||
if( !pDoclist ){
|
||||
fts5BufferFree(&doclist);
|
||||
}else{
|
||||
pIter->pDoclist = pDoclist;
|
||||
fts5DoclistIterInit(&doclist, pIter->pDoclist);
|
||||
}
|
||||
}
|
||||
|
||||
fts5StructureRelease(pStruct);
|
||||
sqlite3_free(aBuf);
|
||||
}
|
||||
|
||||
/*
|
||||
** Open a new iterator to iterate though all docids that match the
|
||||
** specified token or token prefix.
|
||||
@@ -3040,21 +3283,25 @@ Fts5IndexIter *sqlite3Fts5IndexQuery(
|
||||
if( pConfig->aPrefix[iIdx-1]==nToken ) break;
|
||||
}
|
||||
if( iIdx>pConfig->nPrefix ){
|
||||
/* No matching prefix index. todo: deal with this. */
|
||||
assert( 0 );
|
||||
iIdx = -1;
|
||||
}
|
||||
}
|
||||
|
||||
pRet = (Fts5IndexIter*)sqlite3_malloc(sizeof(Fts5IndexIter));
|
||||
if( pRet ){
|
||||
memset(pRet, 0, sizeof(Fts5IndexIter));
|
||||
pRet->pStruct = fts5StructureRead(p, 0);
|
||||
if( pRet->pStruct ){
|
||||
fts5MultiIterNew(p,
|
||||
pRet->pStruct, iIdx, (const u8*)pToken, nToken, -1, 0, &pRet->pMulti
|
||||
);
|
||||
}
|
||||
|
||||
pRet->pIndex = p;
|
||||
if( iIdx>=0 ){
|
||||
pRet->pStruct = fts5StructureRead(p, iIdx);
|
||||
if( pRet->pStruct ){
|
||||
fts5MultiIterNew(p, pRet->pStruct,
|
||||
iIdx, 0, (const u8*)pToken, nToken, -1, 0, &pRet->pMulti
|
||||
);
|
||||
}
|
||||
}else{
|
||||
fts5SetupPrefixIter(p, (const u8*)pToken, nToken, pRet);
|
||||
}
|
||||
}
|
||||
|
||||
if( p->rc ){
|
||||
@@ -3068,24 +3315,37 @@ Fts5IndexIter *sqlite3Fts5IndexQuery(
|
||||
** Return true if the iterator passed as the only argument is at EOF.
|
||||
*/
|
||||
int sqlite3Fts5IterEof(Fts5IndexIter *pIter){
|
||||
return fts5MultiIterEof(pIter->pIndex, pIter->pMulti);
|
||||
if( pIter->pDoclist ){
|
||||
return pIter->pDoclist->aPoslist==0;
|
||||
}else{
|
||||
return fts5MultiIterEof(pIter->pIndex, pIter->pMulti);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Move to the next matching rowid.
|
||||
*/
|
||||
void sqlite3Fts5IterNext(Fts5IndexIter *pIter, i64 iMatch){
|
||||
fts5BufferZero(&pIter->poslist);
|
||||
fts5MultiIterNext(pIter->pIndex, pIter->pMulti);
|
||||
if( pIter->pDoclist ){
|
||||
fts5DoclistIterNext(pIter->pDoclist);
|
||||
}else{
|
||||
fts5BufferZero(&pIter->poslist);
|
||||
fts5MultiIterNext(pIter->pIndex, pIter->pMulti);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Return the current rowid.
|
||||
*/
|
||||
i64 sqlite3Fts5IterRowid(Fts5IndexIter *pIter){
|
||||
return fts5MultiIterRowid(pIter->pMulti);
|
||||
if( pIter->pDoclist ){
|
||||
return pIter->pDoclist->iRowid;
|
||||
}else{
|
||||
return fts5MultiIterRowid(pIter->pMulti);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Return a pointer to a buffer containing a copy of the position list for
|
||||
** the current entry. Output variable *pn is set to the size of the buffer
|
||||
@@ -3095,25 +3355,17 @@ i64 sqlite3Fts5IterRowid(Fts5IndexIter *pIter){
|
||||
** disk.
|
||||
*/
|
||||
const u8 *sqlite3Fts5IterPoslist(Fts5IndexIter *pIter, int *pn){
|
||||
Fts5ChunkIter iter;
|
||||
Fts5Index *p = pIter->pIndex;
|
||||
Fts5SegIter *pSeg = &pIter->pMulti->aSeg[ pIter->pMulti->aFirst[1] ];
|
||||
|
||||
assert( sqlite3Fts5IterEof(pIter)==0 );
|
||||
fts5ChunkIterInit(p, pSeg, &iter);
|
||||
if( fts5ChunkIterEof(p, &iter)==0 ){
|
||||
if( pIter->pDoclist ){
|
||||
*pn = pIter->pDoclist->nPoslist;
|
||||
return pIter->pDoclist->aPoslist;
|
||||
}else{
|
||||
Fts5Index *p = pIter->pIndex;
|
||||
fts5BufferZero(&pIter->poslist);
|
||||
fts5BufferGrow(&p->rc, &pIter->poslist, iter.nRem);
|
||||
while( fts5ChunkIterEof(p, &iter)==0 ){
|
||||
fts5BufferAppendBlob(&p->rc, &pIter->poslist, iter.n, iter.p);
|
||||
fts5ChunkIterNext(p, &iter);
|
||||
}
|
||||
fts5MultiIterPoslist(p, pIter->pMulti, 0, &pIter->poslist);
|
||||
if( p->rc ) return 0;
|
||||
*pn = pIter->poslist.n;
|
||||
return pIter->poslist.p;
|
||||
}
|
||||
fts5ChunkIterRelease(&iter);
|
||||
|
||||
if( p->rc ) return 0;
|
||||
*pn = pIter->poslist.n;
|
||||
return pIter->poslist.p;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -3121,10 +3373,15 @@ const u8 *sqlite3Fts5IterPoslist(Fts5IndexIter *pIter, int *pn){
|
||||
*/
|
||||
void sqlite3Fts5IterClose(Fts5IndexIter *pIter){
|
||||
if( pIter ){
|
||||
fts5MultiIterFree(pIter->pIndex, pIter->pMulti);
|
||||
fts5StructureRelease(pIter->pStruct);
|
||||
if( pIter->pDoclist ){
|
||||
sqlite3_free(pIter->pDoclist->a);
|
||||
sqlite3_free(pIter->pDoclist);
|
||||
}else{
|
||||
fts5MultiIterFree(pIter->pIndex, pIter->pMulti);
|
||||
fts5StructureRelease(pIter->pStruct);
|
||||
fts5BufferFree(&pIter->poslist);
|
||||
}
|
||||
fts5CloseReader(pIter->pIndex);
|
||||
fts5BufferFree(&pIter->poslist);
|
||||
sqlite3_free(pIter);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
C Add\ssupport\sfor\sAND,\sOR\sand\sNOT\sto\sfts5.
|
||||
D 2014-07-05T15:15:41.850
|
||||
C Add\ssupport\sfor\sprefix\squeries\sto\sfts5.
|
||||
D 2014-07-08T16:27:37.120
|
||||
F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
|
||||
F Makefile.in b03432313a3aad96c706f8164fb9f5307eaf19f5
|
||||
F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
|
||||
@@ -104,11 +104,11 @@ F ext/fts3/unicode/CaseFolding.txt 8c678ca52ecc95e16bc7afc2dbf6fc9ffa05db8c
|
||||
F ext/fts3/unicode/UnicodeData.txt cd07314edb62d49fde34debdaf92fa2aa69011e7
|
||||
F ext/fts3/unicode/mkunicode.tcl dc6f268eb526710e2c6e496c372471d773d0c368
|
||||
F ext/fts5/fts5.c 1af3184dd9c0e5c1686f71202d6b6cac8f225f05
|
||||
F ext/fts5/fts5Int.h b7a684ff3508ab24437886f8bc873a16f494a7db
|
||||
F ext/fts5/fts5_buffer.c f1a26a79e2943fe4388e531fa141941b5eb6d31a
|
||||
F ext/fts5/fts5Int.h bb716a6e6a376a7c8211e55e5577c6c020d176c2
|
||||
F ext/fts5/fts5_buffer.c 83b463a179ad4348fa87796fce78b0e4ef6b898a
|
||||
F ext/fts5/fts5_config.c 94f1b4cb4de6a7cd5780c14adb0198e289df8cef
|
||||
F ext/fts5/fts5_expr.c a341fe4f6d49875a7aeaa443036a3dc6aa2bff52
|
||||
F ext/fts5/fts5_index.c d8ab9712e38dc1beb9a9145ec89e18dc083c0467
|
||||
F ext/fts5/fts5_expr.c 21351cdd256f8e561a57a38490d27f7922247696
|
||||
F ext/fts5/fts5_index.c a3084168a384a9d43f7fb045511b386ccb6e55e8
|
||||
F ext/fts5/fts5_storage.c 7848d8f8528d798bba159900ea310a6d4a279da8
|
||||
F ext/icu/README.txt d9fbbad0c2f647c3fdf715fc9fd64af53aedfc43
|
||||
F ext/icu/icu.c d415ccf984defeb9df2c0e1afcfaa2f6dc05eacb
|
||||
@@ -595,6 +595,7 @@ F test/fts4unicode.test 01ec3fe2a7c3cfff3b4c0581b83caa11b33efa36
|
||||
F test/fts5aa.test c8d3b9694f6b2864161c7437408464a535d19343
|
||||
F test/fts5ab.test 4db86a9473ee2a8c2cb30e0d81df21c6022f99b6
|
||||
F test/fts5ac.test d3aeb7a079d40093b34ac8053fc5e4c0ed7e88dc
|
||||
F test/fts5ad.test a4d2f344c86a45ee53b424512585b3900ccb8cf3
|
||||
F test/fts5ea.test ff43b40f8879ba50b82def70f2ab67c195d1a1d4
|
||||
F test/full.test 6b3c8fb43c6beab6b95438c1675374b95fab245d
|
||||
F test/func.test ae97561957aba6ca9e3a7b8a13aac41830d701ef
|
||||
@@ -1190,7 +1191,7 @@ F tool/vdbe_profile.tcl 67746953071a9f8f2f668b73fe899074e2c6d8c1
|
||||
F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4
|
||||
F tool/warnings.sh 0abfd78ceb09b7f7c27c688c8e3fe93268a13b32
|
||||
F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f
|
||||
P 004667106e552e832a564b77e242b86f183d4441
|
||||
R 4ab8db5e873de873b3febadf2e9942c6
|
||||
P 8682b87e794767cefcaa080fd53c8973c24c556a
|
||||
R dba83c3d230dbf413439715289f715cf
|
||||
U dan
|
||||
Z fcdcd2fcf8de98f33f86410b0c7d6d38
|
||||
Z 876bab147b2ac69a43a21b2ef49df211
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
8682b87e794767cefcaa080fd53c8973c24c556a
|
||||
75ebd3cd5904a4f89f7f3a9b25d32b2a42a31310
|
||||
@@ -0,0 +1,196 @@
|
||||
# 2014 June 17
|
||||
#
|
||||
# The author disclaims copyright to this source code. In place of
|
||||
# a legal notice, here is a blessing:
|
||||
#
|
||||
# May you do good and not evil.
|
||||
# May you find forgiveness for yourself and forgive others.
|
||||
# May you share freely, never taking more than you give.
|
||||
#
|
||||
#*************************************************************************
|
||||
# This file implements regression tests for SQLite library. The
|
||||
# focus of this script is testing the FTS5 module.
|
||||
#
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
set testprefix fts5ad
|
||||
|
||||
# If SQLITE_ENABLE_FTS3 is defined, omit this file.
|
||||
ifcapable !fts3 {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
do_execsql_test 1.0 {
|
||||
CREATE VIRTUAL TABLE yy USING fts5(x, y);
|
||||
INSERT INTO yy VALUES('Changes the result to be', 'the list of all matching');
|
||||
INSERT INTO yy VALUES('indices (or all matching', 'values if -inline is');
|
||||
INSERT INTO yy VALUES('specified as well.) If', 'indices are returned, the');
|
||||
} {}
|
||||
|
||||
foreach {tn match res} {
|
||||
1 {c*} {1}
|
||||
2 {i*} {3 2}
|
||||
3 {t*} {3 1}
|
||||
4 {r*} {3 1}
|
||||
} {
|
||||
do_execsql_test 1.$tn {
|
||||
SELECT rowid FROM yy WHERE yy MATCH $match
|
||||
} $res
|
||||
}
|
||||
|
||||
foreach {T create} {
|
||||
2 {
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(a, b);
|
||||
INSERT INTO t1(t1) VALUES('pgsz=32');
|
||||
}
|
||||
|
||||
3 {
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(a, b, prefix=1,2,3,4,5);
|
||||
INSERT INTO t1(t1) VALUES('pgsz=32');
|
||||
}
|
||||
|
||||
} {
|
||||
|
||||
do_test $T.1 {
|
||||
execsql { DROP TABLE IF EXISTS t1 }
|
||||
execsql $create
|
||||
} {}
|
||||
|
||||
do_test $T.1 {
|
||||
foreach {rowid a b} {
|
||||
0 {fghij uvwxyz klmn pq uvwx} {klmn f fgh uv fghij klmno}
|
||||
1 {uv f abcd abcd fghi} {pq klm uv uv fgh uv a}
|
||||
2 {klmn klm pqrs fghij uv} {f k uvw ab abcd pqr uv}
|
||||
3 {ab pqrst a fghi ab pqr fg} {k klmno a fg abcd}
|
||||
4 {abcd pqrst uvwx a fgh} {f klmno fghij kl pqrst}
|
||||
5 {uvwxyz k abcde u a} {uv k k kl klmn}
|
||||
6 {uvwxyz k klmn pqrst uv} {fghi pqrs abcde u k}
|
||||
7 {uvwxy klmn u p pqrst fgh} {p f fghi abcd uvw kl uv}
|
||||
8 {f klmno pqrst uvwxy pqrst} {uv abcde klm pq pqr}
|
||||
9 {f abcde a uvwxyz pqrst} {fghij abc k uvwx pqr fghij uvwxy}
|
||||
10 {ab uv f fg pqrst uvwxy} {fgh p uv k abc klm uvw}
|
||||
11 {pq klmno a uvw abcde uvwxyz} {fghij pq uvwxyz pqr fghi}
|
||||
12 {fgh u pq fgh uvw} {uvw pqr f uvwxy uvwx}
|
||||
13 {uvwx klmn f fgh abcd pqr} {uvw k fg uv klm abcd}
|
||||
14 {ab uvwx pqrst pqr uvwxyz pqrs} {uvwxyz abcde ab ab uvw abcde}
|
||||
15 {abc abcde uvwxyz abc kl k pqr} {klm k k klmno u fgh}
|
||||
16 {fghi abcd fghij uv uvwxyz ab uv} {klmn pqr a uvw fghi}
|
||||
17 {abc pqrst fghi uvwx uvw klmn fghi} {ab fg pqr pqrs p}
|
||||
18 {pqr kl a fghij fgh fg kl} {pqr uvwxyz uvw abcd uvwxyz}
|
||||
19 {fghi fghi pqr kl fghi f} {klmn u u klmno klmno}
|
||||
20 {abc pqrst klmno kl pq uvwxy} {abc k fghi pqrs klm}
|
||||
21 {a pqr uvwxyz uv fghi a fgh} {abc pqrs pqrst pq klm}
|
||||
22 {klm abc uvwxyz klm pqrst} {fghij k pq pqr u klm fghij}
|
||||
23 {p klm uv p a a} {uvwxy klmn uvw abcde pq}
|
||||
24 {uv fgh fg pq uvwxy u uvwxy} {pqrs a uvw p uvwx uvwxyz fg}
|
||||
25 {fghij fghi klmn abcd pq kl} {fghi abcde pqrs abcd fgh uvwxy}
|
||||
26 {pq fgh a abc klmno klmn} {fgh p k p fg fghij}
|
||||
27 {fg pq kl uvwx fghij pqrst klmn} {abcd uvw abcd fghij f fghij}
|
||||
28 {uvw fghi p fghij pq fgh uvwx} {k fghij abcd uvwx pqr fghi}
|
||||
29 {klm pq abcd pq f uvwxy} {pqrst p fghij pqr p}
|
||||
30 {ab uvwx fg uvwx klmn klm} {klmn klmno fghij klmn klm}
|
||||
31 {pq k pqr abcd a pqrs} {abcd abcd uvw a abcd klmno ab}
|
||||
32 {pqrst u abc pq klm} {abc kl uvwxyz fghij u fghi p}
|
||||
33 {f uvwxy u k f uvw uvwx} {pqrs uvw fghi fg pqrst klm}
|
||||
34 {pqrs pq fghij uvwxyz pqr} {ab abc abc uvw f pq f}
|
||||
35 {uvwxy ab uvwxy klmno kl pqrs} {abcde uvw pqrs uvwx k k}
|
||||
36 {uvwxyz k ab abcde abc uvw} {uvw abcde uvw klmn uv klmn}
|
||||
37 {k kl uv abcde uvwx fg u} {u abc uvwxy k fg abcd}
|
||||
38 {fghi pqrst fghi pqr pqrst uvwx} {u uv uvwx fghi abcde}
|
||||
39 {k pqrst k uvw fg pqrst fghij} {uvwxy ab kl klmn uvwxyz abcde}
|
||||
40 {fg uvwxy pqrs klmn uvwxyz klm p} {k uv ab fghij fgh k pqrs}
|
||||
41 {uvwx abc f pq uvwxy k} {ab uvwxyz abc f fghij}
|
||||
42 {uvwxy klmno uvwxyz uvwxyz pqrst} {uv kl kl klmno k f abcde}
|
||||
43 {abcde ab pqrs fg f fgh} {abc fghij fghi k k}
|
||||
44 {uvw abcd a ab pqrst klmn fg} {pqrst u uvwx pqrst fghij f pqrst}
|
||||
45 {uvwxy p kl uvwxyz ab pqrst fghi} {abc f pqr fg a k}
|
||||
46 {u p f a fgh} {a kl pq uv f}
|
||||
47 {pqrs abc fghij fg abcde ab a} {p ab uv pqrs kl fghi abcd}
|
||||
48 {abcde uvwxy pqrst uv abc pqr uvwx} {uvwxy klm uvwxy uvwx k}
|
||||
49 {fgh klm abcde klmno u} {a f fghij f uvwxyz abc u}
|
||||
50 {uv uvw uvwxyz uvwxyz uv ab} {uvwx pq fg u k uvwxy}
|
||||
51 {uvwxy pq p kl fghi} {pqrs fghi pqrs abcde uvwxyz ab}
|
||||
52 {pqr p uvwxy kl pqrs klmno fghij} {ab abcde abc pqrst pqrs uv}
|
||||
53 {fgh pqrst p a klmno} {ab ab pqrst pqr kl pqrst}
|
||||
54 {abcd klm ab uvw a fg u} {f pqr f abcd uv}
|
||||
55 {u fg uvwxyz k uvw} {abc pqrs f fghij fg pqrs uvwxy}
|
||||
56 {klm fg p fghi fg a} {uv a fghi uvwxyz a fghi}
|
||||
57 {uvwxy k abcde fgh f fghi} {f kl klmn f fghi klm}
|
||||
58 {klm k fgh uvw fgh fghi} {klmno uvwx u pqrst u}
|
||||
59 {fghi pqr pqrst p uvw fghij} {uv pqrst pqrs pq fghij klm}
|
||||
60 {uvwx klm uvwxy uv klmn} {p a a abc klmn ab k}
|
||||
61 {uvwxy uvwx klm uvwx klm} {pqrs ab ab uvwxyz fg}
|
||||
62 {kl uv uv uvw fg kl k} {abcde uvw fgh uvwxy klm}
|
||||
63 {a abc fgh u klm abcd} {fgh pqr uv klmn fghij}
|
||||
64 {klmn k klmn klmno pqrs pqr} {fg kl abcde klmno uvwxy kl pq}
|
||||
65 {uvwxyz klm fghi abc abcde kl} {uvwxy uvw uvwxyz uvwxyz pq pqrst}
|
||||
66 {pq klm abc pqrst fgh f} {u abcde pqrst abcde fg}
|
||||
67 {u pqrst kl u uvw klmno} {u pqr pqrs fgh u p}
|
||||
68 {abc fghi uvwxy fgh k pq} {uv p uvwx uvwxyz ab}
|
||||
69 {klmno f uvwxyz uvwxy klmn fg ab} {fgh kl a pqr abcd pqr}
|
||||
70 {fghi pqrst pqrst uv a} {uvwxy k p uvw uvwx a}
|
||||
71 {a fghij f p uvw} {klm fg abcd abcde klmno pqrs}
|
||||
72 {uv uvwx uvwx uvw klm} {uv fghi klmno uvwxy uvw}
|
||||
73 {kl uvwxy ab f pq klm u} {uvwxy klmn klm abcd pq fg k}
|
||||
74 {uvw pqrst abcd uvwxyz ab} {fgh fgh klmn abc pq}
|
||||
75 {uvwxyz klm pq abcd klmno pqr uvwxyz} {kl f a fg pqr klmn}
|
||||
76 {uvw uvwxy pqr k pqrst kl} {uvwxy abc uvw uvw u}
|
||||
77 {fgh klm u uvwxyz f uvwxy abcde} {uv abcde klmno u u ab}
|
||||
78 {klmno abc pq pqr fgh} {p uv abcd fgh abc u k}
|
||||
79 {fg pqr uvw pq uvwx} {uv uvw fghij pqrs fg p}
|
||||
80 {abcd pqrs uvwx uvwxy uvwx} {u uvw pqrst pqr abcde pqrs kl}
|
||||
81 {uvwxyz klm pq uvwxy fghij} {p pq klm fghij u a a}
|
||||
82 {uvwx k uvwxyz klmno pqrst kl} {abcde p f pqrst abcd uvwxyz p}
|
||||
83 {abcd abcde klm pqrst uvwxyz} {uvw pqrst u p uvwxyz a pqrs}
|
||||
84 {k klm abc uv uvwxy klm klmn} {k abc pqr a abc p kl}
|
||||
85 {klmn abcd pqrs p pq klm a} {klmn kl ab uvw pq}
|
||||
86 {klmn a pqrs abc uvw pqrst} {a pqr kl klm a k f}
|
||||
87 {pqrs ab uvwx uvwxy a pqr f} {fg klm uvwx pqr pqr}
|
||||
88 {klmno ab k kl u uvwxyz} {uv kl uvw fghi uv uvw}
|
||||
89 {pq fghi pqrst klmn uvwxy abc pqrs} {fg f f fg abc abcde klm}
|
||||
90 {kl a k fghi uvwx fghi u} {ab uvw pqr fg a p abc}
|
||||
91 {uvwx pqrs klmno ab fgh uvwx} {pqr uvwx abc kl f klmno kl}
|
||||
92 {fghij pq pqrs fghij f pqrst} {u abcde fg pq pqr fgh k}
|
||||
93 {fgh u pqrs abcde klmno abc} {abc fg pqrst pqr abcde}
|
||||
94 {uvwx p abc f pqr p} {k pqrs kl klm abc fghi klm}
|
||||
95 {kl p klmno uvwxyz klmn} {fghi ab a fghi pqrs kl}
|
||||
96 {pqr fgh pq uvwx a} {uvw klm klmno fg uvwxy uvwx}
|
||||
97 {fg abc uvwxyz fghi pqrst pq} {abc k a ab abcde f}
|
||||
98 {uvwxy fghi uvwxy u abcde abcde uvw} {klmn uvwx pqrs uvw uvwxy abcde}
|
||||
99 {pq fg fghi uvwx uvwx fghij uvwxy} {klmn klmn f abc fg a}
|
||||
} {
|
||||
execsql {
|
||||
INSERT INTO t1(rowid, a, b) VALUES($rowid, $a, $b);
|
||||
}
|
||||
}
|
||||
} {}
|
||||
|
||||
proc prefix_query {prefix} {
|
||||
set ret [list]
|
||||
db eval {SELECT rowid, a, b FROM t1 ORDER BY rowid DESC} {
|
||||
if {[lsearch -glob $a $prefix]>=0 || [lsearch -glob $b $prefix]>=0} {
|
||||
lappend ret $rowid
|
||||
}
|
||||
}
|
||||
return $ret
|
||||
}
|
||||
|
||||
foreach {tn prefix} {
|
||||
1 {a*} 2 {ab*} 3 {abc*} 4 {abcd*} 5 {abcde*}
|
||||
6 {f*} 7 {fg*} 8 {fgh*} 9 {fghi*} 10 {fghij*}
|
||||
11 {k*} 12 {kl*} 13 {klm*} 14 {klmn*} 15 {klmno*}
|
||||
16 {p*} 17 {pq*} 18 {pqr*} 19 {pqrs*} 20 {pqrst*}
|
||||
21 {u*} 22 {uv*} 23 {uvw*} 24 {uvwx*} 25 {uvwxy*} 26 {uvwxyz*}
|
||||
27 {x*}
|
||||
} {
|
||||
set res [prefix_query $prefix]
|
||||
set n [llength $res]
|
||||
do_execsql_test $T.$tn.$n {SELECT rowid FROM t1 WHERE t1 MATCH $prefix} $res
|
||||
}
|
||||
}
|
||||
|
||||
finish_test
|
||||
|
||||
Reference in New Issue
Block a user