Add support for prefix queries to fts5.

FossilOrigin-Name: 75ebd3cd5904a4f89f7f3a9b25d32b2a42a31310
This commit is contained in:
dan
2014-07-08 16:27:37 +00:00
parent 103576dd75
commit a2b569f955
7 changed files with 698 additions and 234 deletions
+27
View File
@@ -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.
**************************************************************************/
+60
View File
@@ -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
View File
@@ -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
View File
@@ -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);
}
}
+10 -9
View File
@@ -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
View File
@@ -1 +1 @@
8682b87e794767cefcaa080fd53c8973c24c556a
75ebd3cd5904a4f89f7f3a9b25d32b2a42a31310
+196
View File
@@ -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