mirror of https://github.com/sqlite/sqlite
Change fts4 so that the prefix= parameter is passes a comma-separated list of integers. For each integer N, a separate index of all prefixes of length N bytes is created.
FossilOrigin-Name: be59bf49402d2e2f4b95fb6668849f3745cb7bf2
This commit is contained in:
parent
09643ab745
commit
9d1f874ab3
174
ext/fts3/fts3.c
174
ext/fts3/fts3.c
|
@ -832,6 +832,58 @@ static char *fts3WriteExprList(Fts3Table *p, const char *zFunc, int *pRc){
|
|||
return zRet;
|
||||
}
|
||||
|
||||
static int fts3GobbleInt(const char **pp, int *pnOut){
|
||||
const char *p = *pp;
|
||||
int nInt = 0;
|
||||
for(p=*pp; p[0]>='0' && p[0]<='9'; p++){
|
||||
nInt = nInt * 10 + (p[0] - '0');
|
||||
}
|
||||
if( p==*pp ) return SQLITE_ERROR;
|
||||
*pnOut = nInt;
|
||||
*pp = p;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
|
||||
static int fts3PrefixParameter(
|
||||
const char *zParam, /* ABC in prefix=ABC parameter to parse */
|
||||
int *pnIndex, /* OUT: size of *apIndex[] array */
|
||||
struct Fts3Index **apIndex, /* OUT: Array of indexes for this table */
|
||||
struct Fts3Index **apFree /* OUT: Free this with sqlite3_free() */
|
||||
){
|
||||
struct Fts3Index *aIndex;
|
||||
int nIndex = 1;
|
||||
|
||||
if( zParam && zParam[0] ){
|
||||
const char *p;
|
||||
nIndex++;
|
||||
for(p=zParam; *p; p++){
|
||||
if( *p==',' ) nIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
aIndex = sqlite3_malloc(sizeof(struct Fts3Index) * nIndex);
|
||||
*apIndex = *apFree = aIndex;
|
||||
*pnIndex = nIndex;
|
||||
if( !aIndex ){
|
||||
return SQLITE_NOMEM;
|
||||
}
|
||||
|
||||
memset(aIndex, 0, sizeof(struct Fts3Index) * nIndex);
|
||||
if( zParam ){
|
||||
const char *p = zParam;
|
||||
int i;
|
||||
for(i=1; i<nIndex; i++){
|
||||
int nPrefix;
|
||||
if( fts3GobbleInt(&p, &nPrefix) ) return SQLITE_ERROR;
|
||||
aIndex[i].nPrefix = nPrefix;
|
||||
p++;
|
||||
}
|
||||
}
|
||||
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** This function is the implementation of both the xConnect and xCreate
|
||||
** methods of the FTS3 virtual table.
|
||||
|
@ -865,10 +917,14 @@ static int fts3InitVtab(
|
|||
int nName; /* Bytes required to hold table name */
|
||||
int isFts4 = (argv[0][3]=='4'); /* True for FTS4, false for FTS3 */
|
||||
int bNoDocsize = 0; /* True to omit %_docsize table */
|
||||
int bPrefix = 0; /* True to include a prefix-search index */
|
||||
const char **aCol; /* Array of column names */
|
||||
sqlite3_tokenizer *pTokenizer = 0; /* Tokenizer for this table */
|
||||
|
||||
char *zPrefix = 0; /* Prefix parameter value (or NULL) */
|
||||
int nIndex; /* Size of aIndex[] array */
|
||||
struct Fts3Index *aIndex; /* Array of indexes for this table */
|
||||
struct Fts3Index *aFree = 0; /* Free this before returning */
|
||||
|
||||
char *zCompress = 0;
|
||||
char *zUncompress = 0;
|
||||
|
||||
|
@ -929,7 +985,9 @@ static int fts3InitVtab(
|
|||
zUncompress = zVal;
|
||||
zVal = 0;
|
||||
}else if( nKey==6 && 0==sqlite3_strnicmp(z, "prefix", 6) ){
|
||||
bPrefix = 1;
|
||||
sqlite3_free(zPrefix);
|
||||
zPrefix = zVal;
|
||||
zVal = 0;
|
||||
}else{
|
||||
*pzErr = sqlite3_mprintf("unrecognized parameter: %s", z);
|
||||
rc = SQLITE_ERROR;
|
||||
|
@ -958,10 +1016,18 @@ static int fts3InitVtab(
|
|||
}
|
||||
assert( pTokenizer );
|
||||
|
||||
rc = fts3PrefixParameter(zPrefix, &nIndex, &aIndex, &aFree);
|
||||
if( rc==SQLITE_ERROR ){
|
||||
assert( zPrefix );
|
||||
*pzErr = sqlite3_mprintf("error parsing prefix parameter: %s", zPrefix);
|
||||
}
|
||||
if( rc ) goto fts3_init_out;
|
||||
|
||||
|
||||
/* Allocate and populate the Fts3Table structure. */
|
||||
nByte = sizeof(Fts3Table) + /* Fts3Table */
|
||||
nByte = sizeof(Fts3Table) + /* Fts3Table */
|
||||
nCol * sizeof(char *) + /* azColumn */
|
||||
nIndex * sizeof(struct Fts3Index) + /* aIndex */
|
||||
nName + /* zName */
|
||||
nDb + /* zDb */
|
||||
nString; /* Space for azColumn strings */
|
||||
|
@ -982,12 +1048,16 @@ static int fts3InitVtab(
|
|||
p->bHasStat = isFts4;
|
||||
TESTONLY( p->inTransaction = -1 );
|
||||
TESTONLY( p->mxSavepoint = -1 );
|
||||
p->bPrefix = bPrefix;
|
||||
fts3HashInit(&p->pendingTerms, FTS3_HASH_STRING, 1);
|
||||
fts3HashInit(&p->pendingPrefixes, FTS3_HASH_STRING, 1);
|
||||
|
||||
p->aIndex = (struct Fts3Index *)&p->azColumn[nCol];
|
||||
memcpy(p->aIndex, aIndex, sizeof(struct Fts3Index) * nIndex);
|
||||
p->nIndex = nIndex;
|
||||
for(i=0; i<nIndex; i++){
|
||||
fts3HashInit(&p->aIndex[i].hPending, FTS3_HASH_STRING, 1);
|
||||
}
|
||||
|
||||
/* Fill in the zName and zDb fields of the vtab structure. */
|
||||
zCsr = (char *)&p->azColumn[nCol];
|
||||
zCsr = (char *)&p->aIndex[nIndex];
|
||||
p->zName = zCsr;
|
||||
memcpy(zCsr, argv[2], nName);
|
||||
zCsr += nName;
|
||||
|
@ -1034,6 +1104,8 @@ static int fts3InitVtab(
|
|||
fts3DeclareVtab(&rc, p);
|
||||
|
||||
fts3_init_out:
|
||||
sqlite3_free(zPrefix);
|
||||
sqlite3_free(aFree);
|
||||
sqlite3_free(zCompress);
|
||||
sqlite3_free(zUncompress);
|
||||
sqlite3_free((void *)aCol);
|
||||
|
@ -2144,6 +2216,9 @@ static int fts3DeferredTermSelect(
|
|||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Append SegReader object pNew to the end of the pCsr->apSegment[] array.
|
||||
*/
|
||||
static int fts3SegReaderCursorAppend(
|
||||
Fts3SegReaderCursor *pCsr,
|
||||
Fts3SegReader *pNew
|
||||
|
@ -2163,11 +2238,12 @@ static int fts3SegReaderCursorAppend(
|
|||
}
|
||||
|
||||
/*
|
||||
** Set up a cursor object for iterating through the full-text index or
|
||||
** a single level therein.
|
||||
** Set up a cursor object for iterating through a full-text index or a
|
||||
** single level therein.
|
||||
*/
|
||||
int sqlite3Fts3SegReaderCursor(
|
||||
Fts3Table *p, /* FTS3 table handle */
|
||||
int iIndex, /* Index to search (from 0 to p->nIndex-1) */
|
||||
int iLevel, /* Level of segments to scan */
|
||||
const char *zTerm, /* Term to query for */
|
||||
int nTerm, /* Size of zTerm in bytes */
|
||||
|
@ -2180,49 +2256,41 @@ int sqlite3Fts3SegReaderCursor(
|
|||
int iAge = 0;
|
||||
sqlite3_stmt *pStmt = 0;
|
||||
|
||||
assert( iLevel==FTS3_SEGCURSOR_ALL_TERM
|
||||
assert( iIndex>=0 && iIndex<p->nIndex );
|
||||
assert( iLevel==FTS3_SEGCURSOR_ALL
|
||||
|| iLevel==FTS3_SEGCURSOR_PENDING
|
||||
|| iLevel==FTS3_SEGCURSOR_PENDING_PREFIX
|
||||
|| iLevel==FTS3_SEGCURSOR_ALL_PREFIX
|
||||
|| iLevel>=0
|
||||
);
|
||||
assert( 0>FTS3_SEGCURSOR_ALL_TERM
|
||||
&& 0>FTS3_SEGCURSOR_PENDING
|
||||
&& 0>FTS3_SEGCURSOR_PENDING_PREFIX
|
||||
&& 0>FTS3_SEGCURSOR_ALL_PREFIX
|
||||
);
|
||||
assert( iLevel==FTS3_SEGCURSOR_ALL_TERM
|
||||
|| iLevel==FTS3_SEGCURSOR_ALL_PREFIX
|
||||
|| (zTerm==0 && isPrefix==1)
|
||||
);
|
||||
assert( iLevel<FTS3_SEGDIR_MAXLEVEL );
|
||||
assert( FTS3_SEGCURSOR_ALL<0 && FTS3_SEGCURSOR_PENDING<0 );
|
||||
assert( iLevel==FTS3_SEGCURSOR_ALL || (zTerm==0 && isPrefix==1) );
|
||||
assert( isPrefix==0 || isScan==0 );
|
||||
|
||||
/* "isScan" is only set to true by the ft4aux module, an ordinary
|
||||
** full-text tables. */
|
||||
assert( isScan==0 || p->aIndex==0 );
|
||||
|
||||
memset(pCsr, 0, sizeof(Fts3SegReaderCursor));
|
||||
|
||||
/* "isScan" is only set to true by the ft4aux module, not an ordinary
|
||||
** full-text table. The pendingTerms and pendingPrefixes tables must be
|
||||
** empty in this case. */
|
||||
assert( isScan==0 || fts3HashCount(&p->pendingTerms)==0 );
|
||||
assert( isScan==0 || fts3HashCount(&p->pendingPrefixes)==0 );
|
||||
|
||||
/* If iLevel is less than 0, include a seg-reader for the pending-terms. */
|
||||
if( iLevel<0 && isScan==0 ){
|
||||
int bPrefix = (
|
||||
iLevel==FTS3_SEGCURSOR_PENDING_PREFIX
|
||||
|| iLevel==FTS3_SEGCURSOR_ALL_PREFIX
|
||||
);
|
||||
Fts3SegReader *pPending = 0;
|
||||
|
||||
rc = sqlite3Fts3SegReaderPending(p,zTerm,nTerm,isPrefix,bPrefix,&pPending);
|
||||
if( rc==SQLITE_OK && pPending ){
|
||||
rc = fts3SegReaderCursorAppend(pCsr, pPending);
|
||||
/* If iLevel is less than 0 and this is not a scan, include a seg-reader
|
||||
** for the pending-terms. If this is a scan, then this call must be being
|
||||
** made by an fts4aux module, not an FTS table. In this case calling
|
||||
** Fts3SegReaderPending might segfault, as the data structures used by
|
||||
** fts4aux are not completely populated. So it's easiest to filter these
|
||||
** calls out here. */
|
||||
if( iLevel<0 && p->aIndex ){
|
||||
Fts3SegReader *pSeg = 0;
|
||||
rc = sqlite3Fts3SegReaderPending(p, iIndex, zTerm, nTerm, isPrefix, &pSeg);
|
||||
if( rc==SQLITE_OK && pSeg ){
|
||||
rc = fts3SegReaderCursorAppend(pCsr, pSeg);
|
||||
}
|
||||
}
|
||||
|
||||
if( iLevel!=FTS3_SEGCURSOR_PENDING && iLevel!=FTS3_SEGCURSOR_PENDING_PREFIX ){
|
||||
if( iLevel!=FTS3_SEGCURSOR_PENDING ){
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3Fts3AllSegdirs(p, iLevel, &pStmt);
|
||||
rc = sqlite3Fts3AllSegdirs(p, iIndex, iLevel, &pStmt);
|
||||
}
|
||||
|
||||
while( rc==SQLITE_OK && SQLITE_ROW==(rc = sqlite3_step(pStmt)) ){
|
||||
Fts3SegReader *pSeg = 0;
|
||||
|
||||
|
@ -2273,17 +2341,25 @@ static int fts3TermSegReaderCursor(
|
|||
if( pSegcsr ){
|
||||
int i;
|
||||
int nCost = 0;
|
||||
int bFound = 0; /* True once an index has been found */
|
||||
Fts3Table *p = (Fts3Table *)pCsr->base.pVtab;
|
||||
|
||||
if( isPrefix && p->bPrefix && nTerm<=FTS3_MAX_PREFIX ){
|
||||
rc = sqlite3Fts3SegReaderCursor(
|
||||
p, FTS3_SEGCURSOR_ALL_PREFIX, zTerm, nTerm, 0, 0, pSegcsr);
|
||||
|
||||
}else{
|
||||
rc = sqlite3Fts3SegReaderCursor(
|
||||
p, FTS3_SEGCURSOR_ALL_TERM, zTerm, nTerm, isPrefix, 0, pSegcsr);
|
||||
if( isPrefix ){
|
||||
for(i=1; i<p->nIndex; i++){
|
||||
if( p->aIndex[i].nPrefix==nTerm ){
|
||||
bFound = 1;
|
||||
rc = sqlite3Fts3SegReaderCursor(
|
||||
p, i, FTS3_SEGCURSOR_ALL, zTerm, nTerm, 0, 0, pSegcsr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( bFound==0 ){
|
||||
rc = sqlite3Fts3SegReaderCursor(
|
||||
p, 0, FTS3_SEGCURSOR_ALL, zTerm, nTerm, isPrefix, 0, pSegcsr
|
||||
);
|
||||
}
|
||||
|
||||
for(i=0; rc==SQLITE_OK && i<pSegcsr->nSegment; i++){
|
||||
rc = sqlite3Fts3SegReaderCost(pCsr, pSegcsr->apSegment[i], &nCost);
|
||||
}
|
||||
|
@ -3340,7 +3416,6 @@ static int fts3RollbackMethod(sqlite3_vtab *pVtab){
|
|||
assert( p->inTransaction!=0 );
|
||||
TESTONLY( p->inTransaction = 0 );
|
||||
TESTONLY( p->mxSavepoint = -1; );
|
||||
sqlite3Fts3PendingPrefixesClear((Fts3Table *)pVtab);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
|
@ -3715,7 +3790,6 @@ static int fts3RollbackToMethod(sqlite3_vtab *pVtab, int iSavepoint){
|
|||
assert( p->mxSavepoint >= iSavepoint );
|
||||
TESTONLY( p->mxSavepoint = iSavepoint );
|
||||
sqlite3Fts3PendingTermsClear(p);
|
||||
sqlite3Fts3PendingPrefixesClear((Fts3Table *)pVtab);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
|
|
|
@ -23,8 +23,6 @@
|
|||
#include "fts3_tokenizer.h"
|
||||
#include "fts3_hash.h"
|
||||
|
||||
#define FTS3_MAX_PREFIX 8
|
||||
|
||||
/*
|
||||
** This constant controls how often segments are merged. Once there are
|
||||
** FTS3_MERGE_COUNT segments of level N, they are merged into a single
|
||||
|
@ -56,18 +54,22 @@
|
|||
#define FTS3_VARINT_MAX 10
|
||||
|
||||
/*
|
||||
** FTS4 virtual tables may maintain two separate indexes. One that indexes
|
||||
** all document terms (the same index FTS3 tables maintain) and another used
|
||||
** for prefixes. B+-trees that are part of the prefix index have values for
|
||||
** the %_segdir.level column that are equal to or greater than the following
|
||||
** value.
|
||||
** FTS4 virtual tables may maintain multiple indexes - one index of all terms
|
||||
** in the document set and zero or more prefix indexes. All indexes are stored
|
||||
** as one or more b+-trees in the %_segments and %_segdir tables.
|
||||
**
|
||||
** It is considered impossible for the regular index to use levels this large.
|
||||
** In theory it could, but that would require that at least 2^1024 separate
|
||||
** write operations to be made within the lifetime of the database.
|
||||
** It is possible to determine which index a b+-tree belongs to based on the
|
||||
** value stored in the "%_segdir.level" column. Given this value L, the index
|
||||
** that the b+-tree belongs to is (L<<10). In other words, all b+-trees with
|
||||
** level values between 0 and 1023 (inclusive) belong to index 0, all levels
|
||||
** between 1024 and 2047 to index 1, and so on.
|
||||
**
|
||||
** It is considered impossible for an index to use more than 1024 levels. In
|
||||
** theory though this may happen, but only after at least
|
||||
** (FTS3_MERGE_COUNT^1024) separate flushes of the pending-terms tables.
|
||||
*/
|
||||
#define FTS3_SEGDIR_PREFIXLEVEL 1024
|
||||
#define FTS3_SEGDIR_PREFIXLEVEL_STR "1024"
|
||||
#define FTS3_SEGDIR_MAXLEVEL 1024
|
||||
#define FTS3_SEGDIR_MAXLEVEL_STR "1024"
|
||||
|
||||
/*
|
||||
** The testcase() macro is only used by the amalgamation. If undefined,
|
||||
|
@ -172,23 +174,32 @@ struct Fts3Table {
|
|||
int nNodeSize; /* Soft limit for node size */
|
||||
u8 bHasStat; /* True if %_stat table exists */
|
||||
u8 bHasDocsize; /* True if %_docsize table exists */
|
||||
u8 bPrefix; /* True if there is a prefix index */
|
||||
int nPgsz; /* Page size for host database */
|
||||
char *zSegmentsTbl; /* Name of %_segments table */
|
||||
sqlite3_blob *pSegments; /* Blob handle open on %_segments table */
|
||||
|
||||
/* The following hash table is used to buffer pending index updates during
|
||||
/* TODO: Fix the first paragraph of this comment.
|
||||
**
|
||||
** The following hash table is used to buffer pending index updates during
|
||||
** transactions. Variable nPendingData estimates the memory size of the
|
||||
** pending data, including hash table overhead, but not malloc overhead.
|
||||
** When nPendingData exceeds nMaxPendingData, the buffer is flushed
|
||||
** automatically. Variable iPrevDocid is the docid of the most recently
|
||||
** inserted record.
|
||||
**
|
||||
** A single FTS4 table may have multiple full-text indexes. For each index
|
||||
** there is an entry in the aIndex[] array. Index 0 is an index of all the
|
||||
** terms that appear in the document set. Each subsequent index in aIndex[]
|
||||
** is an index of prefixes of a specific length.
|
||||
*/
|
||||
int nMaxPendingData;
|
||||
int nPendingData;
|
||||
sqlite_int64 iPrevDocid;
|
||||
Fts3Hash pendingTerms;
|
||||
Fts3Hash pendingPrefixes;
|
||||
int nIndex; /* Size of aIndex[] */
|
||||
struct Fts3Index {
|
||||
int nPrefix; /* Prefix length (0 for main terms index) */
|
||||
Fts3Hash hPending; /* Pending terms table for this index */
|
||||
} *aIndex;
|
||||
int nMaxPendingData; /* Max pending data before flush to disk */
|
||||
int nPendingData; /* Current bytes of pending data */
|
||||
sqlite_int64 iPrevDocid; /* Docid of most recently inserted document */
|
||||
|
||||
#if defined(SQLITE_DEBUG)
|
||||
/* State variables used for validating that the transaction control
|
||||
|
@ -336,10 +347,10 @@ int sqlite3Fts3Optimize(Fts3Table *);
|
|||
int sqlite3Fts3SegReaderNew(int, sqlite3_int64,
|
||||
sqlite3_int64, sqlite3_int64, const char *, int, Fts3SegReader**);
|
||||
int sqlite3Fts3SegReaderPending(
|
||||
Fts3Table*,const char*,int,int,int,Fts3SegReader**);
|
||||
Fts3Table*,int,const char*,int,int,Fts3SegReader**);
|
||||
void sqlite3Fts3SegReaderFree(Fts3SegReader *);
|
||||
int sqlite3Fts3SegReaderCost(Fts3Cursor *, Fts3SegReader *, int *);
|
||||
int sqlite3Fts3AllSegdirs(Fts3Table*, int, sqlite3_stmt **);
|
||||
int sqlite3Fts3AllSegdirs(Fts3Table*, int, int, sqlite3_stmt **);
|
||||
int sqlite3Fts3ReadLock(Fts3Table *);
|
||||
int sqlite3Fts3ReadBlock(Fts3Table*, sqlite3_int64, char **, int*);
|
||||
|
||||
|
@ -355,15 +366,13 @@ void sqlite3Fts3SegmentsClose(Fts3Table *);
|
|||
|
||||
/* Special values interpreted by sqlite3SegReaderCursor() */
|
||||
#define FTS3_SEGCURSOR_PENDING -1
|
||||
#define FTS3_SEGCURSOR_PENDING_PREFIX -2
|
||||
#define FTS3_SEGCURSOR_ALL_PREFIX -3
|
||||
#define FTS3_SEGCURSOR_ALL_TERM -4
|
||||
#define FTS3_SEGCURSOR_ALL -2
|
||||
|
||||
int sqlite3Fts3SegReaderStart(Fts3Table*, Fts3SegReaderCursor*, Fts3SegFilter*);
|
||||
int sqlite3Fts3SegReaderStep(Fts3Table *, Fts3SegReaderCursor *);
|
||||
void sqlite3Fts3SegReaderFinish(Fts3SegReaderCursor *);
|
||||
int sqlite3Fts3SegReaderCursor(
|
||||
Fts3Table *, int, const char *, int, int, int, Fts3SegReaderCursor *);
|
||||
Fts3Table *, int, int, const char *, int, int, int, Fts3SegReaderCursor *);
|
||||
|
||||
/* Flags allowed as part of the 4th argument to SegmentReaderIterate() */
|
||||
#define FTS3_SEGMENT_REQUIRE_POS 0x00000001
|
||||
|
|
|
@ -96,6 +96,7 @@ static int fts3auxConnectMethod(
|
|||
p->pFts3Tab->zDb = (char *)&p->pFts3Tab[1];
|
||||
p->pFts3Tab->zName = &p->pFts3Tab->zDb[nDb+1];
|
||||
p->pFts3Tab->db = db;
|
||||
p->pFts3Tab->nIndex = 1;
|
||||
|
||||
memcpy((char *)p->pFts3Tab->zDb, zDb, nDb);
|
||||
memcpy((char *)p->pFts3Tab->zName, zFts3, nFts3);
|
||||
|
@ -375,7 +376,7 @@ static int fts3auxFilterMethod(
|
|||
if( pCsr->zStop==0 ) return SQLITE_NOMEM;
|
||||
}
|
||||
|
||||
rc = sqlite3Fts3SegReaderCursor(pFts3, FTS3_SEGCURSOR_ALL_TERM,
|
||||
rc = sqlite3Fts3SegReaderCursor(pFts3, 0, FTS3_SEGCURSOR_ALL,
|
||||
pCsr->filter.zTerm, pCsr->filter.nTerm, 0, isScan, &pCsr->csr
|
||||
);
|
||||
if( rc==SQLITE_OK ){
|
||||
|
|
|
@ -27,7 +27,7 @@ typedef struct Fts3termCursor Fts3termCursor;
|
|||
|
||||
struct Fts3termTable {
|
||||
sqlite3_vtab base; /* Base class used by SQLite core */
|
||||
int bPrefix; /* True for an fts4prefix table */
|
||||
int iIndex; /* Index for Fts3Table.aIndex[] */
|
||||
Fts3Table *pFts3Tab;
|
||||
};
|
||||
|
||||
|
@ -70,6 +70,12 @@ static int fts3termConnectMethod(
|
|||
int nByte; /* Bytes of space to allocate here */
|
||||
int rc; /* value returned by declare_vtab() */
|
||||
Fts3termTable *p; /* Virtual table object to return */
|
||||
int iIndex = 0;
|
||||
|
||||
if( argc==5 ){
|
||||
iIndex = atoi(argv[4]);
|
||||
argc--;
|
||||
}
|
||||
|
||||
/* The user should specify a single argument - the name of an fts3 table. */
|
||||
if( argc!=4 ){
|
||||
|
@ -96,7 +102,8 @@ static int fts3termConnectMethod(
|
|||
p->pFts3Tab->zDb = (char *)&p->pFts3Tab[1];
|
||||
p->pFts3Tab->zName = &p->pFts3Tab->zDb[nDb+1];
|
||||
p->pFts3Tab->db = db;
|
||||
p->bPrefix = (int)pCtx;
|
||||
p->pFts3Tab->nIndex = iIndex+1;
|
||||
p->iIndex = iIndex;
|
||||
|
||||
memcpy((char *)p->pFts3Tab->zDb, zDb, nDb);
|
||||
memcpy((char *)p->pFts3Tab->zName, zFts3, nFts3);
|
||||
|
@ -263,8 +270,7 @@ static int fts3termFilterMethod(
|
|||
pCsr->filter.flags = FTS3_SEGMENT_REQUIRE_POS|FTS3_SEGMENT_IGNORE_EMPTY;
|
||||
pCsr->filter.flags |= FTS3_SEGMENT_SCAN;
|
||||
|
||||
rc = sqlite3Fts3SegReaderCursor(pFts3,
|
||||
p->bPrefix ? FTS3_SEGCURSOR_ALL_PREFIX : FTS3_SEGCURSOR_ALL_TERM,
|
||||
rc = sqlite3Fts3SegReaderCursor(pFts3, p->iIndex, FTS3_SEGCURSOR_ALL,
|
||||
pCsr->filter.zTerm, pCsr->filter.nTerm, 0, 1, &pCsr->csr
|
||||
);
|
||||
if( rc==SQLITE_OK ){
|
||||
|
@ -355,9 +361,6 @@ int sqlite3Fts3InitTerm(sqlite3 *db){
|
|||
int rc; /* Return code */
|
||||
|
||||
rc = sqlite3_create_module(db, "fts4term", &fts3term_module, 0);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3_create_module(db, "fts4prefix", &fts3term_module, (void*)1);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
|
|
@ -183,10 +183,10 @@ struct SegmentNode {
|
|||
#define SQL_NEXT_SEGMENTS_ID 10
|
||||
#define SQL_INSERT_SEGDIR 11
|
||||
#define SQL_SELECT_LEVEL 12
|
||||
#define SQL_SELECT_ALL_LEVEL 13
|
||||
#define SQL_SELECT_LEVEL_RANGE 13
|
||||
#define SQL_SELECT_LEVEL_COUNT 14
|
||||
#define SQL_SELECT_SEGDIR_MAX_LEVEL 15
|
||||
#define SQL_DELETE_SEGDIR_BY_LEVEL 16
|
||||
#define SQL_DELETE_SEGDIR_LEVEL 16
|
||||
#define SQL_DELETE_SEGMENTS_RANGE 17
|
||||
#define SQL_CONTENT_INSERT 18
|
||||
#define SQL_DELETE_DOCSIZE 19
|
||||
|
@ -194,10 +194,11 @@ struct SegmentNode {
|
|||
#define SQL_SELECT_DOCSIZE 21
|
||||
#define SQL_SELECT_DOCTOTAL 22
|
||||
#define SQL_REPLACE_DOCTOTAL 23
|
||||
#define SQL_SELECT_ALL_PREFIX_LEVEL 24
|
||||
|
||||
#define SQL_SELECT_ALL_PREFIX_LEVEL 24
|
||||
#define SQL_DELETE_ALL_TERMS_SEGDIR 25
|
||||
#define SQL_DELETE_ALL_PREFIX_SEGDIR 26
|
||||
|
||||
#define SQL_DELETE_SEGDIR_RANGE 26
|
||||
|
||||
/*
|
||||
** This function is used to obtain an SQLite prepared statement handle
|
||||
|
@ -234,12 +235,11 @@ static int fts3SqlStmt(
|
|||
/* 12 */ "SELECT idx, start_block, leaves_end_block, end_block, root "
|
||||
"FROM %Q.'%q_segdir' WHERE level = ? ORDER BY idx ASC",
|
||||
/* 13 */ "SELECT idx, start_block, leaves_end_block, end_block, root "
|
||||
"FROM %Q.'%q_segdir' WHERE level < " FTS3_SEGDIR_PREFIXLEVEL_STR
|
||||
" ORDER BY level DESC, idx ASC",
|
||||
"FROM %Q.'%q_segdir' WHERE level BETWEEN ? AND ?"
|
||||
"ORDER BY level DESC, idx ASC",
|
||||
|
||||
/* 14 */ "SELECT count(*) FROM %Q.'%q_segdir' WHERE level = ?",
|
||||
/* 15 */ "SELECT max(level) FROM %Q.'%q_segdir' WHERE level < (?+1)*"
|
||||
FTS3_SEGDIR_PREFIXLEVEL_STR,
|
||||
/* 15 */ "SELECT max(level) FROM %Q.'%q_segdir' WHERE level BETWEEN ? AND ?",
|
||||
|
||||
/* 16 */ "DELETE FROM %Q.'%q_segdir' WHERE level = ?",
|
||||
/* 17 */ "DELETE FROM %Q.'%q_segments' WHERE blockid BETWEEN ? AND ?",
|
||||
|
@ -249,11 +249,11 @@ static int fts3SqlStmt(
|
|||
/* 21 */ "SELECT size FROM %Q.'%q_docsize' WHERE docid=?",
|
||||
/* 22 */ "SELECT value FROM %Q.'%q_stat' WHERE id=0",
|
||||
/* 23 */ "REPLACE INTO %Q.'%q_stat' VALUES(0,?)",
|
||||
/* 24 */ "SELECT idx, start_block, leaves_end_block, end_block, root "
|
||||
"FROM %Q.'%q_segdir' WHERE level >= " FTS3_SEGDIR_PREFIXLEVEL_STR
|
||||
" ORDER BY level DESC, idx ASC",
|
||||
/* 25 */ "DELETE FROM %Q.'%q_segdir' WHERE level<" FTS3_SEGDIR_PREFIXLEVEL_STR,
|
||||
/* 26 */ "DELETE FROM %Q.'%q_segdir' WHERE level>=" FTS3_SEGDIR_PREFIXLEVEL_STR,
|
||||
/* 24 */ "",
|
||||
/* 25 */ "",
|
||||
|
||||
/* 26 */ "DELETE FROM %Q.'%q_segdir' WHERE level BETWEEN ? AND ?",
|
||||
|
||||
};
|
||||
int rc = SQLITE_OK;
|
||||
sqlite3_stmt *pStmt;
|
||||
|
@ -409,17 +409,32 @@ int sqlite3Fts3ReadLock(Fts3Table *p){
|
|||
** 3: end_block
|
||||
** 4: root
|
||||
*/
|
||||
int sqlite3Fts3AllSegdirs(Fts3Table *p, int iLevel, sqlite3_stmt **ppStmt){
|
||||
int sqlite3Fts3AllSegdirs(
|
||||
Fts3Table *p, /* FTS3 table */
|
||||
int iIndex, /* Index for p->aIndex[] */
|
||||
int iLevel, /* Level to select */
|
||||
sqlite3_stmt **ppStmt /* OUT: Compiled statement */
|
||||
){
|
||||
int rc;
|
||||
sqlite3_stmt *pStmt = 0;
|
||||
if( iLevel==FTS3_SEGCURSOR_ALL_PREFIX ){
|
||||
rc = fts3SqlStmt(p, SQL_SELECT_ALL_PREFIX_LEVEL, &pStmt, 0);
|
||||
}else if( iLevel==FTS3_SEGCURSOR_ALL_TERM ){
|
||||
rc = fts3SqlStmt(p, SQL_SELECT_ALL_LEVEL, &pStmt, 0);
|
||||
|
||||
assert( iLevel==FTS3_SEGCURSOR_ALL || iLevel>=0 );
|
||||
assert( iLevel<FTS3_SEGDIR_MAXLEVEL );
|
||||
assert( iIndex>=0 && iIndex<p->nIndex );
|
||||
|
||||
if( iLevel==FTS3_SEGCURSOR_ALL ){
|
||||
/* "SELECT * FROM %_segdir WHERE level BETWEEN ? AND ? ORDER BY ..." */
|
||||
rc = fts3SqlStmt(p, SQL_SELECT_LEVEL_RANGE, &pStmt, 0);
|
||||
if( rc==SQLITE_OK ){
|
||||
sqlite3_bind_int(pStmt, 1, iIndex*FTS3_SEGDIR_MAXLEVEL);
|
||||
sqlite3_bind_int(pStmt, 2, (iIndex+1)*FTS3_SEGDIR_MAXLEVEL-1);
|
||||
}
|
||||
}else{
|
||||
assert( iLevel>=0 );
|
||||
/* "SELECT * FROM %_segdir WHERE level = ? ORDER BY ..." */
|
||||
rc = fts3SqlStmt(p, SQL_SELECT_LEVEL, &pStmt, 0);
|
||||
if( rc==SQLITE_OK ) sqlite3_bind_int(pStmt, 1, iLevel);
|
||||
if( rc==SQLITE_OK ){
|
||||
sqlite3_bind_int(pStmt, 1, iLevel+iIndex*FTS3_SEGDIR_MAXLEVEL);
|
||||
}
|
||||
}
|
||||
*ppStmt = pStmt;
|
||||
return rc;
|
||||
|
@ -565,9 +580,6 @@ static int fts3PendingTermsAddOne(
|
|||
return rc;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
** Tokenize the nul-terminated string zText and add all tokens to the
|
||||
** pending-terms hash-table. The docid used is that currently stored in
|
||||
|
@ -616,6 +628,7 @@ static int fts3PendingTermsAdd(
|
|||
while( SQLITE_OK==rc
|
||||
&& SQLITE_OK==(rc = xNext(pCsr, &zToken, &nToken, &iStart, &iEnd, &iPos))
|
||||
){
|
||||
int i;
|
||||
if( iPos>=nWord ) nWord = iPos+1;
|
||||
|
||||
/* Positions cannot be negative; we use -1 as a terminator internally.
|
||||
|
@ -626,12 +639,19 @@ static int fts3PendingTermsAdd(
|
|||
break;
|
||||
}
|
||||
|
||||
rc = fts3PendingTermsAddOne(p,iCol,iPos,&p->pendingTerms,zToken,nToken);
|
||||
if( p->bPrefix ){
|
||||
int n = (nToken > FTS3_MAX_PREFIX ? FTS3_MAX_PREFIX : nToken);
|
||||
for(; n>0 && rc==SQLITE_OK; n--){
|
||||
rc = fts3PendingTermsAddOne(p,iCol,iPos,&p->pendingPrefixes,zToken,n);
|
||||
}
|
||||
/* Add the term to the terms index */
|
||||
rc = fts3PendingTermsAddOne(
|
||||
p, iCol, iPos, &p->aIndex[0].hPending, zToken, nToken
|
||||
);
|
||||
|
||||
/* Add the term to each of the prefix indexes that it is not too
|
||||
** short for. */
|
||||
for(i=1; rc==SQLITE_OK && i<p->nIndex; i++){
|
||||
struct Fts3Index *pIndex = &p->aIndex[i];
|
||||
if( nToken<pIndex->nPrefix ) continue;
|
||||
rc = fts3PendingTermsAddOne(
|
||||
p, iCol, iPos, &pIndex->hPending, zToken, pIndex->nPrefix
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -661,28 +681,21 @@ static int fts3PendingTermsDocid(Fts3Table *p, sqlite_int64 iDocid){
|
|||
}
|
||||
|
||||
/*
|
||||
** Discard the contents of the pending-terms hash table.
|
||||
** Discard the contents of the pending-terms hash tables.
|
||||
*/
|
||||
void sqlite3Fts3PendingTermsClear(Fts3Table *p){
|
||||
Fts3HashElem *pElem;
|
||||
for(pElem=fts3HashFirst(&p->pendingTerms); pElem; pElem=fts3HashNext(pElem)){
|
||||
sqlite3_free(fts3HashData(pElem));
|
||||
int i;
|
||||
for(i=0; i<p->nIndex; i++){
|
||||
Fts3HashElem *pElem;
|
||||
Fts3Hash *pHash = &p->aIndex[i].hPending;
|
||||
for(pElem=fts3HashFirst(pHash); pElem; pElem=fts3HashNext(pElem)){
|
||||
sqlite3_free(fts3HashData(pElem));
|
||||
}
|
||||
fts3HashClear(pHash);
|
||||
}
|
||||
fts3HashClear(&p->pendingTerms);
|
||||
p->nPendingData = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Discard the contents of the pending-prefixes hash table.
|
||||
*/
|
||||
void sqlite3Fts3PendingPrefixesClear(Fts3Table *p){
|
||||
Fts3HashElem *pElem;
|
||||
for(pElem=fts3HashFirst(&p->pendingPrefixes); pElem; pElem=fts3HashNext(pElem)){
|
||||
sqlite3_free(fts3HashData(pElem));
|
||||
}
|
||||
fts3HashClear(&p->pendingPrefixes);
|
||||
}
|
||||
|
||||
/*
|
||||
** This function is called by the xUpdate() method as part of an INSERT
|
||||
** operation. It adds entries for each term in the new record to the
|
||||
|
@ -780,7 +793,6 @@ static int fts3DeleteAll(Fts3Table *p){
|
|||
|
||||
/* Discard the contents of the pending-terms hash table. */
|
||||
sqlite3Fts3PendingTermsClear(p);
|
||||
sqlite3Fts3PendingPrefixesClear(p);
|
||||
|
||||
/* Delete everything from the %_content, %_segments and %_segdir tables. */
|
||||
fts3SqlExec(&rc, p, SQL_DELETE_ALL_CONTENT, 0);
|
||||
|
@ -836,7 +848,7 @@ static void fts3DeleteTerms(
|
|||
** Forward declaration to account for the circular dependency between
|
||||
** functions fts3SegmentMerge() and fts3AllocateSegdirIdx().
|
||||
*/
|
||||
static int fts3SegmentMerge(Fts3Table *, int);
|
||||
static int fts3SegmentMerge(Fts3Table *, int, int);
|
||||
|
||||
/*
|
||||
** This function allocates a new level iLevel index in the segdir table.
|
||||
|
@ -853,7 +865,12 @@ static int fts3SegmentMerge(Fts3Table *, int);
|
|||
** If successful, *piIdx is set to the allocated index slot and SQLITE_OK
|
||||
** returned. Otherwise, an SQLite error code is returned.
|
||||
*/
|
||||
static int fts3AllocateSegdirIdx(Fts3Table *p, int iLevel, int *piIdx){
|
||||
static int fts3AllocateSegdirIdx(
|
||||
Fts3Table *p,
|
||||
int iIndex, /* Index for p->aIndex */
|
||||
int iLevel,
|
||||
int *piIdx
|
||||
){
|
||||
int rc; /* Return Code */
|
||||
sqlite3_stmt *pNextIdx; /* Query for next idx at level iLevel */
|
||||
int iNext = 0; /* Result of query pNextIdx */
|
||||
|
@ -861,7 +878,7 @@ static int fts3AllocateSegdirIdx(Fts3Table *p, int iLevel, int *piIdx){
|
|||
/* Set variable iNext to the next available segdir index at level iLevel. */
|
||||
rc = fts3SqlStmt(p, SQL_NEXT_SEGMENT_INDEX, &pNextIdx, 0);
|
||||
if( rc==SQLITE_OK ){
|
||||
sqlite3_bind_int(pNextIdx, 1, iLevel);
|
||||
sqlite3_bind_int(pNextIdx, 1, iIndex*FTS3_SEGDIR_MAXLEVEL + iLevel);
|
||||
if( SQLITE_ROW==sqlite3_step(pNextIdx) ){
|
||||
iNext = sqlite3_column_int(pNextIdx, 0);
|
||||
}
|
||||
|
@ -875,7 +892,7 @@ static int fts3AllocateSegdirIdx(Fts3Table *p, int iLevel, int *piIdx){
|
|||
** if iNext is less than FTS3_MERGE_COUNT, allocate index iNext.
|
||||
*/
|
||||
if( iNext>=FTS3_MERGE_COUNT ){
|
||||
rc = fts3SegmentMerge(p, iLevel);
|
||||
rc = fts3SegmentMerge(p, iIndex, iLevel);
|
||||
*piIdx = 0;
|
||||
}else{
|
||||
*piIdx = iNext;
|
||||
|
@ -1309,23 +1326,22 @@ static int fts3CompareElemByTerm(const void *lhs, const void *rhs){
|
|||
*/
|
||||
int sqlite3Fts3SegReaderPending(
|
||||
Fts3Table *p, /* Virtual table handle */
|
||||
int iIndex, /* Index for p->aIndex */
|
||||
const char *zTerm, /* Term to search for */
|
||||
int nTerm, /* Size of buffer zTerm */
|
||||
int isMultiTerm, /* True to visit multiple terms */
|
||||
int isPrefixIter, /* 0->pendingTerms, 1->pendingPrefixes */
|
||||
int bPrefix, /* True for a prefix iterator */
|
||||
Fts3SegReader **ppReader /* OUT: SegReader for pending-terms */
|
||||
){
|
||||
Fts3SegReader *pReader = 0; /* Fts3SegReader object to return */
|
||||
Fts3HashElem **aElem = 0; /* Array of term hash entries to scan */
|
||||
int nElem = 0; /* Size of array at aElem */
|
||||
int rc = SQLITE_OK; /* Return Code */
|
||||
Fts3Hash *pHash;
|
||||
|
||||
if( isMultiTerm ){
|
||||
pHash = &p->aIndex[iIndex].hPending;
|
||||
if( bPrefix ){
|
||||
int nAlloc = 0; /* Size of allocated array at aElem */
|
||||
Fts3HashElem *pE = 0; /* Iterator variable */
|
||||
Fts3Hash *pHash;
|
||||
|
||||
pHash = (isPrefixIter ? &p->pendingPrefixes : &p->pendingTerms);
|
||||
|
||||
for(pE=fts3HashFirst(pHash); pE; pE=fts3HashNext(pE)){
|
||||
char *zKey = (char *)fts3HashKey(pE);
|
||||
|
@ -1360,7 +1376,7 @@ int sqlite3Fts3SegReaderPending(
|
|||
}else{
|
||||
/* The query is a simple term lookup that matches at most one term in
|
||||
** the index. All that is required is a straight hash-lookup. */
|
||||
Fts3HashElem *pE = fts3HashFindElem(&p->pendingTerms, zTerm, nTerm);
|
||||
Fts3HashElem *pE = fts3HashFindElem(pHash, zTerm, nTerm);
|
||||
if( pE ){
|
||||
aElem = &pE;
|
||||
nElem = 1;
|
||||
|
@ -1380,7 +1396,7 @@ int sqlite3Fts3SegReaderPending(
|
|||
}
|
||||
}
|
||||
|
||||
if( isMultiTerm ){
|
||||
if( bPrefix ){
|
||||
sqlite3_free(aElem);
|
||||
}
|
||||
*ppReader = pReader;
|
||||
|
@ -1992,28 +2008,28 @@ static int fts3IsEmpty(Fts3Table *p, sqlite3_value *pRowid, int *pisEmpty){
|
|||
}
|
||||
|
||||
/*
|
||||
** Set *pnMax to the largest segment level in the database for either the
|
||||
** terms index (if parameter bPrefixIndex is 0) or the prefixes index (if
|
||||
** parameter bPrefixIndex is 1).
|
||||
** Set *pnMax to the largest segment level in the database for the index
|
||||
** iIndex.
|
||||
**
|
||||
** Segment levels are stored in the 'level' column of the %_segdir table.
|
||||
**
|
||||
** Return SQLITE_OK if successful, or an SQLite error code if not.
|
||||
*/
|
||||
static int fts3SegmentMaxLevel(Fts3Table *p, int bPrefixIndex, int *pnMax){
|
||||
static int fts3SegmentMaxLevel(Fts3Table *p, int iIndex, int *pnMax){
|
||||
sqlite3_stmt *pStmt;
|
||||
int rc;
|
||||
assert( bPrefixIndex==0 || bPrefixIndex==1 );
|
||||
assert( iIndex>=0 && iIndex<p->nIndex );
|
||||
|
||||
/* Set pStmt to the compiled version of:
|
||||
**
|
||||
** SELECT max(level) FROM %Q.'%q_segdir' WHERE level < (?+1) * 1024
|
||||
** SELECT max(level) FROM %Q.'%q_segdir' WHERE level BETWEEN ? AND ?
|
||||
**
|
||||
** (1024 is actually the value of macro FTS3_SEGDIR_PREFIXLEVEL_STR).
|
||||
*/
|
||||
rc = fts3SqlStmt(p, SQL_SELECT_SEGDIR_MAX_LEVEL, &pStmt, 0);
|
||||
if( rc!=SQLITE_OK ) return rc;
|
||||
sqlite3_bind_int(pStmt, 1, bPrefixIndex);
|
||||
sqlite3_bind_int(pStmt, 1, iIndex*FTS3_SEGDIR_MAXLEVEL);
|
||||
sqlite3_bind_int(pStmt, 2, (iIndex+1)*FTS3_SEGDIR_MAXLEVEL - 1);
|
||||
if( SQLITE_ROW==sqlite3_step(pStmt) ){
|
||||
*pnMax = sqlite3_column_int(pStmt, 0);
|
||||
}
|
||||
|
@ -2036,6 +2052,7 @@ static int fts3SegmentMaxLevel(Fts3Table *p, int bPrefixIndex, int *pnMax){
|
|||
*/
|
||||
static int fts3DeleteSegdir(
|
||||
Fts3Table *p, /* Virtual table handle */
|
||||
int iIndex, /* Index for p->aIndex */
|
||||
int iLevel, /* Level of %_segdir entries to delete */
|
||||
Fts3SegReader **apSegment, /* Array of SegReader objects */
|
||||
int nReader /* Size of array apSegment */
|
||||
|
@ -2058,27 +2075,23 @@ static int fts3DeleteSegdir(
|
|||
return rc;
|
||||
}
|
||||
|
||||
assert( iLevel>=0
|
||||
|| iLevel==FTS3_SEGCURSOR_ALL_TERM
|
||||
|| iLevel==FTS3_SEGCURSOR_ALL_PREFIX
|
||||
|| iLevel==FTS3_SEGCURSOR_PENDING
|
||||
|| iLevel==FTS3_SEGCURSOR_PENDING_PREFIX
|
||||
);
|
||||
if( iLevel==FTS3_SEGCURSOR_ALL_TERM ){
|
||||
fts3SqlExec(&rc, p, SQL_DELETE_ALL_TERMS_SEGDIR, 0);
|
||||
}else if( iLevel==FTS3_SEGCURSOR_ALL_PREFIX ){
|
||||
fts3SqlExec(&rc, p, SQL_DELETE_ALL_PREFIX_SEGDIR, 0);
|
||||
}else if( iLevel==FTS3_SEGCURSOR_PENDING_PREFIX ){
|
||||
sqlite3Fts3PendingPrefixesClear(p);
|
||||
}else if( iLevel==FTS3_SEGCURSOR_PENDING ){
|
||||
sqlite3Fts3PendingTermsClear(p);
|
||||
}else if( iLevel>=0 ){
|
||||
rc = fts3SqlStmt(p, SQL_DELETE_SEGDIR_BY_LEVEL, &pDelete, 0);
|
||||
assert( iLevel>=0 || iLevel==FTS3_SEGCURSOR_ALL );
|
||||
if( iLevel==FTS3_SEGCURSOR_ALL ){
|
||||
rc = fts3SqlStmt(p, SQL_DELETE_SEGDIR_RANGE, &pDelete, 0);
|
||||
if( rc==SQLITE_OK ){
|
||||
sqlite3_bind_int(pDelete, 1, iLevel);
|
||||
sqlite3_step(pDelete);
|
||||
rc = sqlite3_reset(pDelete);
|
||||
sqlite3_bind_int(pDelete, 1, iIndex*FTS3_SEGDIR_MAXLEVEL);
|
||||
sqlite3_bind_int(pDelete, 2, (iIndex+1) * FTS3_SEGDIR_MAXLEVEL - 1);
|
||||
}
|
||||
}else{
|
||||
rc = fts3SqlStmt(p, SQL_DELETE_SEGDIR_LEVEL, &pDelete, 0);
|
||||
if( rc==SQLITE_OK ){
|
||||
sqlite3_bind_int(pDelete, 1, iIndex*FTS3_SEGDIR_MAXLEVEL + iLevel);
|
||||
}
|
||||
}
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
sqlite3_step(pDelete);
|
||||
rc = sqlite3_reset(pDelete);
|
||||
}
|
||||
|
||||
return rc;
|
||||
|
@ -2323,46 +2336,52 @@ void sqlite3Fts3SegReaderFinish(
|
|||
** Otherwise, if successful, SQLITE_OK is returned. If an error occurs,
|
||||
** an SQLite error code is returned.
|
||||
*/
|
||||
static int fts3SegmentMerge(Fts3Table *p, int iLevel){
|
||||
static int fts3SegmentMerge(Fts3Table *p, int iIndex, int iLevel){
|
||||
int rc; /* Return code */
|
||||
int iIdx = 0; /* Index of new segment */
|
||||
int iNewLevel = 0; /* Level to create new segment at */
|
||||
int iNewLevel = 0; /* Level/index to create new segment at */
|
||||
SegmentWriter *pWriter = 0; /* Used to write the new, merged, segment */
|
||||
Fts3SegFilter filter; /* Segment term filter condition */
|
||||
Fts3SegReaderCursor csr; /* Cursor to iterate through level(s) */
|
||||
int bIgnoreEmpty = 0; /* True to ignore empty segments */
|
||||
|
||||
rc = sqlite3Fts3SegReaderCursor(p, iLevel, 0, 0, 1, 0, &csr);
|
||||
assert( iLevel==FTS3_SEGCURSOR_ALL
|
||||
|| iLevel==FTS3_SEGCURSOR_PENDING
|
||||
|| iLevel>=0
|
||||
);
|
||||
assert( iLevel<FTS3_SEGDIR_MAXLEVEL );
|
||||
assert( iIndex>=0 && iIndex<p->nIndex );
|
||||
|
||||
rc = sqlite3Fts3SegReaderCursor(p, iIndex, iLevel, 0, 0, 1, 0, &csr);
|
||||
if( rc!=SQLITE_OK || csr.nSegment==0 ) goto finished;
|
||||
|
||||
if( iLevel==FTS3_SEGCURSOR_ALL_TERM || iLevel==FTS3_SEGCURSOR_ALL_PREFIX ){
|
||||
if( iLevel==FTS3_SEGCURSOR_ALL ){
|
||||
/* This call is to merge all segments in the database to a single
|
||||
** segment. The level of the new segment is equal to the the numerically
|
||||
** greatest segment level currently present in the database. The index
|
||||
** of the new segment is always 0. */
|
||||
** greatest segment level currently present in the database for this
|
||||
** index. The idx of the new segment is always 0. */
|
||||
if( csr.nSegment==1 ){
|
||||
rc = SQLITE_DONE;
|
||||
goto finished;
|
||||
}
|
||||
rc = fts3SegmentMaxLevel(p, iLevel==FTS3_SEGCURSOR_ALL_PREFIX, &iNewLevel);
|
||||
rc = fts3SegmentMaxLevel(p, iIndex, &iNewLevel);
|
||||
bIgnoreEmpty = 1;
|
||||
|
||||
}else if( iLevel==FTS3_SEGCURSOR_PENDING ){
|
||||
iNewLevel = iIndex * FTS3_SEGDIR_MAXLEVEL;
|
||||
rc = fts3AllocateSegdirIdx(p, iIndex, 0, &iIdx);
|
||||
}else{
|
||||
/* This call is to merge all segments at level iLevel. find the next
|
||||
** available segment index at level iLevel+1. The call to
|
||||
** fts3AllocateSegdirIdx() will merge the segments at level iLevel+1 to
|
||||
** a single iLevel+2 segment if necessary. */
|
||||
if( iLevel==FTS3_SEGCURSOR_PENDING ){
|
||||
iNewLevel = 0;
|
||||
}else if( iLevel==FTS3_SEGCURSOR_PENDING_PREFIX ){
|
||||
iNewLevel = FTS3_SEGDIR_PREFIXLEVEL;
|
||||
}else{
|
||||
iNewLevel = iLevel+1;
|
||||
}
|
||||
rc = fts3AllocateSegdirIdx(p, iNewLevel, &iIdx);
|
||||
rc = fts3AllocateSegdirIdx(p, iIndex, iLevel+1, &iIdx);
|
||||
iNewLevel = iIndex * FTS3_SEGDIR_MAXLEVEL + iLevel+1;
|
||||
}
|
||||
if( rc!=SQLITE_OK ) goto finished;
|
||||
assert( csr.nSegment>0 );
|
||||
assert( iNewLevel>=0 );
|
||||
assert( iNewLevel>=(iIndex*FTS3_SEGDIR_MAXLEVEL) );
|
||||
assert( iNewLevel<((iIndex+1)*FTS3_SEGDIR_MAXLEVEL) );
|
||||
|
||||
memset(&filter, 0, sizeof(Fts3SegFilter));
|
||||
filter.flags = FTS3_SEGMENT_REQUIRE_POS;
|
||||
|
@ -2378,8 +2397,10 @@ static int fts3SegmentMerge(Fts3Table *p, int iLevel){
|
|||
if( rc!=SQLITE_OK ) goto finished;
|
||||
assert( pWriter );
|
||||
|
||||
rc = fts3DeleteSegdir(p, iLevel, csr.apSegment, csr.nSegment);
|
||||
if( rc!=SQLITE_OK ) goto finished;
|
||||
if( iLevel!=FTS3_SEGCURSOR_PENDING ){
|
||||
rc = fts3DeleteSegdir(p, iIndex, iLevel, csr.apSegment, csr.nSegment);
|
||||
if( rc!=SQLITE_OK ) goto finished;
|
||||
}
|
||||
rc = fts3SegWriterFlush(p, pWriter, iNewLevel, iIdx);
|
||||
|
||||
finished:
|
||||
|
@ -2390,19 +2411,16 @@ static int fts3SegmentMerge(Fts3Table *p, int iLevel){
|
|||
|
||||
|
||||
/*
|
||||
** Flush the contents of pendingTerms to a level 0 segment.
|
||||
** Flush the contents of pendingTerms to level 0 segments.
|
||||
*/
|
||||
int sqlite3Fts3PendingTermsFlush(Fts3Table *p){
|
||||
int rc = SQLITE_OK;
|
||||
if( p->bPrefix ){
|
||||
rc = fts3SegmentMerge(p, FTS3_SEGCURSOR_PENDING_PREFIX);
|
||||
}
|
||||
if( rc==SQLITE_OK || rc==SQLITE_DONE ){
|
||||
rc = fts3SegmentMerge(p, FTS3_SEGCURSOR_PENDING);
|
||||
}
|
||||
if( rc==SQLITE_DONE ){
|
||||
rc = SQLITE_OK;
|
||||
int i;
|
||||
for(i=0; rc==SQLITE_OK && i<p->nIndex; i++){
|
||||
rc = fts3SegmentMerge(p, i, FTS3_SEGCURSOR_PENDING);
|
||||
if( rc==SQLITE_DONE ) rc = SQLITE_OK;
|
||||
}
|
||||
sqlite3Fts3PendingTermsClear(p);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
@ -2554,6 +2572,23 @@ static void fts3UpdateDocTotals(
|
|||
sqlite3_free(a);
|
||||
}
|
||||
|
||||
static int fts3DoOptimize(Fts3Table *p, int bReturnDone){
|
||||
int i;
|
||||
int bSeenDone = 0;
|
||||
int rc = SQLITE_OK;
|
||||
for(i=0; rc==SQLITE_OK && i<p->nIndex; i++){
|
||||
rc = fts3SegmentMerge(p, i, FTS3_SEGCURSOR_ALL);
|
||||
if( rc==SQLITE_DONE ){
|
||||
bSeenDone = 1;
|
||||
rc = SQLITE_OK;
|
||||
}
|
||||
}
|
||||
sqlite3Fts3SegmentsClose(p);
|
||||
sqlite3Fts3PendingTermsClear(p);
|
||||
|
||||
return (rc==SQLITE_OK && bReturnDone && bSeenDone) ? SQLITE_DONE : rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Handle a 'special' INSERT of the form:
|
||||
**
|
||||
|
@ -2570,10 +2605,7 @@ static int fts3SpecialInsert(Fts3Table *p, sqlite3_value *pVal){
|
|||
if( !zVal ){
|
||||
return SQLITE_NOMEM;
|
||||
}else if( nVal==8 && 0==sqlite3_strnicmp(zVal, "optimize", 8) ){
|
||||
rc = fts3SegmentMerge(p, FTS3_SEGCURSOR_ALL_PREFIX);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = fts3SegmentMerge(p, FTS3_SEGCURSOR_ALL_TERM);
|
||||
}
|
||||
rc = fts3DoOptimize(p, 0);
|
||||
#ifdef SQLITE_TEST
|
||||
}else if( nVal>9 && 0==sqlite3_strnicmp(zVal, "nodesize=", 9) ){
|
||||
p->nNodeSize = atoi(&zVal[9]);
|
||||
|
@ -2586,9 +2618,6 @@ static int fts3SpecialInsert(Fts3Table *p, sqlite3_value *pVal){
|
|||
rc = SQLITE_ERROR;
|
||||
}
|
||||
|
||||
sqlite3Fts3SegmentsClose(p);
|
||||
sqlite3Fts3PendingTermsClear(p);
|
||||
sqlite3Fts3PendingPrefixesClear(p);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
@ -2905,30 +2934,19 @@ int sqlite3Fts3UpdateMethod(
|
|||
*/
|
||||
int sqlite3Fts3Optimize(Fts3Table *p){
|
||||
int rc;
|
||||
int bReturnDone = 0;
|
||||
rc = sqlite3_exec(p->db, "SAVEPOINT fts3", 0, 0, 0);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = fts3SegmentMerge(p, FTS3_SEGCURSOR_ALL_PREFIX);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = fts3SegmentMerge(p, FTS3_SEGCURSOR_ALL_TERM);
|
||||
}
|
||||
if( rc==SQLITE_DONE ){
|
||||
bReturnDone = 1;
|
||||
rc = SQLITE_OK;
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3_exec(p->db, "RELEASE fts3", 0, 0, 0);
|
||||
if( rc==SQLITE_OK ){
|
||||
sqlite3Fts3PendingTermsClear(p);
|
||||
sqlite3Fts3PendingPrefixesClear(p);
|
||||
}
|
||||
rc = fts3DoOptimize(p, 1);
|
||||
if( rc==SQLITE_OK || rc==SQLITE_DONE ){
|
||||
int rc2 = sqlite3_exec(p->db, "RELEASE fts3", 0, 0, 0);
|
||||
if( rc2!=SQLITE_OK ) rc = rc2;
|
||||
}else{
|
||||
sqlite3_exec(p->db, "ROLLBACK TO fts3", 0, 0, 0);
|
||||
sqlite3_exec(p->db, "RELEASE fts3", 0, 0, 0);
|
||||
}
|
||||
}
|
||||
sqlite3Fts3SegmentsClose(p);
|
||||
return ((rc==SQLITE_OK && bReturnDone) ? SQLITE_DONE : rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
25
manifest
25
manifest
|
@ -1,5 +1,5 @@
|
|||
C If\sthe\sfts4\soption\sprefix=1\sis\sspecified,\shave\sthe\sfts4\smodule\smaintain\san\sindex\sof\sprefixes\sas\swell\sas\sterms.
|
||||
D 2011-05-24T18:49:45.786
|
||||
C Change\sfts4\sso\sthat\sthe\sprefix=\sparameter\sis\spasses\sa\scomma-separated\slist\sof\sintegers.\sFor\seach\sinteger\sN,\sa\sseparate\sindex\sof\sall\sprefixes\sof\slength\sN\sbytes\sis\screated.
|
||||
D 2011-05-25T18:34:53.807
|
||||
F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
|
||||
F Makefile.in 11dcc00a8d0e5202def00e81732784fb0cc4fe1d
|
||||
F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
|
||||
|
@ -61,21 +61,21 @@ 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 4a48bfef342badba0a71bdeb5354edaa3ad83382
|
||||
F ext/fts3/fts3.c 9ebda4cd406e5aa234977caa97c2045d36048753
|
||||
F ext/fts3/fts3.h 3a10a0af180d502cecc50df77b1b22df142817fe
|
||||
F ext/fts3/fts3Int.h 02699211c0b6cf5aa713cc68c527c9a6e9159fbe
|
||||
F ext/fts3/fts3_aux.c d68d8e4d39e0342302d2c834618755af7c8058ea
|
||||
F ext/fts3/fts3Int.h deeeac21d17da06683a79b40ae119b93cf86f90a
|
||||
F ext/fts3/fts3_aux.c 2817a1ec9ffbad673cb1a1527ad807811bc7645b
|
||||
F ext/fts3/fts3_expr.c 5f49e0deaf723724b08100bb3ff40aab02ad0c93
|
||||
F ext/fts3/fts3_hash.c 3c8f6387a4a7f5305588b203fa7c887d753e1f1c
|
||||
F ext/fts3/fts3_hash.h 8331fb2206c609f9fc4c4735b9ab5ad6137c88ec
|
||||
F ext/fts3/fts3_icu.c ac494aed69835008185299315403044664bda295
|
||||
F ext/fts3/fts3_porter.c d61cfd81fb0fd8fbcb25adcaee0ba671aefaa5c2
|
||||
F ext/fts3/fts3_snippet.c 92b40397b28422c35c4127492d7ac6da34d1966a
|
||||
F ext/fts3/fts3_term.c cd226a311940b8ef414d5c1f7c74971a47cacedb
|
||||
F ext/fts3/fts3_term.c 0ade1812c0e97f394b58299810dfd5d2fb7ba501
|
||||
F ext/fts3/fts3_tokenizer.c 055f3dc7369585350b28db1ee0f3b214dca6724d
|
||||
F ext/fts3/fts3_tokenizer.h 13ffd9fcb397fec32a05ef5cd9e0fa659bf3dbd3
|
||||
F ext/fts3/fts3_tokenizer1.c 6e5cbaa588924ac578263a598e4fb9f5c9bb179d
|
||||
F ext/fts3/fts3_write.c 0fd6a55c774731852f889007fc6edb1b99819ee5
|
||||
F ext/fts3/fts3_write.c 416b367f56f3100314fa76615535178718c645e1
|
||||
F ext/fts3/fts3speed.tcl b54caf6a18d38174f1a6e84219950d85e98bb1e9
|
||||
F ext/fts3/mkfts3amal.tcl 252ecb7fe6467854f2aa237bf2c390b74e71f100
|
||||
F ext/icu/README.txt bf8461d8cdc6b8f514c080e4e10dc3b2bbdfefa9
|
||||
|
@ -473,7 +473,7 @@ F test/fts3fault2.test dc96203af6ba31ce20163fc35460e1556e8edf4d
|
|||
F test/fts3malloc.test 9c8cc3f885bb4dfc66d0460c52f68f45e4710d1b
|
||||
F test/fts3matchinfo.test cc0b009edbbf575283d5fdb53271179e0d8019ba
|
||||
F test/fts3near.test 2e318ee434d32babd27c167142e2b94ddbab4844
|
||||
F test/fts3prefix.test 5b4e08c63d5d4a79e54754dc6b2209b03c885200
|
||||
F test/fts3prefix.test c51b04f38ce232cd0c9edd3cc60dc981db5e2726
|
||||
F test/fts3query.test ef79d31fdb355d094baec1c1b24b60439a1fb8a2
|
||||
F test/fts3rnd.test 2b1a579be557ab8ac54a51b39caa4aa8043cc4ad
|
||||
F test/fts3shared.test 8bb266521d7c5495c0ae522bb4d376ad5387d4a2
|
||||
|
@ -939,10 +939,7 @@ F tool/speedtest8.c 2902c46588c40b55661e471d7a86e4dd71a18224
|
|||
F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e
|
||||
F tool/split-sqlite3c.tcl d9be87f1c340285a3e081eb19b4a247981ed290c
|
||||
F tool/vdbe-compress.tcl d70ea6d8a19e3571d7ab8c9b75cba86d1173ff0f
|
||||
P 651ef24249d8c22c4f13e4c0bb98a60099cfd23a
|
||||
R c499b030bb5d74c4afca730dec7a05bc
|
||||
T *branch * fts3-prefix-search
|
||||
T *sym-fts3-prefix-search *
|
||||
T -sym-trunk *
|
||||
P b5bdc639898ee22eebedeb560810e94e74de8aa4
|
||||
R e9f427c5bc714eae400763155f710174
|
||||
U dan
|
||||
Z 0c60a0f2885eb6df2e24e22407faffbe
|
||||
Z 6f0f673a9e9eaf0f72d887565c9f2227
|
||||
|
|
|
@ -1 +1 @@
|
|||
b5bdc639898ee22eebedeb560810e94e74de8aa4
|
||||
be59bf49402d2e2f4b95fb6668849f3745cb7bf2
|
|
@ -16,56 +16,75 @@ set testdir [file dirname $argv0]
|
|||
source $testdir/tester.tcl
|
||||
set testprefix fts3prefix
|
||||
|
||||
ifcapable !fts3 {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
# This proc tests that the prefixes index appears to represent the same content
|
||||
# as the terms index.
|
||||
#
|
||||
proc fts3_terms_and_prefixes {db tbl} {
|
||||
$db eval "CREATE VIRTUAL TABLE fts3check1 USING fts4term($tbl);"
|
||||
$db eval "CREATE VIRTUAL TABLE fts3check2 USING fts4prefix($tbl);"
|
||||
proc fts3_terms_and_prefixes {db tbl prefixlengths} {
|
||||
|
||||
$db eval {
|
||||
CREATE TEMP TABLE terms AS SELECT * FROM fts3check1;
|
||||
CREATE TEMP TABLE prefixes AS SELECT * FROM fts3check2;
|
||||
CREATE INDEX temp.idx ON prefixes(term);
|
||||
DROP TABLE fts3check1;
|
||||
DROP TABLE fts3check2;
|
||||
}
|
||||
set iIndex 0
|
||||
foreach len $prefixlengths {
|
||||
incr iIndex
|
||||
$db eval {
|
||||
DROP TABLE IF EXISTS fts3check1;
|
||||
DROP TABLE IF EXISTS fts3check2;
|
||||
}
|
||||
$db eval "CREATE VIRTUAL TABLE fts3check1 USING fts4term($tbl, 0);"
|
||||
$db eval "CREATE VIRTUAL TABLE fts3check2 USING fts4term($tbl, $iIndex);"
|
||||
|
||||
$db eval { SELECT term, docid, col, pos FROM temp.terms } a {
|
||||
set nMax [expr [string length $a(term)] - 1]
|
||||
if {$nMax>8} {set nMax 8}
|
||||
for {set n 0} {$n < $nMax} {incr n} {
|
||||
set t [string range $a(term) 0 $n]
|
||||
$db eval {
|
||||
DROP TABLE IF EXISTS temp.terms;
|
||||
DROP TABLE IF EXISTS temp.prefixes;
|
||||
CREATE TEMP TABLE terms AS SELECT * FROM fts3check1;
|
||||
CREATE TEMP TABLE prefixes AS SELECT * FROM fts3check2;
|
||||
CREATE INDEX temp.idx ON prefixes(term);
|
||||
DROP TABLE fts3check1;
|
||||
DROP TABLE fts3check2;
|
||||
}
|
||||
|
||||
set nExpect 0
|
||||
$db eval { SELECT term, docid, col, pos FROM temp.terms } a {
|
||||
if {[string length $a(term)]<$len} continue
|
||||
incr nExpect
|
||||
set prefix [string range $a(term) 0 [expr $len-1]]
|
||||
set r [$db one {
|
||||
SELECT count(*) FROM temp.prefixes WHERE
|
||||
term = $t AND docid = $a(docid) AND col = $a(col) AND pos = $a(pos)
|
||||
term = $prefix AND docid = $a(docid) AND col = $a(col) AND pos = $a(pos)
|
||||
}]
|
||||
if {$r != 1} {
|
||||
error "$t, $a(docid), $a(col), $a(pos)"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
execsql { DROP TABLE temp.prefixes }
|
||||
execsql { DROP TABLE temp.terms }
|
||||
set nCount [$db one {SELECT count(*) FROM temp.prefixes}]
|
||||
if {$nCount != $nExpect} {
|
||||
error "prefixes.count(*) is $nCount expected $nExpect"
|
||||
}
|
||||
|
||||
execsql { DROP TABLE temp.prefixes }
|
||||
execsql { DROP TABLE temp.terms }
|
||||
|
||||
set terms_layout [$db eval "
|
||||
SELECT level, idx FROM ${tbl}_segdir WHERE level < 1024 ORDER by 1, 2
|
||||
"]
|
||||
set prefixes_layout [$db eval "
|
||||
SELECT level-1024, idx FROM ${tbl}_segdir WHERE level >= 1024 ORDER by 1, 2
|
||||
"]
|
||||
set list [list]
|
||||
$db eval "
|
||||
SELECT sum( 1 << (16*(level%1024)) ) AS total, (level/1024) AS tree
|
||||
FROM ${tbl}_segdir GROUP BY tree
|
||||
" {
|
||||
lappend list [list $total $tree]
|
||||
}
|
||||
|
||||
if {$terms_layout != $prefixes_layout} {
|
||||
puts "TERMS LAYOUT: $terms_layout"
|
||||
puts "PREFIX LAYOUT: $prefixes_layout"
|
||||
error "Terms and prefixes are comprised of different b-trees"
|
||||
if { [lsort -integer -index 0 $list] != [lsort -integer -index 1 $list] } {
|
||||
error "inconsistent tree structures: $list"
|
||||
}
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
proc fts3_tap_test {tn db tbl} {
|
||||
uplevel [list do_test $tn [list fts3_terms_and_prefixes $db $tbl] ""]
|
||||
proc fts3_tap_test {tn db tbl lens} {
|
||||
uplevel [list do_test $tn [list fts3_terms_and_prefixes $db $tbl $lens] ""]
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
|
@ -73,21 +92,24 @@ proc fts3_tap_test {tn db tbl} {
|
|||
# being constructed correctly for the simplest possible case.
|
||||
#
|
||||
do_execsql_test 1.1 {
|
||||
CREATE VIRTUAL TABLE t1 USING fts4(prefix=1);
|
||||
CREATE VIRTUAL TABLE prefixes USING fts4prefix(t1);
|
||||
CREATE VIRTUAL TABLE t1 USING fts4(prefix='1,3,6');
|
||||
|
||||
CREATE VIRTUAL TABLE p1 USING fts4term(t1, 1);
|
||||
CREATE VIRTUAL TABLE p2 USING fts4term(t1, 2);
|
||||
CREATE VIRTUAL TABLE p3 USING fts4term(t1, 3);
|
||||
CREATE VIRTUAL TABLE terms USING fts4term(t1);
|
||||
}
|
||||
do_execsql_test 1.2 {
|
||||
INSERT INTO t1 VALUES('sqlite mysql firebird');
|
||||
}
|
||||
do_execsql_test 1.3 {
|
||||
SELECT term FROM prefixes;
|
||||
} {f fi fir fire fireb firebi firebir firebird m my mys mysq mysql s sq sql sqli sqlit sqlite}
|
||||
do_execsql_test 1.3.1 { SELECT term FROM p1 } {f m s}
|
||||
do_execsql_test 1.3.2 { SELECT term FROM p2 } {fir mys sql}
|
||||
do_execsql_test 1.3.3 { SELECT term FROM p3 } {firebi sqlite}
|
||||
do_execsql_test 1.4 {
|
||||
SELECT term FROM terms;
|
||||
} {firebird mysql sqlite}
|
||||
|
||||
fts3_tap_test 1.5 db t1
|
||||
fts3_tap_test 1.5 db t1 {1 3 6}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# A slightly more complicated dataset. This test also verifies that DELETE
|
||||
|
@ -115,15 +137,15 @@ do_execsql_test 2.1 {
|
|||
INSERT INTO t1 VALUES('Google. It is now developed and maintained as part');
|
||||
INSERT INTO t1 VALUES('of SQLite. ');
|
||||
}
|
||||
fts3_tap_test 2.2 db t1
|
||||
fts3_tap_test 2.2 db t1 {1 3 6}
|
||||
do_execsql_test 2.3 { DELETE FROM t1 WHERE docid%2; }
|
||||
fts3_tap_test 2.4 db t1
|
||||
fts3_tap_test 2.4 db t1 {1 3 6}
|
||||
|
||||
do_execsql_test 2.5 { INSERT INTO t1(t1) VALUES('optimize') }
|
||||
fts3_tap_test 2.6 db t1
|
||||
fts3_tap_test 2.6 db t1 {1 3 6}
|
||||
|
||||
do_execsql_test 3.1 {
|
||||
CREATE VIRTUAL TABLE t2 USING fts4(prefix=1);
|
||||
CREATE VIRTUAL TABLE t2 USING fts4(prefix='1,2,3');
|
||||
INSERT INTO t2 VALUES('On 12 September the wind direction turned and');
|
||||
INSERT INTO t2 VALUES('William''s fleet sailed. A storm blew up and the');
|
||||
INSERT INTO t2 VALUES('fleet was forced to take shelter at');
|
||||
|
@ -148,16 +170,16 @@ do_execsql_test 3.1 {
|
|||
INSERT INTO t2 VALUES('and waited for Harold''s return from the north.');
|
||||
}
|
||||
|
||||
fts3_tap_test 3.2 db t2
|
||||
fts3_tap_test 3.2 db t2 {1 2 3}
|
||||
do_execsql_test 3.3 { SELECT optimize(t2) FROM t2 LIMIT 1 } {{Index optimized}}
|
||||
fts3_tap_test 3.4 db t2
|
||||
fts3_tap_test 3.4 db t2 {1 2 3}
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Simple tests for reading the prefix-index.
|
||||
#
|
||||
do_execsql_test 4.1 {
|
||||
CREATE VIRTUAL TABLE t3 USING fts4(prefix=1);
|
||||
CREATE VIRTUAL TABLE t3 USING fts4(prefix="1,4");
|
||||
INSERT INTO t3 VALUES('one two three');
|
||||
INSERT INTO t3 VALUES('four five six');
|
||||
INSERT INTO t3 VALUES('seven eight nine');
|
||||
|
|
Loading…
Reference in New Issue