mirror of https://github.com/sqlite/sqlite
Improvements to the way fts3 reads the full-text index.
FossilOrigin-Name: 45c051e78651d8204c17cecdda2bde705698881f
This commit is contained in:
parent
948a5f88ea
commit
f13b704ee6
239
ext/fts3/fts3.c
239
ext/fts3/fts3.c
|
@ -905,7 +905,7 @@ static int fts3NextMethod(sqlite3_vtab_cursor *pCursor){
|
|||
static int fts3ReadBlock(
|
||||
Fts3Table *p,
|
||||
sqlite3_int64 iBlock,
|
||||
char **pzBlock,
|
||||
char const **pzBlock,
|
||||
int *pnBlock
|
||||
){
|
||||
sqlite3_stmt *pStmt;
|
||||
|
@ -928,27 +928,28 @@ static int fts3ReadBlock(
|
|||
}
|
||||
|
||||
/*
|
||||
** The buffer pointed to by argument zNode (size nNode bytes) contains a
|
||||
** b-tree segment interior node. This function inspects the sub-tree headed
|
||||
** by the node to determine the range of leaf-nodes (if any) that may
|
||||
** contain a term that matches the contents of buffer zTerm (size nTerm
|
||||
** bytes). If the isPrefix parameter is true, then the range of leaves
|
||||
** returned are those that may contain any term for which zTerm/nTerm is
|
||||
** a prefix.
|
||||
** The buffer pointed to by argument zNode (size nNode bytes) contains the
|
||||
** root node of a b-tree segment. The segment is guaranteed to be at least
|
||||
** one level high (i.e. the root node is not also a leaf). If successful,
|
||||
** this function locates the leaf node of the segment that may contain the
|
||||
** term specified by arguments zTerm and nTerm and writes its block number
|
||||
** to *piLeaf.
|
||||
**
|
||||
** If successful, SQLITE_OK is returned. The blockid of the first leaf in the
|
||||
** selected range is written to piStart before returning. The blockid of the
|
||||
** final leaf in the selected range is written to *piEnd.
|
||||
** It is possible that the returned leaf node does not contain the specified
|
||||
** term. However, if the segment does contain said term, it is stored on
|
||||
** the identified leaf node. Because this function only inspects interior
|
||||
** segment nodes (and never loads leaf nodes into memory), it is not possible
|
||||
** to be sure.
|
||||
**
|
||||
** If an error occurs, an error code other than SQLITE_OK is returned.
|
||||
*/
|
||||
static int fts3SelectLeaves(
|
||||
static int fts3SelectLeaf(
|
||||
Fts3Table *p, /* Virtual table handle */
|
||||
const char *zTerm, /* Term to select leaves for */
|
||||
int nTerm, /* Size of term zTerm in bytes */
|
||||
int isPrefix, /* True for a prefix search */
|
||||
const char *zNode, /* Buffer containing segment interior node */
|
||||
int nNode, /* Size of buffer at zNode */
|
||||
sqlite3_int64 *piStart, /* First selected leaf */
|
||||
sqlite3_int64 *piEnd /* Second selected leaf */
|
||||
sqlite3_int64 *piLeaf /* Selected leaf node */
|
||||
){
|
||||
int rc = SQLITE_OK; /* Return code */
|
||||
const char *zCsr = zNode; /* Cursor to iterate through node */
|
||||
|
@ -956,79 +957,67 @@ static int fts3SelectLeaves(
|
|||
char *zBuffer = 0; /* Buffer to load terms into */
|
||||
int nAlloc = 0; /* Size of allocated buffer */
|
||||
|
||||
int iHeight; /* Height of this node in tree */
|
||||
sqlite3_int64 iChild;
|
||||
sqlite3_int64 iStart = 0;
|
||||
sqlite3_int64 iEnd;
|
||||
while( 1 ){
|
||||
int iHeight; /* Height of this node in tree */
|
||||
sqlite3_int64 iChild; /* Block id of child node to descend to */
|
||||
int nBlock; /* Size of child node in bytes */
|
||||
|
||||
zCsr += sqlite3Fts3GetVarint32(zCsr, &iHeight);
|
||||
zCsr += sqlite3Fts3GetVarint(zCsr, &iChild);
|
||||
|
||||
while( zCsr<zEnd ){
|
||||
int nSuffix; /* Size of term suffix */
|
||||
int nPrefix = 0; /* Size of term prefix */
|
||||
int nBuffer; /* Total term size */
|
||||
int nMin; /* Minimum of nBuffer and nTerm */
|
||||
int cmp; /* Result of comparing term and buffer */
|
||||
|
||||
/* Load the next term on the node into zBuffer */
|
||||
if( zBuffer ){
|
||||
zCsr += sqlite3Fts3GetVarint32(zCsr, &nPrefix);
|
||||
}
|
||||
zCsr += sqlite3Fts3GetVarint32(zCsr, &nSuffix);
|
||||
if( nPrefix+nSuffix>nAlloc ){
|
||||
char *zNew;
|
||||
nAlloc = (nPrefix+nSuffix) * 2;
|
||||
zNew = (char *)sqlite3_realloc(zBuffer, nAlloc);
|
||||
if( !zNew ){
|
||||
sqlite3_free(zBuffer);
|
||||
return SQLITE_NOMEM;
|
||||
zCsr += sqlite3Fts3GetVarint32(zCsr, &iHeight);
|
||||
zCsr += sqlite3Fts3GetVarint(zCsr, &iChild);
|
||||
|
||||
while( zCsr<zEnd ){
|
||||
int nSuffix; /* Size of term suffix */
|
||||
int nPrefix = 0; /* Size of term prefix */
|
||||
int nBuffer; /* Total term size */
|
||||
int nMin; /* Minimum of nBuffer and nTerm */
|
||||
|
||||
/* Load the next term on the node into zBuffer */
|
||||
if( zBuffer ){
|
||||
zCsr += sqlite3Fts3GetVarint32(zCsr, &nPrefix);
|
||||
}
|
||||
zBuffer = zNew;
|
||||
}
|
||||
memcpy(&zBuffer[nPrefix], zCsr, nSuffix);
|
||||
nBuffer = nPrefix + nSuffix;
|
||||
zCsr += nSuffix;
|
||||
zCsr += sqlite3Fts3GetVarint32(zCsr, &nSuffix);
|
||||
if( nPrefix+nSuffix>nAlloc ){
|
||||
char *zNew;
|
||||
nAlloc = (nPrefix+nSuffix) * 2;
|
||||
zNew = (char *)sqlite3_realloc(zBuffer, nAlloc);
|
||||
if( !zNew ){
|
||||
sqlite3_free(zBuffer);
|
||||
return SQLITE_NOMEM;
|
||||
}
|
||||
zBuffer = zNew;
|
||||
}
|
||||
memcpy(&zBuffer[nPrefix], zCsr, nSuffix);
|
||||
nBuffer = nPrefix + nSuffix;
|
||||
zCsr += nSuffix;
|
||||
|
||||
/* Compare the term we are searching for with the term just loaded from
|
||||
** the interior node. If the specified term is greater than or equal
|
||||
** to the term from the interior node, then all terms on the sub-tree
|
||||
** headed by node iChild are smaller than zTerm. No need to search
|
||||
** iChild.
|
||||
**
|
||||
** If the interior node term is larger than the specified term, then
|
||||
** the tree headed by iChild may contain the specified term.
|
||||
*/
|
||||
nMin = (nBuffer>nTerm ? nTerm : nBuffer);
|
||||
if( memcmp(zTerm, zBuffer, nMin)<0 ) break;
|
||||
iChild++;
|
||||
};
|
||||
|
||||
/* Compare the term we are searching for with the term just loaded from
|
||||
** the interior node. If variable cmp is greater than or equal to zero,
|
||||
** then all terms on the sub-tree headed by node iChild are smaller than
|
||||
** zTerm. No need to search iChild.
|
||||
**
|
||||
** If variable cmp is less than zero, then the sub-tree headed by
|
||||
/* If (iHeight==1), the children of this interior node are leaves. The
|
||||
** specified term may be present on leaf node iChild.
|
||||
*/
|
||||
nMin = (nBuffer>nTerm ? nTerm : nBuffer);
|
||||
cmp = memcmp(zTerm, zBuffer, nMin);
|
||||
if( isPrefix && cmp==0 && iStart==0 ){
|
||||
iStart = iChild;
|
||||
if( iHeight==1 ){
|
||||
*piLeaf = iChild;
|
||||
break;
|
||||
}
|
||||
if( cmp<0 ) break;
|
||||
iChild++;
|
||||
};
|
||||
iEnd = iChild;
|
||||
if( iStart==0 ) iStart = iChild;
|
||||
sqlite3_free(zBuffer);
|
||||
|
||||
if( iHeight==1 ){
|
||||
if( piEnd ) *piEnd = iEnd;
|
||||
if( piStart ) *piStart = iStart;
|
||||
}else{
|
||||
char *zBlock;
|
||||
int nBlock;
|
||||
if( piEnd ){
|
||||
rc = fts3ReadBlock(p, iEnd, &zBlock, &nBlock);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = fts3SelectLeaves(p,zTerm,nTerm,isPrefix,zBlock,nBlock,0,piEnd);
|
||||
}
|
||||
}
|
||||
if( piStart && rc==SQLITE_OK ){
|
||||
rc = fts3ReadBlock(p, iStart, &zBlock, &nBlock);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = fts3SelectLeaves(p,zTerm,nTerm,isPrefix,zBlock,nBlock,piStart,0);
|
||||
}
|
||||
}
|
||||
/* Descend to interior node iChild. */
|
||||
rc = fts3ReadBlock(p, iChild, &zCsr, &nBlock);
|
||||
if( rc!=SQLITE_OK ) break;
|
||||
zEnd = &zCsr[nBlock];
|
||||
}
|
||||
|
||||
sqlite3_free(zBuffer);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
@ -1409,7 +1398,6 @@ static int fts3DoclistMerge(
|
|||
return SQLITE_NOMEM;
|
||||
}
|
||||
}
|
||||
(mergetype==MERGE_NEAR ? 0 : &p);
|
||||
|
||||
while( p1 && p2 ){
|
||||
if( i1==i2 ){
|
||||
|
@ -1444,58 +1432,54 @@ static int fts3DoclistMerge(
|
|||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** A pointer to an instance of this structure is used as the context
|
||||
** argument to sqlite3Fts3SegReaderIterate()
|
||||
*/
|
||||
typedef struct TermSelect TermSelect;
|
||||
struct TermSelect {
|
||||
char const *zTerm;
|
||||
int nTerm;
|
||||
int isPrefix;
|
||||
int isReqPos;
|
||||
char *aOutput; /* Malloc'd output buffer */
|
||||
int nOutput; /* Size of output in bytes */
|
||||
};
|
||||
|
||||
static int fts3TermSelectCb(
|
||||
Fts3Table *p,
|
||||
void *pContext,
|
||||
Fts3Table *p, /* Virtual table object */
|
||||
void *pContext, /* Pointer to TermSelect structure */
|
||||
char *zTerm,
|
||||
int nTerm,
|
||||
char *aDoclist,
|
||||
int nDoclist
|
||||
){
|
||||
TermSelect *pTS = (TermSelect *)pContext;
|
||||
int nNew = pTS->nOutput + nDoclist;
|
||||
|
||||
if( (pTS->nTerm==nTerm || (pTS->isPrefix && pTS->nTerm<nTerm))
|
||||
&& 0==memcmp(zTerm, pTS->zTerm, pTS->nTerm)
|
||||
){
|
||||
int nNew = pTS->nOutput + nDoclist;
|
||||
char *aNew = sqlite3_malloc(nNew);
|
||||
if( !aNew ){
|
||||
return SQLITE_NOMEM;
|
||||
}
|
||||
|
||||
if( pTS->nOutput==0 ){
|
||||
/* If this is the first term selected, copy the doclist to the output
|
||||
** buffer using memcpy(). TODO: Add a way to transfer control of the
|
||||
** aDoclist buffer from the caller so as to avoid the memcpy().
|
||||
*/
|
||||
memcpy(aNew, aDoclist, nDoclist);
|
||||
}else{
|
||||
/* The output buffer is not empty. Merge doclist aDoclist with the
|
||||
** existing output. This can only happen with prefix-searches (as
|
||||
** searches for exact terms return exactly one doclist).
|
||||
*/
|
||||
int mergetype = (pTS->isReqPos ? MERGE_POS_OR : MERGE_OR);
|
||||
assert( pTS->isPrefix );
|
||||
fts3DoclistMerge(mergetype, 0, 0,
|
||||
aNew, &nNew, pTS->aOutput, pTS->nOutput, aDoclist, nDoclist
|
||||
);
|
||||
}
|
||||
|
||||
sqlite3_free(pTS->aOutput);
|
||||
pTS->aOutput = aNew;
|
||||
pTS->nOutput = nNew;
|
||||
char *aNew = sqlite3_malloc(nNew);
|
||||
if( !aNew ){
|
||||
return SQLITE_NOMEM;
|
||||
}
|
||||
|
||||
if( pTS->nOutput==0 ){
|
||||
/* If this is the first term selected, copy the doclist to the output
|
||||
** buffer using memcpy(). TODO: Add a way to transfer control of the
|
||||
** aDoclist buffer from the caller so as to avoid the memcpy().
|
||||
*/
|
||||
memcpy(aNew, aDoclist, nDoclist);
|
||||
}else{
|
||||
/* The output buffer is not empty. Merge doclist aDoclist with the
|
||||
** existing output. This can only happen with prefix-searches (as
|
||||
** searches for exact terms return exactly one doclist).
|
||||
*/
|
||||
int mergetype = (pTS->isReqPos ? MERGE_POS_OR : MERGE_OR);
|
||||
fts3DoclistMerge(mergetype, 0, 0,
|
||||
aNew, &nNew, pTS->aOutput, pTS->nOutput, aDoclist, nDoclist
|
||||
);
|
||||
}
|
||||
|
||||
sqlite3_free(pTS->aOutput);
|
||||
pTS->aOutput = aNew;
|
||||
pTS->nOutput = nNew;
|
||||
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
|
@ -1522,13 +1506,13 @@ static int fts3TermSelect(
|
|||
){
|
||||
int i;
|
||||
TermSelect tsc;
|
||||
Fts3SegFilter filter; /* Segment term filter configuration */
|
||||
Fts3SegReader **apSegment = 0; /* Array of segments to read data from */
|
||||
int nSegment = 0; /* Size of apSegment array */
|
||||
int nAlloc = 0; /* Allocated size of segment array */
|
||||
int rc; /* Return code */
|
||||
sqlite3_stmt *pStmt; /* SQL statement to scan %_segdir table */
|
||||
int iAge = 0; /* Used to assign ages to segments */
|
||||
int flags;
|
||||
|
||||
/* Loop through the entire %_segdir table. For each segment, create a
|
||||
** Fts3SegReader to iterate through the subset of the segment leaves
|
||||
|
@ -1552,10 +1536,10 @@ static int fts3TermSelect(
|
|||
*/
|
||||
rc = sqlite3Fts3SegReaderNew(p, iAge, 0, 0, 0, zRoot, nRoot, &pNew);
|
||||
}else{
|
||||
sqlite3_int64 i1, i2;
|
||||
rc = fts3SelectLeaves(p, zTerm, nTerm, isPrefix, zRoot, nRoot, &i1, &i2);
|
||||
sqlite3_int64 i1;
|
||||
rc = fts3SelectLeaf(p, zTerm, nTerm, zRoot, nRoot, &i1);
|
||||
if( rc==SQLITE_OK ){
|
||||
assert( i1 && i2 );
|
||||
sqlite3_int64 i2 = sqlite3_column_int64(pStmt, 3);
|
||||
rc = sqlite3Fts3SegReaderNew(p, iAge, i1, i2, 0, 0, 0, &pNew);
|
||||
}
|
||||
}
|
||||
|
@ -1585,16 +1569,17 @@ static int fts3TermSelect(
|
|||
}
|
||||
|
||||
memset(&tsc, 0, sizeof(TermSelect));
|
||||
tsc.zTerm = zTerm;
|
||||
tsc.nTerm = nTerm;
|
||||
tsc.isPrefix = isPrefix;
|
||||
tsc.isReqPos = isReqPos;
|
||||
|
||||
flags = FTS3_SEGMENT_IGNORE_EMPTY
|
||||
filter.flags = FTS3_SEGMENT_IGNORE_EMPTY
|
||||
| (isPrefix ? FTS3_SEGMENT_PREFIX : 0)
|
||||
| (isReqPos ? FTS3_SEGMENT_REQUIRE_POS : 0)
|
||||
| (iColumn<p->nColumn ? FTS3_SEGMENT_COLUMN_FILTER : 0);
|
||||
rc = sqlite3Fts3SegReaderIterate(p, apSegment, nSegment, flags,
|
||||
iColumn, fts3TermSelectCb, (void *)&tsc
|
||||
filter.iCol = iColumn;
|
||||
filter.zTerm = zTerm;
|
||||
filter.nTerm = nTerm;
|
||||
rc = sqlite3Fts3SegReaderIterate(p, apSegment, nSegment, &filter,
|
||||
fts3TermSelectCb, (void *)&tsc
|
||||
);
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
|
|
|
@ -58,6 +58,7 @@ typedef struct Fts3Cursor Fts3Cursor;
|
|||
typedef struct Fts3Expr Fts3Expr;
|
||||
typedef struct Fts3Phrase Fts3Phrase;
|
||||
typedef struct Fts3SegReader Fts3SegReader;
|
||||
typedef struct Fts3SegFilter Fts3SegFilter;
|
||||
|
||||
/*
|
||||
** A connection to a fulltext index is an instance of the following
|
||||
|
@ -123,14 +124,14 @@ struct Fts3Cursor {
|
|||
** of tokens in the string.
|
||||
*/
|
||||
struct Fts3Phrase {
|
||||
int nToken; /* Number of tokens in the phrase */
|
||||
int iColumn; /* Index of column this phrase must match */
|
||||
int isNot; /* Phrase prefixed by unary not (-) operator */
|
||||
int nToken; /* Number of tokens in the phrase */
|
||||
int iColumn; /* Index of column this phrase must match */
|
||||
int isNot; /* Phrase prefixed by unary not (-) operator */
|
||||
struct PhraseToken {
|
||||
char *z; /* Text of the token */
|
||||
int n; /* Number of bytes in buffer pointed to by z */
|
||||
int isPrefix; /* True if token ends in with a "*" character */
|
||||
} aToken[1]; /* One entry for each token in the phrase */
|
||||
char *z; /* Text of the token */
|
||||
int n; /* Number of bytes in buffer pointed to by z */
|
||||
int isPrefix; /* True if token ends in with a "*" character */
|
||||
} aToken[1]; /* One entry for each token in the phrase */
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -178,12 +179,21 @@ int sqlite3Fts3Optimize(Fts3Table *);
|
|||
#define FTS3_SEGMENT_REQUIRE_POS 0x00000001
|
||||
#define FTS3_SEGMENT_IGNORE_EMPTY 0x00000002
|
||||
#define FTS3_SEGMENT_COLUMN_FILTER 0x00000004
|
||||
#define FTS3_SEGMENT_PREFIX 0x00000008
|
||||
|
||||
struct Fts3SegFilter {
|
||||
const char *zTerm;
|
||||
int nTerm;
|
||||
int iCol;
|
||||
int flags;
|
||||
};
|
||||
|
||||
int sqlite3Fts3SegReaderNew(Fts3Table *,int, sqlite3_int64,
|
||||
sqlite3_int64, sqlite3_int64, const char *, int, Fts3SegReader**);
|
||||
void sqlite3Fts3SegReaderFree(Fts3SegReader *);
|
||||
|
||||
int sqlite3Fts3SegReaderIterate(
|
||||
Fts3Table *, Fts3SegReader **, int, int, int,
|
||||
Fts3Table *, Fts3SegReader **, int, Fts3SegFilter *,
|
||||
int (*)(Fts3Table *, void *, char *, int, char *, int), void *
|
||||
);
|
||||
|
||||
|
|
|
@ -873,6 +873,33 @@ static int fts3SegReaderCmp2(Fts3SegReader *pLhs, Fts3SegReader *pRhs){
|
|||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Compare the term that the Fts3SegReader object passed as the first argument
|
||||
** points to with the term specified by arguments zTerm and nTerm.
|
||||
**
|
||||
** If the pSeg iterator is already at EOF, return 0. Otherwise, return
|
||||
** -ve if the pSeg term is less than zTerm/nTerm, 0 if the two terms are
|
||||
** equal, or +ve if the pSeg term is greater than zTerm/nTerm.
|
||||
*/
|
||||
static int fts3SegReaderTermCmp(
|
||||
Fts3SegReader *pSeg, /* Segment reader object */
|
||||
const char *zTerm, /* Term to compare to */
|
||||
int nTerm /* Size of term zTerm in bytes */
|
||||
){
|
||||
int res = 0;
|
||||
if( pSeg->aNode ){
|
||||
if( pSeg->nTerm>nTerm ){
|
||||
res = memcmp(pSeg->zTerm, zTerm, nTerm);
|
||||
}else{
|
||||
res = memcmp(pSeg->zTerm, zTerm, pSeg->nTerm);
|
||||
}
|
||||
if( res==0 ){
|
||||
res = pSeg->nTerm-nTerm;
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/*
|
||||
** Argument apSegment is an array of nSegment elements. It is known that
|
||||
** the final (nSegment-nSuspect) members are already in sorted order
|
||||
|
@ -1427,8 +1454,9 @@ static void fts3ColumnFilter(int iCol, char **ppList, int *pnList){
|
|||
int nList = *pnList;
|
||||
char *pEnd = &pList[nList];
|
||||
int iCurrent = 0;
|
||||
|
||||
char *p = pList;
|
||||
|
||||
assert( iCol>=0 );
|
||||
while( 1 ){
|
||||
char c = 0;
|
||||
while( p<pEnd && (c | *p)&0xFE ) c = *p++ & 0x80;
|
||||
|
@ -1467,8 +1495,7 @@ int sqlite3Fts3SegReaderIterate(
|
|||
Fts3Table *p, /* Virtual table handle */
|
||||
Fts3SegReader **apSegment, /* Array of Fts3SegReader objects */
|
||||
int nSegment, /* Size of apSegment array */
|
||||
int flags, /* Flags mask */
|
||||
int iCol, /* Column to filter for */
|
||||
Fts3SegFilter *pFilter, /* Restrictions on range of iteration */
|
||||
int (*xFunc)(Fts3Table *, void *, char *, int, char *, int), /* Callback */
|
||||
void *pContext /* Callback context (2nd argument) */
|
||||
){
|
||||
|
@ -1477,9 +1504,28 @@ int sqlite3Fts3SegReaderIterate(
|
|||
int nAlloc = 0; /* Allocated size of aBuffer buffer */
|
||||
int rc = SQLITE_OK; /* Return code */
|
||||
|
||||
int isIgnoreEmpty = (flags&FTS3_SEGMENT_IGNORE_EMPTY);
|
||||
int isRequirePos = (flags&FTS3_SEGMENT_REQUIRE_POS);
|
||||
int isColFilter = (flags&FTS3_SEGMENT_COLUMN_FILTER);
|
||||
int isIgnoreEmpty = (pFilter->flags & FTS3_SEGMENT_IGNORE_EMPTY);
|
||||
int isRequirePos = (pFilter->flags & FTS3_SEGMENT_REQUIRE_POS);
|
||||
int isColFilter = (pFilter->flags & FTS3_SEGMENT_COLUMN_FILTER);
|
||||
int isPrefix = (pFilter->flags & FTS3_SEGMENT_PREFIX);
|
||||
|
||||
/* If the Fts3SegFilter defines a specific term (or term prefix) to search
|
||||
** for, then advance each segment iterator until it points to a term of
|
||||
** equal or greater value than the specified term. This prevents many
|
||||
** unnecessary merge/sort operations for the case where single segment
|
||||
** b-tree leaf nodes contain more than one term.
|
||||
*/
|
||||
if( pFilter->zTerm ){
|
||||
int nTerm = pFilter->nTerm;
|
||||
char *zTerm = pFilter->zTerm;
|
||||
for(i=0; i<nSegment; i++){
|
||||
Fts3SegReader *pSeg = apSegment[i];
|
||||
while( fts3SegReaderTermCmp(pSeg, zTerm, nTerm)<0 ){
|
||||
rc = fts3SegReaderNext(pSeg);
|
||||
if( rc!=SQLITE_OK ) goto finished;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fts3SegReaderSort(apSegment, nSegment, nSegment, fts3SegReaderCmp);
|
||||
while( apSegment[0]->aNode ){
|
||||
|
@ -1487,6 +1533,22 @@ int sqlite3Fts3SegReaderIterate(
|
|||
char *zTerm = apSegment[0]->zTerm;
|
||||
int nMerge = 1;
|
||||
|
||||
/* If this is a prefix-search, and if the term that apSegment[0] points
|
||||
** to does not share a suffix with pFilter->zTerm/nTerm, then all
|
||||
** required callbacks have been made. In this case exit early.
|
||||
**
|
||||
** Similarly, if this is a search for an exact match, and the first term
|
||||
** of segment apSegment[0] is not a match, exit early.
|
||||
*/
|
||||
if( pFilter->zTerm ){
|
||||
if( nTerm<pFilter->nTerm
|
||||
|| (!isPrefix && nTerm>pFilter->nTerm)
|
||||
|| memcmp(zTerm, pFilter->zTerm, pFilter->nTerm)
|
||||
){
|
||||
goto finished;
|
||||
}
|
||||
}
|
||||
|
||||
while( nMerge<nSegment
|
||||
&& apSegment[nMerge]->aNode
|
||||
&& apSegment[nMerge]->nTerm==nTerm
|
||||
|
@ -1527,9 +1589,8 @@ int sqlite3Fts3SegReaderIterate(
|
|||
j++;
|
||||
}
|
||||
|
||||
assert( iCol>=0 || isColFilter==0 );
|
||||
if( isColFilter ){
|
||||
fts3ColumnFilter(iCol, &pList, &nList);
|
||||
fts3ColumnFilter(pFilter->iCol, &pList, &nList);
|
||||
}
|
||||
|
||||
if( !isIgnoreEmpty || nList>0 ){
|
||||
|
@ -1562,6 +1623,14 @@ int sqlite3Fts3SegReaderIterate(
|
|||
}
|
||||
}
|
||||
|
||||
/* If there is a term specified to filter on, and this is not a prefix
|
||||
** search, return now. The callback that corresponds to the required
|
||||
** term (if such a term exists in the index) has already been made.
|
||||
*/
|
||||
if( pFilter->zTerm && !isPrefix ){
|
||||
goto finished;
|
||||
}
|
||||
|
||||
for(i=0; i<nMerge; i++){
|
||||
rc = fts3SegReaderNext(apSegment[i]);
|
||||
if( rc!=SQLITE_OK ) goto finished;
|
||||
|
@ -1594,6 +1663,7 @@ static int fts3SegmentMerge(Fts3Table *p, int iLevel){
|
|||
SegmentWriter *pWriter = 0;
|
||||
int nSegment = 0; /* Number of segments being merged */
|
||||
Fts3SegReader **apSegment = 0; /* Array of Segment iterators */
|
||||
Fts3SegFilter filter; /* Segment term filter condition */
|
||||
|
||||
if( iLevel<0 ){
|
||||
/* This call is to merge all segments in the database to a single
|
||||
|
@ -1646,10 +1716,11 @@ static int fts3SegmentMerge(Fts3Table *p, int iLevel){
|
|||
pStmt = 0;
|
||||
if( rc!=SQLITE_OK ) goto finished;
|
||||
|
||||
rc = sqlite3Fts3SegReaderIterate(
|
||||
p, apSegment, nSegment,
|
||||
(iLevel<0 ? FTS3_SEGMENT_IGNORE_EMPTY : 0)|FTS3_SEGMENT_REQUIRE_POS,
|
||||
0, fts3MergeCallback, (void *)&pWriter
|
||||
memset(&filter, 0, sizeof(Fts3SegFilter));
|
||||
filter.flags = FTS3_SEGMENT_REQUIRE_POS;
|
||||
filter.flags |= (iLevel<0 ? FTS3_SEGMENT_IGNORE_EMPTY : 0);
|
||||
rc = sqlite3Fts3SegReaderIterate(p, apSegment, nSegment,
|
||||
&filter, fts3MergeCallback, (void *)&pWriter
|
||||
);
|
||||
if( rc!=SQLITE_OK ) goto finished;
|
||||
|
||||
|
|
18
manifest
18
manifest
|
@ -1,5 +1,5 @@
|
|||
C Add\sa\sfew\sextra\scoverage\stest\scases\sfor\sfts3.
|
||||
D 2009-11-16T16:36:23
|
||||
C Improvements\sto\sthe\sway\sfts3\sreads\sthe\sfull-text\sindex.
|
||||
D 2009-11-17T12:52:10
|
||||
F Makefile.arm-wince-mingw32ce-gcc fcd5e9cd67fe88836360bb4f9ef4cb7f8e2fb5a0
|
||||
F Makefile.in 53f3dfa49f28ab5b80cb083fb7c9051e596bcfa1
|
||||
F Makefile.linux-gcc d53183f4aa6a9192d249731c90dbdffbd2c68654
|
||||
|
@ -56,9 +56,9 @@ F ext/fts2/mkfts2amal.tcl 974d5d438cb3f7c4a652639262f82418c1e4cff0
|
|||
F ext/fts3/README.syntax a19711dc5458c20734b8e485e75fb1981ec2427a
|
||||
F ext/fts3/README.tokenizers 998756696647400de63d5ba60e9655036cb966e9
|
||||
F ext/fts3/README.txt 8c18f41574404623b76917b9da66fcb0ab38328d
|
||||
F ext/fts3/fts3.c 4d701e31cb32cfb9b535edcc33271d8bcb2fa76f
|
||||
F ext/fts3/fts3.c a72c19fa6270b5f88ad9b1215c821f7082164655
|
||||
F ext/fts3/fts3.h 3a10a0af180d502cecc50df77b1b22df142817fe
|
||||
F ext/fts3/fts3Int.h f8419da445790c0666d4b4d72dc15a07dd7ae93e
|
||||
F ext/fts3/fts3Int.h 5c040c0fb47ed81aaba589a55a7455c980592bea
|
||||
F ext/fts3/fts3_expr.c bdf11f3602f62f36f0e42823680bf22033dae0de
|
||||
F ext/fts3/fts3_hash.c 1af1833a4d581ee8d668bb71f5a500f7a0104982
|
||||
F ext/fts3/fts3_hash.h 39524725425078bf9e814e9569c74a8e5a21b9fb
|
||||
|
@ -68,7 +68,7 @@ F ext/fts3/fts3_snippet.c 8ea9619247ac61c79aca650fc3307b8b4097b5f3
|
|||
F ext/fts3/fts3_tokenizer.c 185a212670a9bbdeb5cad6942305e681bce5c87b
|
||||
F ext/fts3/fts3_tokenizer.h 7ff73caa3327589bf6550f60d93ebdd1f6a0fb5c
|
||||
F ext/fts3/fts3_tokenizer1.c 0a5bcc579f35de5d24a9345d7908dc25ae403ee7
|
||||
F ext/fts3/fts3_write.c 4285a2804ef308ed2eef946dae20d9d0361554d0
|
||||
F ext/fts3/fts3_write.c edf123f978fca3d26707452a380fa169849eb655
|
||||
F ext/fts3/mkfts3amal.tcl 252ecb7fe6467854f2aa237bf2c390b74e71f100
|
||||
F ext/icu/README.txt 3b130aa66e7a681136f6add198b076a2f90d1e33
|
||||
F ext/icu/icu.c 12e763d288d23b5a49de37caa30737b971a2f1e2
|
||||
|
@ -398,7 +398,7 @@ F test/fts3d.test 95fb3c862cbc4297c93fceb9a635543744e9ef52
|
|||
F test/fts3e.test 1f6c6ac9cc8b772ca256e6b22aaeed50c9350851
|
||||
F test/fts3expr.test 05dab77387801e4900009917bb18f556037d82da
|
||||
F test/fts3expr2.test 18da930352e5693eaa163a3eacf96233b7290d1a
|
||||
F test/fts3malloc.test ed11f188d03560d7f44bd5c126ac004f011cc719
|
||||
F test/fts3malloc.test cda1b22d8c86c3e434d93b63f2fc7a0191fb6d30
|
||||
F test/fts3near.test dc196dd17b4606f440c580d45b3d23aa975fd077
|
||||
F test/func.test af106ed834001738246d276659406823e35cde7b
|
||||
F test/func2.test 772d66227e4e6684b86053302e2d74a2500e1e0f
|
||||
|
@ -772,7 +772,7 @@ F tool/speedtest2.tcl ee2149167303ba8e95af97873c575c3e0fab58ff
|
|||
F tool/speedtest8.c 2902c46588c40b55661e471d7a86e4dd71a18224
|
||||
F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e
|
||||
F tool/vdbe-compress.tcl d70ea6d8a19e3571d7ab8c9b75cba86d1173ff0f
|
||||
P d3cae986ee1a176b1b015c3cebcd58ff0c3bdf92
|
||||
R 4deae89fec5a2f223cc1fc606b6691c8
|
||||
P f29c8fcade4aadeae3824975cf59f306c11c906b
|
||||
R a419bf1bcdd810be07b904f21b0a1ef0
|
||||
U dan
|
||||
Z 34f2a8a40095974f35e0328293748f2b
|
||||
Z 4e40dbade582f444c0b93ffcad88c385
|
||||
|
|
|
@ -1 +1 @@
|
|||
f29c8fcade4aadeae3824975cf59f306c11c906b
|
||||
45c051e78651d8204c17cecdda2bde705698881f
|
|
@ -181,17 +181,24 @@ do_write_test fts3_malloc-1.6 sqlite_master {
|
|||
}
|
||||
|
||||
# Test the xConnect/xDisconnect methods:
|
||||
db eval { ATTACH 'test2.db' AS aux }
|
||||
do_write_test fts3_malloc-1.6 aux.sqlite_master {
|
||||
CREATE VIRTUAL TABLE aux.ft7 USING fts3(a, b, c);
|
||||
}
|
||||
do_write_test fts3_malloc-1.6 aux.sqlite_master {
|
||||
CREATE VIRTUAL TABLE aux.ft7 USING fts3(a, b, c);
|
||||
}
|
||||
#db eval { ATTACH 'test2.db' AS aux }
|
||||
#do_write_test fts3_malloc-1.6 aux.sqlite_master {
|
||||
# CREATE VIRTUAL TABLE aux.ft7 USING fts3(a, b, c);
|
||||
#}
|
||||
#do_write_test fts3_malloc-1.6 aux.sqlite_master {
|
||||
# CREATE VIRTUAL TABLE aux.ft7 USING fts3(a, b, c);
|
||||
#}
|
||||
|
||||
|
||||
|
||||
do_test fts3_malloc-2.0 {
|
||||
execsql {
|
||||
DROP TABLE ft1;
|
||||
DROP TABLE ft2;
|
||||
DROP TABLE ft3;
|
||||
DROP TABLE ft4;
|
||||
DROP TABLE ft6;
|
||||
}
|
||||
execsql { CREATE VIRTUAL TABLE ft USING fts3(a, b) }
|
||||
for {set ii 1} {$ii < 32} {incr ii} {
|
||||
set a [list]
|
||||
|
|
Loading…
Reference in New Issue