Add the "detail" option to fts5. Used to reduce the amount of information stored in an fts5 index.
FossilOrigin-Name: a220e85fe535af5ef2da6ef5fb76abe5a96b5abf
This commit is contained in:
commit
8656b24144
@ -110,6 +110,12 @@ struct Fts5PhraseIter {
|
||||
** should be greater than or equal to zero and smaller than the value
|
||||
** output by xInstCount().
|
||||
**
|
||||
** Usually, output parameter *piPhrase is set to the phrase number, *piCol
|
||||
** to the column in which it occurs and *piOff the token offset of the
|
||||
** first token of the phrase. The exception is if the table was created
|
||||
** with the offsets=0 option specified. In this case *piOff is always
|
||||
** set to -1.
|
||||
**
|
||||
** Returns SQLITE_OK if successful, or an error code (i.e. SQLITE_NOMEM)
|
||||
** if an error occurs.
|
||||
**
|
||||
@ -196,7 +202,7 @@ struct Fts5PhraseIter {
|
||||
** Fts5PhraseIter iter;
|
||||
** int iCol, iOff;
|
||||
** for(pApi->xPhraseFirst(pFts, iPhrase, &iter, &iCol, &iOff);
|
||||
** iOff>=0;
|
||||
** iCol>=0;
|
||||
** pApi->xPhraseNext(pFts, &iter, &iCol, &iOff)
|
||||
** ){
|
||||
** // An instance of phrase iPhrase at offset iOff of column iCol
|
||||
@ -210,7 +216,7 @@ struct Fts5PhraseIter {
|
||||
** See xPhraseFirst above.
|
||||
*/
|
||||
struct Fts5ExtensionApi {
|
||||
int iVersion; /* Currently always set to 1 */
|
||||
int iVersion; /* Currently always set to 3 */
|
||||
|
||||
void *(*xUserData)(Fts5Context*);
|
||||
|
||||
@ -240,8 +246,11 @@ struct Fts5ExtensionApi {
|
||||
int (*xSetAuxdata)(Fts5Context*, void *pAux, void(*xDelete)(void*));
|
||||
void *(*xGetAuxdata)(Fts5Context*, int bClear);
|
||||
|
||||
void (*xPhraseFirst)(Fts5Context*, int iPhrase, Fts5PhraseIter*, int*, int*);
|
||||
int (*xPhraseFirst)(Fts5Context*, int iPhrase, Fts5PhraseIter*, int*, int*);
|
||||
void (*xPhraseNext)(Fts5Context*, Fts5PhraseIter*, int *piCol, int *piOff);
|
||||
|
||||
int (*xPhraseFirstColumn)(Fts5Context*, int iPhrase, Fts5PhraseIter*, int*);
|
||||
void (*xPhraseNextColumn)(Fts5Context*, Fts5PhraseIter*, int *piCol);
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -151,6 +151,7 @@ struct Fts5Config {
|
||||
char *zContent; /* content table */
|
||||
char *zContentRowid; /* "content_rowid=" option value */
|
||||
int bColumnsize; /* "columnsize=" option value (dflt==1) */
|
||||
int eDetail; /* FTS5_DETAIL_XXX value */
|
||||
char *zContentExprlist;
|
||||
Fts5Tokenizer *pTok;
|
||||
fts5_tokenizer *pTokApi;
|
||||
@ -179,6 +180,9 @@ struct Fts5Config {
|
||||
#define FTS5_CONTENT_NONE 1
|
||||
#define FTS5_CONTENT_EXTERNAL 2
|
||||
|
||||
#define FTS5_DETAIL_FULL 0
|
||||
#define FTS5_DETAIL_NONE 1
|
||||
#define FTS5_DETAIL_COLUMNS 2
|
||||
|
||||
|
||||
|
||||
@ -292,6 +296,13 @@ char *sqlite3Fts5Strndup(int *pRc, const char *pIn, int nIn);
|
||||
/* Character set tests (like isspace(), isalpha() etc.) */
|
||||
int sqlite3Fts5IsBareword(char t);
|
||||
|
||||
|
||||
/* Bucket of terms object used by the integrity-check in offsets=0 mode. */
|
||||
typedef struct Fts5Termset Fts5Termset;
|
||||
int sqlite3Fts5TermsetNew(Fts5Termset**);
|
||||
int sqlite3Fts5TermsetAdd(Fts5Termset*, int, const char*, int, int *pbPresent);
|
||||
void sqlite3Fts5TermsetFree(Fts5Termset*);
|
||||
|
||||
/*
|
||||
** End of interface to code in fts5_buffer.c.
|
||||
**************************************************************************/
|
||||
@ -436,6 +447,8 @@ int sqlite3Fts5IndexMerge(Fts5Index *p, int nMerge);
|
||||
|
||||
int sqlite3Fts5IndexLoadConfig(Fts5Index *p);
|
||||
|
||||
int sqlite3Fts5IterCollist(Fts5IndexIter*, const u8 **, int*);
|
||||
|
||||
/*
|
||||
** End of interface to code in fts5_index.c.
|
||||
**************************************************************************/
|
||||
@ -492,7 +505,7 @@ typedef struct Fts5Hash Fts5Hash;
|
||||
/*
|
||||
** Create a hash table, free a hash table.
|
||||
*/
|
||||
int sqlite3Fts5HashNew(Fts5Hash**, int *pnSize);
|
||||
int sqlite3Fts5HashNew(Fts5Config*, Fts5Hash**, int *pnSize);
|
||||
void sqlite3Fts5HashFree(Fts5Hash*);
|
||||
|
||||
int sqlite3Fts5HashWrite(
|
||||
@ -629,8 +642,18 @@ int sqlite3Fts5ExprPhraseCount(Fts5Expr*);
|
||||
int sqlite3Fts5ExprPhraseSize(Fts5Expr*, int iPhrase);
|
||||
int sqlite3Fts5ExprPoslist(Fts5Expr*, int, const u8 **);
|
||||
|
||||
typedef struct Fts5PoslistPopulator Fts5PoslistPopulator;
|
||||
Fts5PoslistPopulator *sqlite3Fts5ExprClearPoslists(Fts5Expr*, int);
|
||||
int sqlite3Fts5ExprPopulatePoslists(
|
||||
Fts5Config*, Fts5Expr*, Fts5PoslistPopulator*, int, const char*, int
|
||||
);
|
||||
void sqlite3Fts5ExprCheckPoslists(Fts5Expr*, i64);
|
||||
void sqlite3Fts5ExprClearEof(Fts5Expr*);
|
||||
|
||||
int sqlite3Fts5ExprClonePhrase(Fts5Config*, Fts5Expr*, int, Fts5Expr**);
|
||||
|
||||
int sqlite3Fts5ExprPhraseCollist(Fts5Expr *, int, const u8 **, int *);
|
||||
|
||||
/*******************************************
|
||||
** The fts5_expr.c API above this point is used by the other hand-written
|
||||
** C code in this module. The interfaces below this point are called by
|
||||
|
@ -292,3 +292,86 @@ int sqlite3Fts5IsBareword(char t){
|
||||
}
|
||||
|
||||
|
||||
/*************************************************************************
|
||||
*/
|
||||
typedef struct Fts5TermsetEntry Fts5TermsetEntry;
|
||||
struct Fts5TermsetEntry {
|
||||
char *pTerm;
|
||||
int nTerm;
|
||||
int iIdx; /* Index (main or aPrefix[] entry) */
|
||||
Fts5TermsetEntry *pNext;
|
||||
};
|
||||
|
||||
struct Fts5Termset {
|
||||
Fts5TermsetEntry *apHash[512];
|
||||
};
|
||||
|
||||
int sqlite3Fts5TermsetNew(Fts5Termset **pp){
|
||||
int rc = SQLITE_OK;
|
||||
*pp = sqlite3Fts5MallocZero(&rc, sizeof(Fts5Termset));
|
||||
return rc;
|
||||
}
|
||||
|
||||
int sqlite3Fts5TermsetAdd(
|
||||
Fts5Termset *p,
|
||||
int iIdx,
|
||||
const char *pTerm, int nTerm,
|
||||
int *pbPresent
|
||||
){
|
||||
int rc = SQLITE_OK;
|
||||
*pbPresent = 0;
|
||||
if( p ){
|
||||
int i;
|
||||
int hash;
|
||||
Fts5TermsetEntry *pEntry;
|
||||
|
||||
/* Calculate a hash value for this term */
|
||||
hash = 104 + iIdx;
|
||||
for(i=0; i<nTerm; i++){
|
||||
hash += (hash << 3) + (int)pTerm[i];
|
||||
}
|
||||
hash = hash % ArraySize(p->apHash);
|
||||
|
||||
for(pEntry=p->apHash[hash]; pEntry; pEntry=pEntry->pNext){
|
||||
if( pEntry->iIdx==iIdx
|
||||
&& pEntry->nTerm==nTerm
|
||||
&& memcmp(pEntry->pTerm, pTerm, nTerm)==0
|
||||
){
|
||||
*pbPresent = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if( pEntry==0 ){
|
||||
pEntry = sqlite3Fts5MallocZero(&rc, sizeof(Fts5TermsetEntry) + nTerm);
|
||||
if( pEntry ){
|
||||
pEntry->pTerm = (char*)&pEntry[1];
|
||||
pEntry->nTerm = nTerm;
|
||||
pEntry->iIdx = iIdx;
|
||||
memcpy(pEntry->pTerm, pTerm, nTerm);
|
||||
pEntry->pNext = p->apHash[hash];
|
||||
p->apHash[hash] = pEntry;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
void sqlite3Fts5TermsetFree(Fts5Termset *p){
|
||||
if( p ){
|
||||
int i;
|
||||
for(i=0; i<ArraySize(p->apHash); i++){
|
||||
Fts5TermsetEntry *pEntry = p->apHash[i];
|
||||
while( pEntry ){
|
||||
Fts5TermsetEntry *pDel = pEntry;
|
||||
pEntry = pEntry->pNext;
|
||||
sqlite3_free(pDel);
|
||||
}
|
||||
}
|
||||
sqlite3_free(p);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
@ -14,7 +14,6 @@
|
||||
*/
|
||||
|
||||
|
||||
|
||||
#include "fts5Int.h"
|
||||
|
||||
#define FTS5_DEFAULT_PAGE_SIZE 4050
|
||||
@ -195,6 +194,33 @@ void sqlite3Fts5Dequote(char *z){
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
struct Fts5Enum {
|
||||
const char *zName;
|
||||
int eVal;
|
||||
};
|
||||
typedef struct Fts5Enum Fts5Enum;
|
||||
|
||||
static int fts5ConfigSetEnum(
|
||||
const Fts5Enum *aEnum,
|
||||
const char *zEnum,
|
||||
int *peVal
|
||||
){
|
||||
int nEnum = strlen(zEnum);
|
||||
int i;
|
||||
int iVal = -1;
|
||||
|
||||
for(i=0; aEnum[i].zName; i++){
|
||||
if( sqlite3_strnicmp(aEnum[i].zName, zEnum, nEnum)==0 ){
|
||||
if( iVal>=0 ) return SQLITE_ERROR;
|
||||
iVal = aEnum[i].eVal;
|
||||
}
|
||||
}
|
||||
|
||||
*peVal = iVal;
|
||||
return iVal<0 ? SQLITE_ERROR : SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Parse a "special" CREATE VIRTUAL TABLE directive and update
|
||||
** configuration object pConfig as appropriate.
|
||||
@ -345,6 +371,20 @@ static int fts5ConfigParseSpecial(
|
||||
return rc;
|
||||
}
|
||||
|
||||
if( sqlite3_strnicmp("detail", zCmd, nCmd)==0 ){
|
||||
const Fts5Enum aDetail[] = {
|
||||
{ "none", FTS5_DETAIL_NONE },
|
||||
{ "full", FTS5_DETAIL_FULL },
|
||||
{ "columns", FTS5_DETAIL_COLUMNS },
|
||||
{ 0, 0 }
|
||||
};
|
||||
|
||||
if( rc = fts5ConfigSetEnum(aDetail, zArg, &pConfig->eDetail) ){
|
||||
*pzErr = sqlite3_mprintf("malformed detail=... directive");
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
*pzErr = sqlite3_mprintf("unrecognized option: \"%.*s\"", nCmd, zCmd);
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
@ -500,6 +540,7 @@ int sqlite3Fts5ConfigParse(
|
||||
pRet->zDb = sqlite3Fts5Strndup(&rc, azArg[1], -1);
|
||||
pRet->zName = sqlite3Fts5Strndup(&rc, azArg[2], -1);
|
||||
pRet->bColumnsize = 1;
|
||||
pRet->eDetail = FTS5_DETAIL_FULL;
|
||||
#ifdef SQLITE_DEBUG
|
||||
pRet->bPrefixIndex = 1;
|
||||
#endif
|
||||
|
@ -40,6 +40,7 @@ void sqlite3Fts5ParserTrace(FILE*, char*);
|
||||
|
||||
struct Fts5Expr {
|
||||
Fts5Index *pIndex;
|
||||
Fts5Config *pConfig;
|
||||
Fts5ExprNode *pRoot;
|
||||
int bDesc; /* Iterate in descending rowid order */
|
||||
int nPhrase; /* Number of phrases in expression */
|
||||
@ -235,6 +236,7 @@ int sqlite3Fts5ExprNew(
|
||||
}else{
|
||||
pNew->pRoot = sParse.pExpr;
|
||||
pNew->pIndex = 0;
|
||||
pNew->pConfig = pConfig;
|
||||
pNew->apExprPhrase = sParse.apPhrase;
|
||||
pNew->nPhrase = sParse.nPhrase;
|
||||
sParse.apPhrase = 0;
|
||||
@ -299,8 +301,9 @@ static i64 fts5ExprSynonymRowid(Fts5ExprTerm *pTerm, int bDesc, int *pbEof){
|
||||
/*
|
||||
** Argument pTerm must be a synonym iterator.
|
||||
*/
|
||||
static int fts5ExprSynonymPoslist(
|
||||
static int fts5ExprSynonymList(
|
||||
Fts5ExprTerm *pTerm,
|
||||
int bCollist,
|
||||
Fts5Colset *pColset,
|
||||
i64 iRowid,
|
||||
int *pbDel, /* OUT: Caller should sqlite3_free(*pa) */
|
||||
@ -319,9 +322,16 @@ static int fts5ExprSynonymPoslist(
|
||||
if( sqlite3Fts5IterEof(pIter)==0 && sqlite3Fts5IterRowid(pIter)==iRowid ){
|
||||
const u8 *a;
|
||||
int n;
|
||||
i64 dummy;
|
||||
rc = sqlite3Fts5IterPoslist(pIter, pColset, &a, &n, &dummy);
|
||||
|
||||
if( bCollist ){
|
||||
rc = sqlite3Fts5IterCollist(pIter, &a, &n);
|
||||
}else{
|
||||
i64 dummy;
|
||||
rc = sqlite3Fts5IterPoslist(pIter, pColset, &a, &n, &dummy);
|
||||
}
|
||||
|
||||
if( rc!=SQLITE_OK ) goto synonym_poslist_out;
|
||||
if( n==0 ) continue;
|
||||
if( nIter==nAlloc ){
|
||||
int nByte = sizeof(Fts5PoslistReader) * nAlloc * 2;
|
||||
Fts5PoslistReader *aNew = (Fts5PoslistReader*)sqlite3_malloc(nByte);
|
||||
@ -422,8 +432,8 @@ static int fts5ExprPhraseIsMatch(
|
||||
int bFlag = 0;
|
||||
const u8 *a = 0;
|
||||
if( pTerm->pSynonym ){
|
||||
rc = fts5ExprSynonymPoslist(
|
||||
pTerm, pColset, pNode->iRowid, &bFlag, (u8**)&a, &n
|
||||
rc = fts5ExprSynonymList(
|
||||
pTerm, 0, pColset, pNode->iRowid, &bFlag, (u8**)&a, &n
|
||||
);
|
||||
}else{
|
||||
rc = sqlite3Fts5IterPoslist(pTerm->pIter, pColset, &a, &n, &dummy);
|
||||
@ -757,30 +767,51 @@ static int fts5ExprNearTest(
|
||||
){
|
||||
Fts5ExprNearset *pNear = pNode->pNear;
|
||||
int rc = *pRc;
|
||||
int i;
|
||||
|
||||
/* Check that each phrase in the nearset matches the current row.
|
||||
** Populate the pPhrase->poslist buffers at the same time. If any
|
||||
** phrase is not a match, break out of the loop early. */
|
||||
for(i=0; rc==SQLITE_OK && i<pNear->nPhrase; i++){
|
||||
Fts5ExprPhrase *pPhrase = pNear->apPhrase[i];
|
||||
if( pPhrase->nTerm>1 || pPhrase->aTerm[0].pSynonym || pNear->pColset ){
|
||||
int bMatch = 0;
|
||||
rc = fts5ExprPhraseIsMatch(pNode, pNear->pColset, pPhrase, &bMatch);
|
||||
if( bMatch==0 ) break;
|
||||
}else{
|
||||
rc = sqlite3Fts5IterPoslistBuffer(
|
||||
pPhrase->aTerm[0].pIter, &pPhrase->poslist
|
||||
);
|
||||
if( pExpr->pConfig->eDetail!=FTS5_DETAIL_FULL ){
|
||||
Fts5ExprTerm *pTerm;
|
||||
Fts5ExprPhrase *pPhrase = pNear->apPhrase[0];
|
||||
pPhrase->poslist.n = 0;
|
||||
for(pTerm=&pPhrase->aTerm[0]; pTerm; pTerm=pTerm->pSynonym){
|
||||
Fts5IndexIter *pIter = pTerm->pIter;
|
||||
if( sqlite3Fts5IterEof(pIter)==0 ){
|
||||
int n;
|
||||
i64 iRowid;
|
||||
rc = sqlite3Fts5IterPoslist(pIter, pNear->pColset, 0, &n, &iRowid);
|
||||
if( rc!=SQLITE_OK ){
|
||||
*pRc = rc;
|
||||
return 0;
|
||||
}else if( iRowid==pNode->iRowid && n>0 ){
|
||||
pPhrase->poslist.n = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return pPhrase->poslist.n;
|
||||
}else{
|
||||
int i;
|
||||
|
||||
*pRc = rc;
|
||||
if( i==pNear->nPhrase && (i==1 || fts5ExprNearIsMatch(pRc, pNear)) ){
|
||||
return 1;
|
||||
}
|
||||
/* Check that each phrase in the nearset matches the current row.
|
||||
** Populate the pPhrase->poslist buffers at the same time. If any
|
||||
** phrase is not a match, break out of the loop early. */
|
||||
for(i=0; rc==SQLITE_OK && i<pNear->nPhrase; i++){
|
||||
Fts5ExprPhrase *pPhrase = pNear->apPhrase[i];
|
||||
if( pPhrase->nTerm>1 || pPhrase->aTerm[0].pSynonym || pNear->pColset ){
|
||||
int bMatch = 0;
|
||||
rc = fts5ExprPhraseIsMatch(pNode, pNear->pColset, pPhrase, &bMatch);
|
||||
if( bMatch==0 ) break;
|
||||
}else{
|
||||
rc = sqlite3Fts5IterPoslistBuffer(
|
||||
pPhrase->aTerm[0].pIter, &pPhrase->poslist
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
*pRc = rc;
|
||||
if( i==pNear->nPhrase && (i==1 || fts5ExprNearIsMatch(pRc, pNear)) ){
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int fts5ExprTokenTest(
|
||||
@ -1218,6 +1249,9 @@ static int fts5ExprNodeNextMatch(
|
||||
}
|
||||
pNode->bEof = p1->bEof;
|
||||
pNode->iRowid = p1->iRowid;
|
||||
if( p1->bEof ){
|
||||
fts5ExprNodeZeroPoslist(p2);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -1603,6 +1637,7 @@ int sqlite3Fts5ExprClonePhrase(
|
||||
if( rc==SQLITE_OK ){
|
||||
/* All the allocations succeeded. Put the expression object together. */
|
||||
pNew->pIndex = pExpr->pIndex;
|
||||
pNew->pConfig = pExpr->pConfig;
|
||||
pNew->nPhrase = 1;
|
||||
pNew->apExprPhrase[0] = sCtx.pPhrase;
|
||||
pNew->pRoot->pNear->apPhrase[0] = sCtx.pPhrase;
|
||||
@ -1744,6 +1779,15 @@ void sqlite3Fts5ParseSetColset(
|
||||
Fts5ExprNearset *pNear,
|
||||
Fts5Colset *pColset
|
||||
){
|
||||
if( pParse->pConfig->eDetail==FTS5_DETAIL_NONE ){
|
||||
pParse->rc = SQLITE_ERROR;
|
||||
pParse->zErr = sqlite3_mprintf(
|
||||
"fts5: column queries are not supported (detail=none)"
|
||||
);
|
||||
sqlite3_free(pColset);
|
||||
return;
|
||||
}
|
||||
|
||||
if( pNear ){
|
||||
pNear->pColset = pColset;
|
||||
}else{
|
||||
@ -1805,11 +1849,20 @@ Fts5ExprNode *sqlite3Fts5ParseNode(
|
||||
for(iPhrase=0; iPhrase<pNear->nPhrase; iPhrase++){
|
||||
pNear->apPhrase[iPhrase]->pNode = pRet;
|
||||
}
|
||||
if( pNear->nPhrase==1
|
||||
&& pNear->apPhrase[0]->nTerm==1
|
||||
&& pNear->apPhrase[0]->aTerm[0].pSynonym==0
|
||||
){
|
||||
pRet->eType = FTS5_TERM;
|
||||
if( pNear->nPhrase==1 && pNear->apPhrase[0]->nTerm==1 ){
|
||||
if( pNear->apPhrase[0]->aTerm[0].pSynonym==0 ){
|
||||
pRet->eType = FTS5_TERM;
|
||||
}
|
||||
}else if( pParse->pConfig->eDetail!=FTS5_DETAIL_FULL ){
|
||||
assert( pParse->rc==SQLITE_OK );
|
||||
pParse->rc = SQLITE_ERROR;
|
||||
assert( pParse->zErr==0 );
|
||||
pParse->zErr = sqlite3_mprintf(
|
||||
"fts5: %s queries are not supported (detail!=full)",
|
||||
pNear->nPhrase==1 ? "phrase": "NEAR"
|
||||
);
|
||||
sqlite3_free(pRet);
|
||||
pRet = 0;
|
||||
}
|
||||
}else{
|
||||
fts5ExprAddChildren(pRet, pLeft);
|
||||
@ -1923,6 +1976,9 @@ static char *fts5ExprPrintTcl(
|
||||
for(iTerm=0; zRet && iTerm<pPhrase->nTerm; iTerm++){
|
||||
char *zTerm = pPhrase->aTerm[iTerm].zTerm;
|
||||
zRet = fts5PrintfAppend(zRet, "%s%s", iTerm==0?"":" ", zTerm);
|
||||
if( pPhrase->aTerm[iTerm].bPrefix ){
|
||||
zRet = fts5PrintfAppend(zRet, "*");
|
||||
}
|
||||
}
|
||||
|
||||
if( zRet ) zRet = fts5PrintfAppend(zRet, "}");
|
||||
@ -2235,3 +2291,229 @@ int sqlite3Fts5ExprPoslist(Fts5Expr *pExpr, int iPhrase, const u8 **pa){
|
||||
}
|
||||
return nRet;
|
||||
}
|
||||
|
||||
struct Fts5PoslistPopulator {
|
||||
Fts5PoslistWriter writer;
|
||||
int bOk; /* True if ok to populate */
|
||||
int bMiss;
|
||||
};
|
||||
|
||||
Fts5PoslistPopulator *sqlite3Fts5ExprClearPoslists(Fts5Expr *pExpr, int bLive){
|
||||
Fts5PoslistPopulator *pRet;
|
||||
pRet = sqlite3_malloc(sizeof(Fts5PoslistPopulator)*pExpr->nPhrase);
|
||||
if( pRet ){
|
||||
int i;
|
||||
memset(pRet, 0, sizeof(Fts5PoslistPopulator)*pExpr->nPhrase);
|
||||
for(i=0; i<pExpr->nPhrase; i++){
|
||||
Fts5Buffer *pBuf = &pExpr->apExprPhrase[i]->poslist;
|
||||
Fts5ExprNode *pNode = pExpr->apExprPhrase[i]->pNode;
|
||||
assert( pExpr->apExprPhrase[i]->nTerm==1 );
|
||||
if( bLive &&
|
||||
(pBuf->n==0 || pNode->iRowid!=pExpr->pRoot->iRowid || pNode->bEof)
|
||||
){
|
||||
pRet[i].bMiss = 1;
|
||||
}else{
|
||||
pBuf->n = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
return pRet;
|
||||
}
|
||||
|
||||
struct Fts5ExprCtx {
|
||||
Fts5Expr *pExpr;
|
||||
Fts5PoslistPopulator *aPopulator;
|
||||
i64 iOff;
|
||||
};
|
||||
typedef struct Fts5ExprCtx Fts5ExprCtx;
|
||||
|
||||
/*
|
||||
** TODO: Make this more efficient!
|
||||
*/
|
||||
static int fts5ExprColsetTest(Fts5Colset *pColset, int iCol){
|
||||
int i;
|
||||
for(i=0; i<pColset->nCol; i++){
|
||||
if( pColset->aiCol[i]==iCol ) return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fts5ExprPopulatePoslistsCb(
|
||||
void *pCtx, /* Copy of 2nd argument to xTokenize() */
|
||||
int tflags, /* Mask of FTS5_TOKEN_* flags */
|
||||
const char *pToken, /* Pointer to buffer containing token */
|
||||
int nToken, /* Size of token in bytes */
|
||||
int iStart, /* Byte offset of token within input text */
|
||||
int iEnd /* Byte offset of end of token within input text */
|
||||
){
|
||||
Fts5ExprCtx *p = (Fts5ExprCtx*)pCtx;
|
||||
Fts5Expr *pExpr = p->pExpr;
|
||||
int i;
|
||||
|
||||
if( (tflags & FTS5_TOKEN_COLOCATED)==0 ) p->iOff++;
|
||||
for(i=0; i<pExpr->nPhrase; i++){
|
||||
Fts5ExprTerm *pTerm;
|
||||
if( p->aPopulator[i].bOk==0 ) continue;
|
||||
for(pTerm=&pExpr->apExprPhrase[i]->aTerm[0]; pTerm; pTerm=pTerm->pSynonym){
|
||||
int nTerm = strlen(pTerm->zTerm);
|
||||
if( (nTerm==nToken || (nTerm<nToken && pTerm->bPrefix))
|
||||
&& memcmp(pTerm->zTerm, pToken, nTerm)==0
|
||||
){
|
||||
int rc = sqlite3Fts5PoslistWriterAppend(
|
||||
&pExpr->apExprPhrase[i]->poslist, &p->aPopulator[i].writer, p->iOff
|
||||
);
|
||||
if( rc ) return rc;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
int sqlite3Fts5ExprPopulatePoslists(
|
||||
Fts5Config *pConfig,
|
||||
Fts5Expr *pExpr,
|
||||
Fts5PoslistPopulator *aPopulator,
|
||||
int iCol,
|
||||
const char *z, int n
|
||||
){
|
||||
int i;
|
||||
Fts5ExprCtx sCtx;
|
||||
sCtx.pExpr = pExpr;
|
||||
sCtx.aPopulator = aPopulator;
|
||||
sCtx.iOff = (((i64)iCol) << 32) - 1;
|
||||
|
||||
for(i=0; i<pExpr->nPhrase; i++){
|
||||
Fts5ExprNode *pNode = pExpr->apExprPhrase[i]->pNode;
|
||||
Fts5Colset *pColset = pNode->pNear->pColset;
|
||||
if( (pColset && 0==fts5ExprColsetTest(pColset, iCol))
|
||||
|| aPopulator[i].bMiss
|
||||
){
|
||||
aPopulator[i].bOk = 0;
|
||||
}else{
|
||||
aPopulator[i].bOk = 1;
|
||||
}
|
||||
}
|
||||
|
||||
return sqlite3Fts5Tokenize(pConfig,
|
||||
FTS5_TOKENIZE_AUX, z, n, (void*)&sCtx, fts5ExprPopulatePoslistsCb
|
||||
);
|
||||
}
|
||||
|
||||
static void fts5ExprClearPoslists(Fts5ExprNode *pNode){
|
||||
if( pNode->eType==FTS5_TERM || pNode->eType==FTS5_STRING ){
|
||||
pNode->pNear->apPhrase[0]->poslist.n = 0;
|
||||
}else{
|
||||
int i;
|
||||
for(i=0; i<pNode->nChild; i++){
|
||||
fts5ExprClearPoslists(pNode->apChild[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int fts5ExprCheckPoslists(Fts5ExprNode *pNode, i64 iRowid){
|
||||
if( pNode ){
|
||||
pNode->iRowid = iRowid;
|
||||
pNode->bEof = 0;
|
||||
switch( pNode->eType ){
|
||||
case FTS5_TERM:
|
||||
case FTS5_STRING:
|
||||
return (pNode->pNear->apPhrase[0]->poslist.n>0);
|
||||
|
||||
case FTS5_AND: {
|
||||
int i;
|
||||
for(i=0; i<pNode->nChild; i++){
|
||||
if( fts5ExprCheckPoslists(pNode->apChild[i], iRowid)==0 ){
|
||||
fts5ExprClearPoslists(pNode);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
case FTS5_OR: {
|
||||
int i;
|
||||
int bRet = 0;
|
||||
for(i=0; i<pNode->nChild; i++){
|
||||
if( fts5ExprCheckPoslists(pNode->apChild[i], iRowid) ){
|
||||
bRet = 1;
|
||||
}
|
||||
}
|
||||
if( bRet==0 ){
|
||||
fts5ExprClearPoslists(pNode);
|
||||
}
|
||||
return bRet;
|
||||
}
|
||||
|
||||
default: {
|
||||
assert( pNode->eType==FTS5_NOT );
|
||||
if( 0==fts5ExprCheckPoslists(pNode->apChild[0], iRowid)
|
||||
|| 0!=fts5ExprCheckPoslists(pNode->apChild[1], iRowid)
|
||||
){
|
||||
fts5ExprClearPoslists(pNode);
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void sqlite3Fts5ExprCheckPoslists(Fts5Expr *pExpr, i64 iRowid){
|
||||
fts5ExprCheckPoslists(pExpr->pRoot, iRowid);
|
||||
}
|
||||
|
||||
static void fts5ExprClearEof(Fts5ExprNode *pNode){
|
||||
int i;
|
||||
for(i=0; i<pNode->nChild; i++){
|
||||
fts5ExprClearEof(pNode->apChild[i]);
|
||||
}
|
||||
pNode->bEof = 0;
|
||||
}
|
||||
void sqlite3Fts5ExprClearEof(Fts5Expr *pExpr){
|
||||
fts5ExprClearEof(pExpr->pRoot);
|
||||
}
|
||||
|
||||
/*
|
||||
** This function is only called for detail=columns tables.
|
||||
*/
|
||||
int sqlite3Fts5ExprPhraseCollist(
|
||||
Fts5Expr *pExpr,
|
||||
int iPhrase,
|
||||
const u8 **ppCollist,
|
||||
int *pnCollist
|
||||
){
|
||||
Fts5ExprPhrase *pPhrase = pExpr->apExprPhrase[iPhrase];
|
||||
Fts5ExprNode *pNode = pPhrase->pNode;
|
||||
int rc = SQLITE_OK;
|
||||
|
||||
assert( iPhrase>=0 && iPhrase<pExpr->nPhrase );
|
||||
if( pNode->bEof==0
|
||||
&& pNode->iRowid==pExpr->pRoot->iRowid
|
||||
&& pPhrase->poslist.n>0
|
||||
){
|
||||
Fts5ExprTerm *pTerm = &pPhrase->aTerm[0];
|
||||
if( pTerm->pSynonym ){
|
||||
int bDel = 0;
|
||||
u8 *a;
|
||||
rc = fts5ExprSynonymList(
|
||||
pTerm, 1, 0, pNode->iRowid, &bDel, &a, pnCollist
|
||||
);
|
||||
if( bDel ){
|
||||
sqlite3Fts5BufferSet(&rc, &pPhrase->poslist, *pnCollist, a);
|
||||
*ppCollist = pPhrase->poslist.p;
|
||||
sqlite3_free(a);
|
||||
}else{
|
||||
*ppCollist = a;
|
||||
}
|
||||
}else{
|
||||
sqlite3Fts5IterCollist(pPhrase->aTerm[0].pIter, ppCollist, pnCollist);
|
||||
}
|
||||
}else{
|
||||
*ppCollist = 0;
|
||||
*pnCollist = 0;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
@ -26,6 +26,7 @@ typedef struct Fts5HashEntry Fts5HashEntry;
|
||||
|
||||
|
||||
struct Fts5Hash {
|
||||
int eDetail; /* Copy of Fts5Config.eDetail */
|
||||
int *pnByte; /* Pointer to bytes counter */
|
||||
int nEntry; /* Number of entries currently in hash */
|
||||
int nSlot; /* Size of aSlot[] array */
|
||||
@ -62,6 +63,7 @@ struct Fts5HashEntry {
|
||||
int iSzPoslist; /* Offset of space for 4-byte poslist size */
|
||||
int nData; /* Total bytes of data (incl. structure) */
|
||||
u8 bDel; /* Set delete-flag @ iSzPoslist */
|
||||
u8 bContent; /* Set content-flag (detail=none mode) */
|
||||
|
||||
int iCol; /* Column of last value written */
|
||||
int iPos; /* Position of last value written */
|
||||
@ -79,7 +81,7 @@ struct Fts5HashEntry {
|
||||
/*
|
||||
** Allocate a new hash table.
|
||||
*/
|
||||
int sqlite3Fts5HashNew(Fts5Hash **ppNew, int *pnByte){
|
||||
int sqlite3Fts5HashNew(Fts5Config *pConfig, Fts5Hash **ppNew, int *pnByte){
|
||||
int rc = SQLITE_OK;
|
||||
Fts5Hash *pNew;
|
||||
|
||||
@ -90,6 +92,7 @@ int sqlite3Fts5HashNew(Fts5Hash **ppNew, int *pnByte){
|
||||
int nByte;
|
||||
memset(pNew, 0, sizeof(Fts5Hash));
|
||||
pNew->pnByte = pnByte;
|
||||
pNew->eDetail = pConfig->eDetail;
|
||||
|
||||
pNew->nSlot = 1024;
|
||||
nByte = sizeof(Fts5HashEntry*) * pNew->nSlot;
|
||||
@ -182,26 +185,46 @@ static int fts5HashResize(Fts5Hash *pHash){
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
static void fts5HashAddPoslistSize(Fts5HashEntry *p){
|
||||
static void fts5HashAddPoslistSize(Fts5Hash *pHash, Fts5HashEntry *p){
|
||||
if( p->iSzPoslist ){
|
||||
u8 *pPtr = (u8*)p;
|
||||
int nSz = (p->nData - p->iSzPoslist - 1); /* Size in bytes */
|
||||
int nPos = nSz*2 + p->bDel; /* Value of nPos field */
|
||||
|
||||
assert( p->bDel==0 || p->bDel==1 );
|
||||
if( nPos<=127 ){
|
||||
pPtr[p->iSzPoslist] = (u8)nPos;
|
||||
if( pHash->eDetail==FTS5_DETAIL_NONE ){
|
||||
assert( p->nData==p->iSzPoslist );
|
||||
if( p->bDel ){
|
||||
pPtr[p->nData++] = 0x00;
|
||||
if( p->bContent ){
|
||||
pPtr[p->nData++] = 0x00;
|
||||
}
|
||||
}
|
||||
}else{
|
||||
int nByte = sqlite3Fts5GetVarintLen((u32)nPos);
|
||||
memmove(&pPtr[p->iSzPoslist + nByte], &pPtr[p->iSzPoslist + 1], nSz);
|
||||
sqlite3Fts5PutVarint(&pPtr[p->iSzPoslist], nPos);
|
||||
p->nData += (nByte-1);
|
||||
int nSz = (p->nData - p->iSzPoslist - 1); /* Size in bytes */
|
||||
int nPos = nSz*2 + p->bDel; /* Value of nPos field */
|
||||
|
||||
assert( p->bDel==0 || p->bDel==1 );
|
||||
if( nPos<=127 ){
|
||||
pPtr[p->iSzPoslist] = (u8)nPos;
|
||||
}else{
|
||||
int nByte = sqlite3Fts5GetVarintLen((u32)nPos);
|
||||
memmove(&pPtr[p->iSzPoslist + nByte], &pPtr[p->iSzPoslist + 1], nSz);
|
||||
sqlite3Fts5PutVarint(&pPtr[p->iSzPoslist], nPos);
|
||||
p->nData += (nByte-1);
|
||||
}
|
||||
}
|
||||
p->bDel = 0;
|
||||
|
||||
p->iSzPoslist = 0;
|
||||
p->bDel = 0;
|
||||
p->bContent = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Add an entry to the in-memory hash table. The key is the concatenation
|
||||
** of bByte and (pToken/nToken). The value is (iRowid/iCol/iPos).
|
||||
**
|
||||
** (bByte || pToken) -> (iRowid,iCol,iPos)
|
||||
**
|
||||
** Or, if iCol is negative, then the value is a delete marker.
|
||||
*/
|
||||
int sqlite3Fts5HashWrite(
|
||||
Fts5Hash *pHash,
|
||||
i64 iRowid, /* Rowid for this entry */
|
||||
@ -214,6 +237,9 @@ int sqlite3Fts5HashWrite(
|
||||
Fts5HashEntry *p;
|
||||
u8 *pPtr;
|
||||
int nIncr = 0; /* Amount to increment (*pHash->pnByte) by */
|
||||
int bNew; /* If non-delete entry should be written */
|
||||
|
||||
bNew = (pHash->eDetail==FTS5_DETAIL_FULL);
|
||||
|
||||
/* Attempt to locate an existing hash entry */
|
||||
iHash = fts5HashKey2(pHash->nSlot, (u8)bByte, (const u8*)pToken, nToken);
|
||||
@ -228,15 +254,18 @@ int sqlite3Fts5HashWrite(
|
||||
|
||||
/* If an existing hash entry cannot be found, create a new one. */
|
||||
if( p==0 ){
|
||||
/* Figure out how much space to allocate */
|
||||
int nByte = FTS5_HASHENTRYSIZE + (nToken+1) + 1 + 64;
|
||||
if( nByte<128 ) nByte = 128;
|
||||
|
||||
/* Grow the Fts5Hash.aSlot[] array if necessary. */
|
||||
if( (pHash->nEntry*2)>=pHash->nSlot ){
|
||||
int rc = fts5HashResize(pHash);
|
||||
if( rc!=SQLITE_OK ) return rc;
|
||||
iHash = fts5HashKey2(pHash->nSlot, (u8)bByte, (const u8*)pToken, nToken);
|
||||
}
|
||||
|
||||
/* Allocate new Fts5HashEntry and add it to the hash table. */
|
||||
p = (Fts5HashEntry*)sqlite3_malloc(nByte);
|
||||
if( !p ) return SQLITE_NOMEM;
|
||||
memset(p, 0, FTS5_HASHENTRYSIZE);
|
||||
@ -246,70 +275,95 @@ int sqlite3Fts5HashWrite(
|
||||
assert( iHash==fts5HashKey(pHash->nSlot, (u8*)p->zKey, nToken+1) );
|
||||
p->zKey[nToken+1] = '\0';
|
||||
p->nData = nToken+1 + 1 + FTS5_HASHENTRYSIZE;
|
||||
p->nData += sqlite3Fts5PutVarint(&((u8*)p)[p->nData], iRowid);
|
||||
p->iSzPoslist = p->nData;
|
||||
p->nData += 1;
|
||||
p->iRowid = iRowid;
|
||||
p->pHashNext = pHash->aSlot[iHash];
|
||||
pHash->aSlot[iHash] = p;
|
||||
pHash->nEntry++;
|
||||
nIncr += p->nData;
|
||||
}
|
||||
|
||||
/* Check there is enough space to append a new entry. Worst case scenario
|
||||
** is:
|
||||
**
|
||||
** + 9 bytes for a new rowid,
|
||||
** + 4 byte reserved for the "poslist size" varint.
|
||||
** + 1 byte for a "new column" byte,
|
||||
** + 3 bytes for a new column number (16-bit max) as a varint,
|
||||
** + 5 bytes for the new position offset (32-bit max).
|
||||
*/
|
||||
if( (p->nAlloc - p->nData) < (9 + 4 + 1 + 3 + 5) ){
|
||||
int nNew = p->nAlloc * 2;
|
||||
Fts5HashEntry *pNew;
|
||||
Fts5HashEntry **pp;
|
||||
pNew = (Fts5HashEntry*)sqlite3_realloc(p, nNew);
|
||||
if( pNew==0 ) return SQLITE_NOMEM;
|
||||
pNew->nAlloc = nNew;
|
||||
for(pp=&pHash->aSlot[iHash]; *pp!=p; pp=&(*pp)->pHashNext);
|
||||
*pp = pNew;
|
||||
p = pNew;
|
||||
/* Add the first rowid field to the hash-entry */
|
||||
p->nData += sqlite3Fts5PutVarint(&((u8*)p)[p->nData], iRowid);
|
||||
p->iRowid = iRowid;
|
||||
|
||||
p->iSzPoslist = p->nData;
|
||||
if( pHash->eDetail!=FTS5_DETAIL_NONE ){
|
||||
p->nData += 1;
|
||||
p->iCol = (pHash->eDetail==FTS5_DETAIL_FULL ? 0 : -1);
|
||||
}
|
||||
|
||||
nIncr += p->nData;
|
||||
}else{
|
||||
|
||||
/* Appending to an existing hash-entry. Check that there is enough
|
||||
** space to append the largest possible new entry. Worst case scenario
|
||||
** is:
|
||||
**
|
||||
** + 9 bytes for a new rowid,
|
||||
** + 4 byte reserved for the "poslist size" varint.
|
||||
** + 1 byte for a "new column" byte,
|
||||
** + 3 bytes for a new column number (16-bit max) as a varint,
|
||||
** + 5 bytes for the new position offset (32-bit max).
|
||||
*/
|
||||
if( (p->nAlloc - p->nData) < (9 + 4 + 1 + 3 + 5) ){
|
||||
int nNew = p->nAlloc * 2;
|
||||
Fts5HashEntry *pNew;
|
||||
Fts5HashEntry **pp;
|
||||
pNew = (Fts5HashEntry*)sqlite3_realloc(p, nNew);
|
||||
if( pNew==0 ) return SQLITE_NOMEM;
|
||||
pNew->nAlloc = nNew;
|
||||
for(pp=&pHash->aSlot[iHash]; *pp!=p; pp=&(*pp)->pHashNext);
|
||||
*pp = pNew;
|
||||
p = pNew;
|
||||
}
|
||||
nIncr -= p->nData;
|
||||
}
|
||||
assert( (p->nAlloc - p->nData) >= (9 + 4 + 1 + 3 + 5) );
|
||||
|
||||
pPtr = (u8*)p;
|
||||
nIncr -= p->nData;
|
||||
|
||||
/* If this is a new rowid, append the 4-byte size field for the previous
|
||||
** entry, and the new rowid for this entry. */
|
||||
if( iRowid!=p->iRowid ){
|
||||
fts5HashAddPoslistSize(p);
|
||||
fts5HashAddPoslistSize(pHash, p);
|
||||
p->nData += sqlite3Fts5PutVarint(&pPtr[p->nData], iRowid - p->iRowid);
|
||||
p->iSzPoslist = p->nData;
|
||||
p->nData += 1;
|
||||
p->iCol = 0;
|
||||
p->iPos = 0;
|
||||
p->iRowid = iRowid;
|
||||
bNew = 1;
|
||||
p->iSzPoslist = p->nData;
|
||||
if( pHash->eDetail!=FTS5_DETAIL_NONE ){
|
||||
p->nData += 1;
|
||||
p->iCol = (pHash->eDetail==FTS5_DETAIL_FULL ? 0 : -1);
|
||||
p->iPos = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if( iCol>=0 ){
|
||||
/* Append a new column value, if necessary */
|
||||
assert( iCol>=p->iCol );
|
||||
if( iCol!=p->iCol ){
|
||||
pPtr[p->nData++] = 0x01;
|
||||
p->nData += sqlite3Fts5PutVarint(&pPtr[p->nData], iCol);
|
||||
p->iCol = iCol;
|
||||
p->iPos = 0;
|
||||
}
|
||||
if( pHash->eDetail==FTS5_DETAIL_NONE ){
|
||||
p->bContent = 1;
|
||||
}else{
|
||||
/* Append a new column value, if necessary */
|
||||
assert( iCol>=p->iCol );
|
||||
if( iCol!=p->iCol ){
|
||||
if( pHash->eDetail==FTS5_DETAIL_FULL ){
|
||||
pPtr[p->nData++] = 0x01;
|
||||
p->nData += sqlite3Fts5PutVarint(&pPtr[p->nData], iCol);
|
||||
p->iCol = iCol;
|
||||
p->iPos = 0;
|
||||
}else{
|
||||
bNew = 1;
|
||||
p->iCol = iPos = iCol;
|
||||
}
|
||||
}
|
||||
|
||||
/* Append the new position offset */
|
||||
p->nData += sqlite3Fts5PutVarint(&pPtr[p->nData], iPos - p->iPos + 2);
|
||||
p->iPos = iPos;
|
||||
/* Append the new position offset, if necessary */
|
||||
if( bNew ){
|
||||
p->nData += sqlite3Fts5PutVarint(&pPtr[p->nData], iPos - p->iPos + 2);
|
||||
p->iPos = iPos;
|
||||
}
|
||||
}
|
||||
}else{
|
||||
/* This is a delete. Set the delete flag. */
|
||||
p->bDel = 1;
|
||||
}
|
||||
nIncr += p->nData;
|
||||
|
||||
nIncr += p->nData;
|
||||
*pHash->pnByte += nIncr;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
@ -423,7 +477,7 @@ int sqlite3Fts5HashQuery(
|
||||
}
|
||||
|
||||
if( p ){
|
||||
fts5HashAddPoslistSize(p);
|
||||
fts5HashAddPoslistSize(pHash, p);
|
||||
*ppDoclist = (const u8*)&p->zKey[nTerm+1];
|
||||
*pnDoclist = p->nData - (FTS5_HASHENTRYSIZE + nTerm + 1);
|
||||
}else{
|
||||
@ -459,7 +513,7 @@ void sqlite3Fts5HashScanEntry(
|
||||
Fts5HashEntry *p;
|
||||
if( (p = pHash->pScan) ){
|
||||
int nTerm = (int)strlen(p->zKey);
|
||||
fts5HashAddPoslistSize(p);
|
||||
fts5HashAddPoslistSize(pHash, p);
|
||||
*pzTerm = p->zKey;
|
||||
*ppDoclist = (const u8*)&p->zKey[nTerm+1];
|
||||
*pnDoclist = p->nData - (FTS5_HASHENTRYSIZE + nTerm + 1);
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -226,6 +226,7 @@ struct Fts5Cursor {
|
||||
#define FTS5CSR_EOF 0x08
|
||||
#define FTS5CSR_FREE_ZRANK 0x10
|
||||
#define FTS5CSR_REQUIRE_RESEEK 0x20
|
||||
#define FTS5CSR_REQUIRE_POSLIST 0x40
|
||||
|
||||
#define BitFlagAllTest(x,y) (((x) & (y))==(y))
|
||||
#define BitFlagTest(x,y) (((x) & (y))!=0)
|
||||
@ -309,6 +310,13 @@ static int fts5IsContentless(Fts5Table *pTab){
|
||||
return pTab->pConfig->eContent==FTS5_CONTENT_NONE;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return true if pTab is an offsetless table.
|
||||
*/
|
||||
static int fts5IsOffsetless(Fts5Table *pTab){
|
||||
return pTab->pConfig->eDetail!=FTS5_DETAIL_FULL;
|
||||
}
|
||||
|
||||
/*
|
||||
** Delete a virtual table handle allocated by fts5InitVtab().
|
||||
*/
|
||||
@ -639,6 +647,7 @@ static void fts5CsrNewrow(Fts5Cursor *pCsr){
|
||||
FTS5CSR_REQUIRE_CONTENT
|
||||
| FTS5CSR_REQUIRE_DOCSIZE
|
||||
| FTS5CSR_REQUIRE_INST
|
||||
| FTS5CSR_REQUIRE_POSLIST
|
||||
);
|
||||
}
|
||||
|
||||
@ -721,15 +730,18 @@ static int fts5SorterNext(Fts5Cursor *pCsr){
|
||||
nBlob = sqlite3_column_bytes(pSorter->pStmt, 1);
|
||||
aBlob = a = sqlite3_column_blob(pSorter->pStmt, 1);
|
||||
|
||||
for(i=0; i<(pSorter->nIdx-1); i++){
|
||||
int iVal;
|
||||
a += fts5GetVarint32(a, iVal);
|
||||
iOff += iVal;
|
||||
pSorter->aIdx[i] = iOff;
|
||||
/* nBlob==0 in detail=none mode. */
|
||||
if( nBlob>0 ){
|
||||
for(i=0; i<(pSorter->nIdx-1); i++){
|
||||
int iVal;
|
||||
a += fts5GetVarint32(a, iVal);
|
||||
iOff += iVal;
|
||||
pSorter->aIdx[i] = iOff;
|
||||
}
|
||||
pSorter->aIdx[i] = &aBlob[nBlob] - a;
|
||||
pSorter->aPoslist = a;
|
||||
}
|
||||
pSorter->aIdx[i] = &aBlob[nBlob] - a;
|
||||
|
||||
pSorter->aPoslist = a;
|
||||
fts5CsrNewrow(pCsr);
|
||||
}
|
||||
|
||||
@ -1167,6 +1179,7 @@ static int fts5FilterMethod(
|
||||
pCsr->ePlan = FTS5_PLAN_SOURCE;
|
||||
pCsr->pExpr = pTab->pSortCsr->pExpr;
|
||||
rc = fts5CursorFirst(pTab, pCsr, bDesc);
|
||||
sqlite3Fts5ExprClearEof(pCsr->pExpr);
|
||||
}else if( pMatch ){
|
||||
const char *zExpr = (const char*)sqlite3_value_text(apVal[0]);
|
||||
if( zExpr==0 ) zExpr = "";
|
||||
@ -1596,6 +1609,8 @@ static int fts5RollbackMethod(sqlite3_vtab *pVtab){
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int fts5CsrPoslist(Fts5Cursor*, int, const u8**, int*);
|
||||
|
||||
static void *fts5ApiUserData(Fts5Context *pCtx){
|
||||
Fts5Cursor *pCsr = (Fts5Cursor*)pCtx;
|
||||
return pCsr->pAux->pUserData;
|
||||
@ -1645,17 +1660,72 @@ static int fts5ApiPhraseSize(Fts5Context *pCtx, int iPhrase){
|
||||
return sqlite3Fts5ExprPhraseSize(pCsr->pExpr, iPhrase);
|
||||
}
|
||||
|
||||
static int fts5CsrPoslist(Fts5Cursor *pCsr, int iPhrase, const u8 **pa){
|
||||
int n;
|
||||
if( pCsr->pSorter ){
|
||||
static int fts5ApiColumnText(
|
||||
Fts5Context *pCtx,
|
||||
int iCol,
|
||||
const char **pz,
|
||||
int *pn
|
||||
){
|
||||
int rc = SQLITE_OK;
|
||||
Fts5Cursor *pCsr = (Fts5Cursor*)pCtx;
|
||||
if( fts5IsContentless((Fts5Table*)(pCsr->base.pVtab)) ){
|
||||
*pz = 0;
|
||||
*pn = 0;
|
||||
}else{
|
||||
rc = fts5SeekCursor(pCsr, 0);
|
||||
if( rc==SQLITE_OK ){
|
||||
*pz = (const char*)sqlite3_column_text(pCsr->pStmt, iCol+1);
|
||||
*pn = sqlite3_column_bytes(pCsr->pStmt, iCol+1);
|
||||
}
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int fts5CsrPoslist(
|
||||
Fts5Cursor *pCsr,
|
||||
int iPhrase,
|
||||
const u8 **pa,
|
||||
int *pn
|
||||
){
|
||||
Fts5Config *pConfig = ((Fts5Table*)(pCsr->base.pVtab))->pConfig;
|
||||
int rc = SQLITE_OK;
|
||||
int bLive = (pCsr->pSorter==0);
|
||||
|
||||
if( CsrFlagTest(pCsr, FTS5CSR_REQUIRE_POSLIST) ){
|
||||
|
||||
if( pConfig->eDetail!=FTS5_DETAIL_FULL ){
|
||||
Fts5PoslistPopulator *aPopulator;
|
||||
int i;
|
||||
aPopulator = sqlite3Fts5ExprClearPoslists(pCsr->pExpr, bLive);
|
||||
if( aPopulator==0 ) rc = SQLITE_NOMEM;
|
||||
for(i=0; i<pConfig->nCol && rc==SQLITE_OK; i++){
|
||||
int n; const char *z;
|
||||
rc = fts5ApiColumnText((Fts5Context*)pCsr, i, &z, &n);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3Fts5ExprPopulatePoslists(
|
||||
pConfig, pCsr->pExpr, aPopulator, i, z, n
|
||||
);
|
||||
}
|
||||
}
|
||||
sqlite3_free(aPopulator);
|
||||
|
||||
if( pCsr->pSorter ){
|
||||
sqlite3Fts5ExprCheckPoslists(pCsr->pExpr, pCsr->pSorter->iRowid);
|
||||
}
|
||||
}
|
||||
CsrFlagClear(pCsr, FTS5CSR_REQUIRE_POSLIST);
|
||||
}
|
||||
|
||||
if( pCsr->pSorter && pConfig->eDetail==FTS5_DETAIL_FULL ){
|
||||
Fts5Sorter *pSorter = pCsr->pSorter;
|
||||
int i1 = (iPhrase==0 ? 0 : pSorter->aIdx[iPhrase-1]);
|
||||
n = pSorter->aIdx[iPhrase] - i1;
|
||||
*pn = pSorter->aIdx[iPhrase] - i1;
|
||||
*pa = &pSorter->aPoslist[i1];
|
||||
}else{
|
||||
n = sqlite3Fts5ExprPoslist(pCsr->pExpr, iPhrase, pa);
|
||||
*pn = sqlite3Fts5ExprPoslist(pCsr->pExpr, iPhrase, pa);
|
||||
}
|
||||
return n;
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1680,43 +1750,46 @@ static int fts5CacheInstArray(Fts5Cursor *pCsr){
|
||||
int i;
|
||||
|
||||
/* Initialize all iterators */
|
||||
for(i=0; i<nIter; i++){
|
||||
for(i=0; i<nIter && rc==SQLITE_OK; i++){
|
||||
const u8 *a;
|
||||
int n = fts5CsrPoslist(pCsr, i, &a);
|
||||
int n;
|
||||
rc = fts5CsrPoslist(pCsr, i, &a, &n);
|
||||
sqlite3Fts5PoslistReaderInit(a, n, &aIter[i]);
|
||||
}
|
||||
|
||||
while( 1 ){
|
||||
int *aInst;
|
||||
int iBest = -1;
|
||||
for(i=0; i<nIter; i++){
|
||||
if( (aIter[i].bEof==0)
|
||||
&& (iBest<0 || aIter[i].iPos<aIter[iBest].iPos)
|
||||
){
|
||||
iBest = i;
|
||||
if( rc==SQLITE_OK ){
|
||||
while( 1 ){
|
||||
int *aInst;
|
||||
int iBest = -1;
|
||||
for(i=0; i<nIter; i++){
|
||||
if( (aIter[i].bEof==0)
|
||||
&& (iBest<0 || aIter[i].iPos<aIter[iBest].iPos)
|
||||
){
|
||||
iBest = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
if( iBest<0 ) break;
|
||||
if( iBest<0 ) break;
|
||||
|
||||
nInst++;
|
||||
if( nInst>=pCsr->nInstAlloc ){
|
||||
pCsr->nInstAlloc = pCsr->nInstAlloc ? pCsr->nInstAlloc*2 : 32;
|
||||
aInst = (int*)sqlite3_realloc(
|
||||
pCsr->aInst, pCsr->nInstAlloc*sizeof(int)*3
|
||||
);
|
||||
if( aInst ){
|
||||
pCsr->aInst = aInst;
|
||||
}else{
|
||||
rc = SQLITE_NOMEM;
|
||||
break;
|
||||
nInst++;
|
||||
if( nInst>=pCsr->nInstAlloc ){
|
||||
pCsr->nInstAlloc = pCsr->nInstAlloc ? pCsr->nInstAlloc*2 : 32;
|
||||
aInst = (int*)sqlite3_realloc(
|
||||
pCsr->aInst, pCsr->nInstAlloc*sizeof(int)*3
|
||||
);
|
||||
if( aInst ){
|
||||
pCsr->aInst = aInst;
|
||||
}else{
|
||||
rc = SQLITE_NOMEM;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
aInst = &pCsr->aInst[3 * (nInst-1)];
|
||||
aInst[0] = iBest;
|
||||
aInst[1] = FTS5_POS2COLUMN(aIter[iBest].iPos);
|
||||
aInst[2] = FTS5_POS2OFFSET(aIter[iBest].iPos);
|
||||
sqlite3Fts5PoslistReaderNext(&aIter[iBest]);
|
||||
aInst = &pCsr->aInst[3 * (nInst-1)];
|
||||
aInst[0] = iBest;
|
||||
aInst[1] = FTS5_POS2COLUMN(aIter[iBest].iPos);
|
||||
aInst[2] = FTS5_POS2OFFSET(aIter[iBest].iPos);
|
||||
sqlite3Fts5PoslistReaderNext(&aIter[iBest]);
|
||||
}
|
||||
}
|
||||
|
||||
pCsr->nInstCount = nInst;
|
||||
@ -1749,6 +1822,12 @@ static int fts5ApiInst(
|
||||
){
|
||||
if( iIdx<0 || iIdx>=pCsr->nInstCount ){
|
||||
rc = SQLITE_RANGE;
|
||||
#if 0
|
||||
}else if( fts5IsOffsetless((Fts5Table*)pCsr->base.pVtab) ){
|
||||
*piPhrase = pCsr->aInst[iIdx*3];
|
||||
*piCol = pCsr->aInst[iIdx*3 + 2];
|
||||
*piOff = -1;
|
||||
#endif
|
||||
}else{
|
||||
*piPhrase = pCsr->aInst[iIdx*3];
|
||||
*piCol = pCsr->aInst[iIdx*3 + 1];
|
||||
@ -1762,27 +1841,6 @@ static sqlite3_int64 fts5ApiRowid(Fts5Context *pCtx){
|
||||
return fts5CursorRowid((Fts5Cursor*)pCtx);
|
||||
}
|
||||
|
||||
static int fts5ApiColumnText(
|
||||
Fts5Context *pCtx,
|
||||
int iCol,
|
||||
const char **pz,
|
||||
int *pn
|
||||
){
|
||||
int rc = SQLITE_OK;
|
||||
Fts5Cursor *pCsr = (Fts5Cursor*)pCtx;
|
||||
if( fts5IsContentless((Fts5Table*)(pCsr->base.pVtab)) ){
|
||||
*pz = 0;
|
||||
*pn = 0;
|
||||
}else{
|
||||
rc = fts5SeekCursor(pCsr, 0);
|
||||
if( rc==SQLITE_OK ){
|
||||
*pz = (const char*)sqlite3_column_text(pCsr->pStmt, iCol+1);
|
||||
*pn = sqlite3_column_bytes(pCsr->pStmt, iCol+1);
|
||||
}
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int fts5ColumnSizeCb(
|
||||
void *pContext, /* Pointer to int */
|
||||
int tflags,
|
||||
@ -1927,20 +1985,91 @@ static void fts5ApiPhraseNext(
|
||||
}
|
||||
}
|
||||
|
||||
static void fts5ApiPhraseFirst(
|
||||
static int fts5ApiPhraseFirst(
|
||||
Fts5Context *pCtx,
|
||||
int iPhrase,
|
||||
Fts5PhraseIter *pIter,
|
||||
int *piCol, int *piOff
|
||||
){
|
||||
Fts5Cursor *pCsr = (Fts5Cursor*)pCtx;
|
||||
int n = fts5CsrPoslist(pCsr, iPhrase, &pIter->a);
|
||||
pIter->b = &pIter->a[n];
|
||||
*piCol = 0;
|
||||
*piOff = 0;
|
||||
fts5ApiPhraseNext(pCtx, pIter, piCol, piOff);
|
||||
int n;
|
||||
int rc = fts5CsrPoslist(pCsr, iPhrase, &pIter->a, &n);
|
||||
if( rc==SQLITE_OK ){
|
||||
pIter->b = &pIter->a[n];
|
||||
*piCol = 0;
|
||||
*piOff = 0;
|
||||
fts5ApiPhraseNext(pCtx, pIter, piCol, piOff);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void fts5ApiPhraseNextColumn(
|
||||
Fts5Context *pCtx,
|
||||
Fts5PhraseIter *pIter,
|
||||
int *piCol
|
||||
){
|
||||
Fts5Cursor *pCsr = (Fts5Cursor*)pCtx;
|
||||
Fts5Config *pConfig = ((Fts5Table*)(pCsr->base.pVtab))->pConfig;
|
||||
|
||||
if( pConfig->eDetail==FTS5_DETAIL_COLUMNS ){
|
||||
if( pIter->a>=pIter->b ){
|
||||
*piCol = -1;
|
||||
}else{
|
||||
int iIncr;
|
||||
pIter->a += fts5GetVarint32(&pIter->a[0], iIncr);
|
||||
*piCol += (iIncr-2);
|
||||
}
|
||||
}else{
|
||||
while( 1 ){
|
||||
int dummy;
|
||||
if( pIter->a>=pIter->b ){
|
||||
*piCol = -1;
|
||||
return;
|
||||
}
|
||||
if( pIter->a[0]==0x01 ) break;
|
||||
pIter->a += fts5GetVarint32(pIter->a, dummy);
|
||||
}
|
||||
pIter->a += 1 + fts5GetVarint32(&pIter->a[1], *piCol);
|
||||
}
|
||||
}
|
||||
|
||||
static int fts5ApiPhraseFirstColumn(
|
||||
Fts5Context *pCtx,
|
||||
int iPhrase,
|
||||
Fts5PhraseIter *pIter,
|
||||
int *piCol
|
||||
){
|
||||
int rc = SQLITE_OK;
|
||||
Fts5Cursor *pCsr = (Fts5Cursor*)pCtx;
|
||||
Fts5Config *pConfig = ((Fts5Table*)(pCsr->base.pVtab))->pConfig;
|
||||
|
||||
if( pConfig->eDetail==FTS5_DETAIL_COLUMNS ){
|
||||
int n;
|
||||
rc = sqlite3Fts5ExprPhraseCollist(pCsr->pExpr, iPhrase, &pIter->a, &n);
|
||||
if( rc==SQLITE_OK ){
|
||||
pIter->b = &pIter->a[n];
|
||||
*piCol = 0;
|
||||
fts5ApiPhraseNextColumn(pCtx, pIter, piCol);
|
||||
}
|
||||
}else{
|
||||
int n;
|
||||
rc = fts5CsrPoslist(pCsr, iPhrase, &pIter->a, &n);
|
||||
if( rc==SQLITE_OK ){
|
||||
pIter->b = &pIter->a[n];
|
||||
if( n<=0 ){
|
||||
*piCol = -1;
|
||||
}else if( pIter->a[0]==0x01 ){
|
||||
pIter->a += 1 + fts5GetVarint32(&pIter->a[1], *piCol);
|
||||
}else{
|
||||
*piCol = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
static int fts5ApiQueryPhrase(Fts5Context*, int, void*,
|
||||
int(*)(const Fts5ExtensionApi*, Fts5Context*, void*)
|
||||
);
|
||||
@ -1964,9 +2093,10 @@ static const Fts5ExtensionApi sFts5Api = {
|
||||
fts5ApiGetAuxdata,
|
||||
fts5ApiPhraseFirst,
|
||||
fts5ApiPhraseNext,
|
||||
fts5ApiPhraseFirstColumn,
|
||||
fts5ApiPhraseNextColumn,
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
** Implementation of API function xQueryPhrase().
|
||||
*/
|
||||
@ -2098,20 +2228,46 @@ static int fts5PoslistBlob(sqlite3_context *pCtx, Fts5Cursor *pCsr){
|
||||
Fts5Buffer val;
|
||||
|
||||
memset(&val, 0, sizeof(Fts5Buffer));
|
||||
switch( ((Fts5Table*)(pCsr->base.pVtab))->pConfig->eDetail ){
|
||||
case FTS5_DETAIL_FULL:
|
||||
|
||||
/* Append the varints */
|
||||
for(i=0; i<(nPhrase-1); i++){
|
||||
const u8 *dummy;
|
||||
int nByte = sqlite3Fts5ExprPoslist(pCsr->pExpr, i, &dummy);
|
||||
sqlite3Fts5BufferAppendVarint(&rc, &val, nByte);
|
||||
}
|
||||
/* Append the varints */
|
||||
for(i=0; i<(nPhrase-1); i++){
|
||||
const u8 *dummy;
|
||||
int nByte = sqlite3Fts5ExprPoslist(pCsr->pExpr, i, &dummy);
|
||||
sqlite3Fts5BufferAppendVarint(&rc, &val, nByte);
|
||||
}
|
||||
|
||||
/* Append the position lists */
|
||||
for(i=0; i<nPhrase; i++){
|
||||
const u8 *pPoslist;
|
||||
int nPoslist;
|
||||
nPoslist = sqlite3Fts5ExprPoslist(pCsr->pExpr, i, &pPoslist);
|
||||
sqlite3Fts5BufferAppendBlob(&rc, &val, nPoslist, pPoslist);
|
||||
/* Append the position lists */
|
||||
for(i=0; i<nPhrase; i++){
|
||||
const u8 *pPoslist;
|
||||
int nPoslist;
|
||||
nPoslist = sqlite3Fts5ExprPoslist(pCsr->pExpr, i, &pPoslist);
|
||||
sqlite3Fts5BufferAppendBlob(&rc, &val, nPoslist, pPoslist);
|
||||
}
|
||||
break;
|
||||
|
||||
case FTS5_DETAIL_COLUMNS:
|
||||
|
||||
/* Append the varints */
|
||||
for(i=0; rc==SQLITE_OK && i<(nPhrase-1); i++){
|
||||
const u8 *dummy;
|
||||
int nByte;
|
||||
rc = sqlite3Fts5ExprPhraseCollist(pCsr->pExpr, i, &dummy, &nByte);
|
||||
sqlite3Fts5BufferAppendVarint(&rc, &val, nByte);
|
||||
}
|
||||
|
||||
/* Append the position lists */
|
||||
for(i=0; rc==SQLITE_OK && i<nPhrase; i++){
|
||||
const u8 *pPoslist;
|
||||
int nPoslist;
|
||||
rc = sqlite3Fts5ExprPhraseCollist(pCsr->pExpr, i, &pPoslist, &nPoslist);
|
||||
sqlite3Fts5BufferAppendBlob(&rc, &val, nPoslist, pPoslist);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
sqlite3_result_blob(pCtx, val.p, val.n, sqlite3_free);
|
||||
|
@ -825,14 +825,16 @@ struct Fts5IntegrityCtx {
|
||||
int iCol;
|
||||
int szCol;
|
||||
u64 cksum;
|
||||
Fts5Termset *pTermset;
|
||||
Fts5Config *pConfig;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
** Tokenization callback used by integrity check.
|
||||
*/
|
||||
static int fts5StorageIntegrityCallback(
|
||||
void *pContext, /* Pointer to Fts5InsertCtx object */
|
||||
void *pContext, /* Pointer to Fts5IntegrityCtx object */
|
||||
int tflags,
|
||||
const char *pToken, /* Buffer containing token */
|
||||
int nToken, /* Size of token in bytes */
|
||||
@ -840,13 +842,56 @@ static int fts5StorageIntegrityCallback(
|
||||
int iEnd /* End offset of token */
|
||||
){
|
||||
Fts5IntegrityCtx *pCtx = (Fts5IntegrityCtx*)pContext;
|
||||
Fts5Termset *pTermset = pCtx->pTermset;
|
||||
int bPresent;
|
||||
int ii;
|
||||
int rc = SQLITE_OK;
|
||||
int iPos;
|
||||
int iCol;
|
||||
|
||||
if( (tflags & FTS5_TOKEN_COLOCATED)==0 || pCtx->szCol==0 ){
|
||||
pCtx->szCol++;
|
||||
}
|
||||
pCtx->cksum ^= sqlite3Fts5IndexCksum(
|
||||
pCtx->pConfig, pCtx->iRowid, pCtx->iCol, pCtx->szCol-1, pToken, nToken
|
||||
);
|
||||
return SQLITE_OK;
|
||||
|
||||
switch( pCtx->pConfig->eDetail ){
|
||||
case FTS5_DETAIL_FULL:
|
||||
iPos = pCtx->szCol-1;
|
||||
iCol = pCtx->iCol;
|
||||
break;
|
||||
|
||||
case FTS5_DETAIL_COLUMNS:
|
||||
iPos = pCtx->iCol;
|
||||
iCol = 0;
|
||||
break;
|
||||
|
||||
default:
|
||||
assert( pCtx->pConfig->eDetail==FTS5_DETAIL_NONE );
|
||||
iPos = 0;
|
||||
iCol = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
rc = sqlite3Fts5TermsetAdd(pTermset, 0, pToken, nToken, &bPresent);
|
||||
if( rc==SQLITE_OK && bPresent==0 ){
|
||||
pCtx->cksum ^= sqlite3Fts5IndexEntryCksum(
|
||||
pCtx->iRowid, iCol, iPos, 0, pToken, nToken
|
||||
);
|
||||
}
|
||||
|
||||
for(ii=0; rc==SQLITE_OK && ii<pCtx->pConfig->nPrefix; ii++){
|
||||
const int nChar = pCtx->pConfig->aPrefix[ii];
|
||||
int nByte = sqlite3Fts5IndexCharlenToBytelen(pToken, nToken, nChar);
|
||||
if( nByte ){
|
||||
rc = sqlite3Fts5TermsetAdd(pTermset, ii+1, pToken, nByte, &bPresent);
|
||||
if( bPresent==0 ){
|
||||
pCtx->cksum ^= sqlite3Fts5IndexEntryCksum(
|
||||
pCtx->iRowid, iCol, iPos, ii+1, pToken, nByte
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -882,22 +927,37 @@ int sqlite3Fts5StorageIntegrity(Fts5Storage *p){
|
||||
if( pConfig->bColumnsize ){
|
||||
rc = sqlite3Fts5StorageDocsize(p, ctx.iRowid, aColSize);
|
||||
}
|
||||
if( rc==SQLITE_OK && pConfig->eDetail==FTS5_DETAIL_NONE ){
|
||||
rc = sqlite3Fts5TermsetNew(&ctx.pTermset);
|
||||
}
|
||||
for(i=0; rc==SQLITE_OK && i<pConfig->nCol; i++){
|
||||
if( pConfig->abUnindexed[i] ) continue;
|
||||
ctx.iCol = i;
|
||||
ctx.szCol = 0;
|
||||
rc = sqlite3Fts5Tokenize(pConfig,
|
||||
FTS5_TOKENIZE_DOCUMENT,
|
||||
(const char*)sqlite3_column_text(pScan, i+1),
|
||||
sqlite3_column_bytes(pScan, i+1),
|
||||
(void*)&ctx,
|
||||
fts5StorageIntegrityCallback
|
||||
);
|
||||
if( pConfig->bColumnsize && ctx.szCol!=aColSize[i] ){
|
||||
if( pConfig->eDetail==FTS5_DETAIL_COLUMNS ){
|
||||
rc = sqlite3Fts5TermsetNew(&ctx.pTermset);
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3Fts5Tokenize(pConfig,
|
||||
FTS5_TOKENIZE_DOCUMENT,
|
||||
(const char*)sqlite3_column_text(pScan, i+1),
|
||||
sqlite3_column_bytes(pScan, i+1),
|
||||
(void*)&ctx,
|
||||
fts5StorageIntegrityCallback
|
||||
);
|
||||
}
|
||||
if( rc==SQLITE_OK && pConfig->bColumnsize && ctx.szCol!=aColSize[i] ){
|
||||
rc = FTS5_CORRUPT;
|
||||
}
|
||||
aTotalSize[i] += ctx.szCol;
|
||||
if( pConfig->eDetail==FTS5_DETAIL_COLUMNS ){
|
||||
sqlite3Fts5TermsetFree(ctx.pTermset);
|
||||
ctx.pTermset = 0;
|
||||
}
|
||||
}
|
||||
sqlite3Fts5TermsetFree(ctx.pTermset);
|
||||
ctx.pTermset = 0;
|
||||
|
||||
if( rc!=SQLITE_OK ) break;
|
||||
}
|
||||
rc2 = sqlite3_reset(pScan);
|
||||
|
@ -235,6 +235,8 @@ static int xF5tApi(
|
||||
{ "xGetAuxdata", 1, "CLEAR" }, /* 13 */
|
||||
{ "xSetAuxdataInt", 1, "INTEGER" }, /* 14 */
|
||||
{ "xGetAuxdataInt", 1, "CLEAR" }, /* 15 */
|
||||
{ "xPhraseForeach", 4, "IPHRASE COLVAR OFFVAR SCRIPT" }, /* 16 */
|
||||
{ "xPhraseColumnForeach", 3, "IPHRASE COLVAR SCRIPT" }, /* 17 */
|
||||
{ 0, 0, 0}
|
||||
};
|
||||
|
||||
@ -431,6 +433,62 @@ static int xF5tApi(
|
||||
break;
|
||||
}
|
||||
|
||||
CASE(16, "xPhraseForeach") {
|
||||
int iPhrase;
|
||||
int iCol;
|
||||
int iOff;
|
||||
const char *zColvar;
|
||||
const char *zOffvar;
|
||||
Tcl_Obj *pScript = objv[5];
|
||||
Fts5PhraseIter iter;
|
||||
|
||||
if( Tcl_GetIntFromObj(interp, objv[2], &iPhrase) ) return TCL_ERROR;
|
||||
zColvar = Tcl_GetString(objv[3]);
|
||||
zOffvar = Tcl_GetString(objv[4]);
|
||||
|
||||
for(p->pApi->xPhraseFirst(p->pFts, iPhrase, &iter, &iCol, &iOff);
|
||||
iCol>=0;
|
||||
p->pApi->xPhraseNext(p->pFts, &iter, &iCol, &iOff)
|
||||
){
|
||||
Tcl_SetVar2Ex(interp, zColvar, 0, Tcl_NewIntObj(iCol), 0);
|
||||
Tcl_SetVar2Ex(interp, zOffvar, 0, Tcl_NewIntObj(iOff), 0);
|
||||
rc = Tcl_EvalObjEx(interp, pScript, 0);
|
||||
if( rc==TCL_CONTINUE ) rc = TCL_OK;
|
||||
if( rc!=TCL_OK ){
|
||||
if( rc==TCL_BREAK ) rc = TCL_OK;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
CASE(17, "xPhraseColumnForeach") {
|
||||
int iPhrase;
|
||||
int iCol;
|
||||
const char *zColvar;
|
||||
Tcl_Obj *pScript = objv[4];
|
||||
Fts5PhraseIter iter;
|
||||
|
||||
if( Tcl_GetIntFromObj(interp, objv[2], &iPhrase) ) return TCL_ERROR;
|
||||
zColvar = Tcl_GetString(objv[3]);
|
||||
|
||||
for(p->pApi->xPhraseFirstColumn(p->pFts, iPhrase, &iter, &iCol);
|
||||
iCol>=0;
|
||||
p->pApi->xPhraseNextColumn(p->pFts, &iter, &iCol)
|
||||
){
|
||||
Tcl_SetVar2Ex(interp, zColvar, 0, Tcl_NewIntObj(iCol), 0);
|
||||
rc = Tcl_EvalObjEx(interp, pScript, 0);
|
||||
if( rc==TCL_CONTINUE ) rc = TCL_OK;
|
||||
if( rc!=TCL_OK ){
|
||||
if( rc==TCL_BREAK ) rc = TCL_OK;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
assert( 0 );
|
||||
break;
|
||||
|
@ -134,7 +134,7 @@ static int fts5MatchinfoXCb(
|
||||
int iPrev = -1;
|
||||
|
||||
for(pApi->xPhraseFirst(pFts, 0, &iter, &iCol, &iOff);
|
||||
iOff>=0;
|
||||
iCol>=0;
|
||||
pApi->xPhraseNext(pFts, &iter, &iCol, &iOff)
|
||||
){
|
||||
aOut[iCol*3+1]++;
|
||||
@ -211,18 +211,31 @@ static int fts5MatchinfoLocalCb(
|
||||
int rc = SQLITE_OK;
|
||||
|
||||
switch( f ){
|
||||
case 'b':
|
||||
case 'b': {
|
||||
int iPhrase;
|
||||
int nInt = ((p->nCol + 31) / 32) * p->nPhrase;
|
||||
for(i=0; i<nInt; i++) aOut[i] = 0;
|
||||
|
||||
for(iPhrase=0; iPhrase<p->nPhrase; iPhrase++){
|
||||
Fts5PhraseIter iter;
|
||||
int iCol;
|
||||
for(pApi->xPhraseFirstColumn(pFts, iPhrase, &iter, &iCol);
|
||||
iCol>=0;
|
||||
pApi->xPhraseNextColumn(pFts, &iter, &iCol)
|
||||
){
|
||||
aOut[iPhrase * ((p->nCol+31)/32) + iCol/32] |= ((u32)1 << iCol%32);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case 'x':
|
||||
case 'y': {
|
||||
int nMul = (f=='x' ? 3 : 1);
|
||||
int iPhrase;
|
||||
|
||||
if( f=='b' ){
|
||||
int nInt = ((p->nCol + 31) / 32) * p->nPhrase;
|
||||
for(i=0; i<nInt; i++) aOut[i] = 0;
|
||||
}else{
|
||||
for(i=0; i<(p->nCol*p->nPhrase); i++) aOut[i*nMul] = 0;
|
||||
}
|
||||
for(i=0; i<(p->nCol*p->nPhrase); i++) aOut[i*nMul] = 0;
|
||||
|
||||
for(iPhrase=0; iPhrase<p->nPhrase; iPhrase++){
|
||||
Fts5PhraseIter iter;
|
||||
|
@ -379,7 +379,7 @@ static int fts5VocabNextMethod(sqlite3_vtab_cursor *pCursor){
|
||||
|
||||
if( pTab->eType==FTS5_VOCAB_COL ){
|
||||
for(pCsr->iCol++; pCsr->iCol<nCol; pCsr->iCol++){
|
||||
if( pCsr->aCnt[pCsr->iCol] ) break;
|
||||
if( pCsr->aDoc[pCsr->iCol] ) break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -412,24 +412,52 @@ static int fts5VocabNextMethod(sqlite3_vtab_cursor *pCursor){
|
||||
i64 iPos = 0; /* 64-bit position read from poslist */
|
||||
int iOff = 0; /* Current offset within position list */
|
||||
|
||||
rc = sqlite3Fts5IterPoslist(pCsr->pIter, 0, &pPos, &nPos, &dummy);
|
||||
if( rc==SQLITE_OK ){
|
||||
if( pTab->eType==FTS5_VOCAB_ROW ){
|
||||
while( 0==sqlite3Fts5PoslistNext64(pPos, nPos, &iOff, &iPos) ){
|
||||
pCsr->aCnt[0]++;
|
||||
}
|
||||
pCsr->aDoc[0]++;
|
||||
}else{
|
||||
int iCol = -1;
|
||||
while( 0==sqlite3Fts5PoslistNext64(pPos, nPos, &iOff, &iPos) ){
|
||||
int ii = FTS5_POS2COLUMN(iPos);
|
||||
pCsr->aCnt[ii]++;
|
||||
if( iCol!=ii ){
|
||||
pCsr->aDoc[ii]++;
|
||||
iCol = ii;
|
||||
switch( pCsr->pConfig->eDetail ){
|
||||
case FTS5_DETAIL_FULL:
|
||||
rc = sqlite3Fts5IterPoslist(pCsr->pIter, 0, &pPos, &nPos, &dummy);
|
||||
if( rc==SQLITE_OK ){
|
||||
if( pTab->eType==FTS5_VOCAB_ROW ){
|
||||
while( 0==sqlite3Fts5PoslistNext64(pPos, nPos, &iOff, &iPos) ){
|
||||
pCsr->aCnt[0]++;
|
||||
}
|
||||
pCsr->aDoc[0]++;
|
||||
}else{
|
||||
int iCol = -1;
|
||||
while( 0==sqlite3Fts5PoslistNext64(pPos, nPos, &iOff, &iPos) ){
|
||||
int ii = FTS5_POS2COLUMN(iPos);
|
||||
pCsr->aCnt[ii]++;
|
||||
if( iCol!=ii ){
|
||||
pCsr->aDoc[ii]++;
|
||||
iCol = ii;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case FTS5_DETAIL_COLUMNS:
|
||||
if( pTab->eType==FTS5_VOCAB_ROW ){
|
||||
pCsr->aDoc[0]++;
|
||||
}else{
|
||||
Fts5Buffer buf = {0, 0, 0};
|
||||
rc = sqlite3Fts5IterPoslistBuffer(pCsr->pIter, &buf);
|
||||
if( rc==SQLITE_OK ){
|
||||
while( 0==sqlite3Fts5PoslistNext64(buf.p, buf.n, &iOff,&iPos) ){
|
||||
assert_nc( iPos>=0 && iPos<nCol );
|
||||
if( iPos<nCol ) pCsr->aDoc[iPos]++;
|
||||
}
|
||||
}
|
||||
sqlite3Fts5BufferFree(&buf);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
assert( pCsr->pConfig->eDetail==FTS5_DETAIL_NONE );
|
||||
pCsr->aDoc[0]++;
|
||||
break;
|
||||
}
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3Fts5IterNextScan(pCsr->pIter);
|
||||
}
|
||||
|
||||
@ -445,7 +473,7 @@ static int fts5VocabNextMethod(sqlite3_vtab_cursor *pCursor){
|
||||
}
|
||||
|
||||
if( pCsr->bEof==0 && pTab->eType==FTS5_VOCAB_COL ){
|
||||
while( pCsr->aCnt[pCsr->iCol]==0 ) pCsr->iCol++;
|
||||
while( pCsr->aDoc[pCsr->iCol]==0 ) pCsr->iCol++;
|
||||
assert( pCsr->iCol<pCsr->pConfig->nCol );
|
||||
}
|
||||
return rc;
|
||||
@ -525,30 +553,36 @@ static int fts5VocabColumnMethod(
|
||||
int iCol /* Index of column to read value from */
|
||||
){
|
||||
Fts5VocabCursor *pCsr = (Fts5VocabCursor*)pCursor;
|
||||
int eDetail = pCsr->pConfig->eDetail;
|
||||
int eType = ((Fts5VocabTable*)(pCursor->pVtab))->eType;
|
||||
i64 iVal = 0;
|
||||
|
||||
if( iCol==0 ){
|
||||
sqlite3_result_text(
|
||||
pCtx, (const char*)pCsr->term.p, pCsr->term.n, SQLITE_TRANSIENT
|
||||
);
|
||||
}
|
||||
else if( ((Fts5VocabTable*)(pCursor->pVtab))->eType==FTS5_VOCAB_COL ){
|
||||
}else if( eType==FTS5_VOCAB_COL ){
|
||||
assert( iCol==1 || iCol==2 || iCol==3 );
|
||||
if( iCol==1 ){
|
||||
const char *z = pCsr->pConfig->azCol[pCsr->iCol];
|
||||
sqlite3_result_text(pCtx, z, -1, SQLITE_STATIC);
|
||||
if( eDetail!=FTS5_DETAIL_NONE ){
|
||||
const char *z = pCsr->pConfig->azCol[pCsr->iCol];
|
||||
sqlite3_result_text(pCtx, z, -1, SQLITE_STATIC);
|
||||
}
|
||||
}else if( iCol==2 ){
|
||||
sqlite3_result_int64(pCtx, pCsr->aDoc[pCsr->iCol]);
|
||||
iVal = pCsr->aDoc[pCsr->iCol];
|
||||
}else{
|
||||
sqlite3_result_int64(pCtx, pCsr->aCnt[pCsr->iCol]);
|
||||
iVal = pCsr->aCnt[pCsr->iCol];
|
||||
}
|
||||
}else{
|
||||
assert( iCol==1 || iCol==2 );
|
||||
if( iCol==1 ){
|
||||
sqlite3_result_int64(pCtx, pCsr->aDoc[0]);
|
||||
iVal = pCsr->aDoc[0];
|
||||
}else{
|
||||
sqlite3_result_int64(pCtx, pCsr->aCnt[0]);
|
||||
iVal = pCsr->aCnt[0];
|
||||
}
|
||||
}
|
||||
|
||||
if( iVal>0 ) sqlite3_result_int64(pCtx, iVal);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
|
@ -28,6 +28,28 @@ proc fts5_test_poslist {cmd} {
|
||||
set res
|
||||
}
|
||||
|
||||
proc fts5_test_poslist2 {cmd} {
|
||||
set res [list]
|
||||
|
||||
for {set i 0} {$i < [$cmd xPhraseCount]} {incr i} {
|
||||
$cmd xPhraseForeach $i c o {
|
||||
lappend res $i.$c.$o
|
||||
}
|
||||
}
|
||||
|
||||
set res
|
||||
}
|
||||
|
||||
proc fts5_test_collist {cmd} {
|
||||
set res [list]
|
||||
|
||||
for {set i 0} {$i < [$cmd xPhraseCount]} {incr i} {
|
||||
$cmd xPhraseColumnForeach $i c { lappend res $i.$c }
|
||||
}
|
||||
|
||||
set res
|
||||
}
|
||||
|
||||
proc fts5_test_columnsize {cmd} {
|
||||
set res [list]
|
||||
for {set i 0} {$i < [$cmd xColumnCount]} {incr i} {
|
||||
@ -113,6 +135,8 @@ proc fts5_aux_test_functions {db} {
|
||||
fts5_test_columntext
|
||||
fts5_test_columntotalsize
|
||||
fts5_test_poslist
|
||||
fts5_test_poslist2
|
||||
fts5_test_collist
|
||||
fts5_test_tokenize
|
||||
fts5_test_rowcount
|
||||
fts5_test_all
|
||||
@ -178,15 +202,24 @@ proc fts5_rnddoc {n} {
|
||||
# -near N (NEAR distance. Default 10)
|
||||
# -col C (List of column indexes to match against)
|
||||
# -pc VARNAME (variable in caller frame to use for phrase numbering)
|
||||
# -dict VARNAME (array in caller frame to use for synonyms)
|
||||
#
|
||||
proc nearset {aCol args} {
|
||||
|
||||
# Process the command line options.
|
||||
#
|
||||
set O(-near) 10
|
||||
set O(-col) {}
|
||||
set O(-pc) ""
|
||||
set O(-dict) ""
|
||||
|
||||
set nOpt [lsearch -exact $args --]
|
||||
if {$nOpt<0} { error "no -- option" }
|
||||
|
||||
# Set $lPhrase to be a list of phrases. $nPhrase its length.
|
||||
set lPhrase [lrange $args [expr $nOpt+1] end]
|
||||
set nPhrase [llength $lPhrase]
|
||||
|
||||
foreach {k v} [lrange $args 0 [expr $nOpt-1]] {
|
||||
if {[info exists O($k)]==0} { error "unrecognized option $k" }
|
||||
set O($k) $v
|
||||
@ -198,9 +231,7 @@ proc nearset {aCol args} {
|
||||
upvar $O(-pc) counter
|
||||
}
|
||||
|
||||
# Set $phraselist to be a list of phrases. $nPhrase its length.
|
||||
set phraselist [lrange $args [expr $nOpt+1] end]
|
||||
set nPhrase [llength $phraselist]
|
||||
if {$O(-dict)!=""} { upvar $O(-dict) aDict }
|
||||
|
||||
for {set j 0} {$j < [llength $aCol]} {incr j} {
|
||||
for {set i 0} {$i < $nPhrase} {incr i} {
|
||||
@ -208,27 +239,54 @@ proc nearset {aCol args} {
|
||||
}
|
||||
}
|
||||
|
||||
set iCol -1
|
||||
foreach col $aCol {
|
||||
incr iCol
|
||||
if {$O(-col)!="" && [lsearch $O(-col) $iCol]<0} continue
|
||||
set nToken [llength $col]
|
||||
# Loop through each column of the current row.
|
||||
for {set iCol 0} {$iCol < [llength $aCol]} {incr iCol} {
|
||||
|
||||
set iFL [expr $O(-near) >= $nToken ? $nToken - 1 : $O(-near)]
|
||||
for { } {$iFL < $nToken} {incr iFL} {
|
||||
for {set iPhrase 0} {$iPhrase<$nPhrase} {incr iPhrase} {
|
||||
set B($iPhrase) [list]
|
||||
}
|
||||
# If there is a column filter, test whether this column is excluded. If
|
||||
# so, skip to the next iteration of this loop. Otherwise, set zCol to the
|
||||
# column value and nToken to the number of tokens that comprise it.
|
||||
if {$O(-col)!="" && [lsearch $O(-col) $iCol]<0} continue
|
||||
set zCol [lindex $aCol $iCol]
|
||||
set nToken [llength $zCol]
|
||||
|
||||
# Each iteration of the following loop searches a substring of the
|
||||
# column value for phrase matches. The last token of the substring
|
||||
# is token $iLast of the column value. The first token is:
|
||||
#
|
||||
# iFirst = ($iLast - $O(-near) - 1)
|
||||
#
|
||||
# where $sz is the length of the phrase being searched for. A phrase
|
||||
# counts as matching the substring if its first token lies on or before
|
||||
# $iLast and its last token on or after $iFirst.
|
||||
#
|
||||
# For example, if the query is "NEAR(a+b c, 2)" and the column value:
|
||||
#
|
||||
# "x x x x A B x x C x"
|
||||
# 0 1 2 3 4 5 6 7 8 9"
|
||||
#
|
||||
# when (iLast==8 && iFirst=5) the range will contain both phrases and
|
||||
# so both instances can be added to the output poslists.
|
||||
#
|
||||
set iLast [expr $O(-near) >= $nToken ? $nToken - 1 : $O(-near)]
|
||||
for { } {$iLast < $nToken} {incr iLast} {
|
||||
|
||||
catch { array unset B }
|
||||
|
||||
for {set iPhrase 0} {$iPhrase<$nPhrase} {incr iPhrase} {
|
||||
set p [lindex $phraselist $iPhrase]
|
||||
set p [lindex $lPhrase $iPhrase]
|
||||
set nPm1 [expr {[llength $p] - 1}]
|
||||
set iFirst [expr $iFL - $O(-near) - [llength $p]]
|
||||
set iFirst [expr $iLast - $O(-near) - [llength $p]]
|
||||
|
||||
for {set i $iFirst} {$i <= $iFL} {incr i} {
|
||||
if {[lrange $col $i [expr $i+$nPm1]] == $p} { lappend B($iPhrase) $i }
|
||||
for {set i $iFirst} {$i <= $iLast} {incr i} {
|
||||
set lCand [lrange $zCol $i [expr $i+$nPm1]]
|
||||
set bMatch 1
|
||||
foreach tok $p term $lCand {
|
||||
if {[nearset_match aDict $tok $term]==0} { set bMatch 0 ; break }
|
||||
}
|
||||
if {$bMatch} { lappend B($iPhrase) $i }
|
||||
}
|
||||
if {[llength $B($iPhrase)] == 0} break
|
||||
|
||||
if {![info exists B($iPhrase)]} break
|
||||
}
|
||||
|
||||
if {$iPhrase==$nPhrase} {
|
||||
@ -252,10 +310,22 @@ proc nearset {aCol args} {
|
||||
incr counter
|
||||
}
|
||||
|
||||
#puts $res
|
||||
#puts "$aCol -> $res"
|
||||
sort_poslist $res
|
||||
}
|
||||
|
||||
proc nearset_match {aDictVar tok term} {
|
||||
if {[string match $tok $term]} { return 1 }
|
||||
|
||||
upvar $aDictVar aDict
|
||||
if {[info exists aDict($tok)]} {
|
||||
foreach s $aDict($tok) {
|
||||
if {[string match $s $term]} { return 1 }
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Usage:
|
||||
#
|
||||
@ -327,3 +397,167 @@ proc fts5_tokenize_split {text} {
|
||||
set ret
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
proc foreach_detail_mode {prefix script} {
|
||||
set saved $::testprefix
|
||||
foreach d [list full col none] {
|
||||
set s [string map [list %DETAIL% $d] $script]
|
||||
set ::detail $d
|
||||
set ::testprefix "$prefix-$d"
|
||||
reset_db
|
||||
uplevel $s
|
||||
unset ::detail
|
||||
}
|
||||
set ::testprefix $saved
|
||||
}
|
||||
|
||||
proc detail_check {} {
|
||||
if {$::detail != "none" && $::detail!="full" && $::detail!="col"} {
|
||||
error "not in foreach_detail_mode {...} block"
|
||||
}
|
||||
}
|
||||
proc detail_is_none {} { detail_check ; expr {$::detail == "none"} }
|
||||
proc detail_is_col {} { detail_check ; expr {$::detail == "col" } }
|
||||
proc detail_is_full {} { detail_check ; expr {$::detail == "full"} }
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Convert a poslist of the type returned by fts5_test_poslist() to a
|
||||
# collist as returned by fts5_test_collist().
|
||||
#
|
||||
proc fts5_poslist2collist {poslist} {
|
||||
set res [list]
|
||||
foreach h $poslist {
|
||||
regexp {(.*)\.[1234567890]+} $h -> cand
|
||||
lappend res $cand
|
||||
}
|
||||
set res [lsort -command fts5_collist_elem_compare -unique $res]
|
||||
return $res
|
||||
}
|
||||
|
||||
# Comparison function used by fts5_poslist2collist to sort collist entries.
|
||||
proc fts5_collist_elem_compare {a b} {
|
||||
foreach {a1 a2} [split $a .] {}
|
||||
foreach {b1 b2} [split $b .] {}
|
||||
|
||||
if {$a1==$b1} { return [expr $a2 - $b2] }
|
||||
return [expr $a1 - $b1]
|
||||
}
|
||||
|
||||
|
||||
#--------------------------------------------------------------------------
|
||||
# Construct and return a tcl list equivalent to that returned by the SQL
|
||||
# query executed against database handle [db]:
|
||||
#
|
||||
# SELECT
|
||||
# rowid,
|
||||
# fts5_test_poslist($tbl),
|
||||
# fts5_test_collist($tbl)
|
||||
# FROM $tbl('$expr')
|
||||
# ORDER BY rowid $order;
|
||||
#
|
||||
proc fts5_query_data {expr tbl {order ASC} {aDictVar ""}} {
|
||||
|
||||
# Figure out the set of columns in the FTS5 table. This routine does
|
||||
# not handle tables with UNINDEXED columns, but if it did, it would
|
||||
# have to be here.
|
||||
db eval "PRAGMA table_info = $tbl" x { lappend lCols $x(name) }
|
||||
|
||||
set d ""
|
||||
if {$aDictVar != ""} {
|
||||
upvar $aDictVar aDict
|
||||
set d aDict
|
||||
}
|
||||
|
||||
set cols ""
|
||||
foreach e $lCols { append cols ", '$e'" }
|
||||
set tclexpr [db one [subst -novar {
|
||||
SELECT fts5_expr_tcl( $expr, 'nearset $cols -dict $d -pc ::pc' [set cols] )
|
||||
}]]
|
||||
|
||||
set res [list]
|
||||
db eval "SELECT rowid, * FROM $tbl ORDER BY rowid $order" x {
|
||||
set cols [list]
|
||||
foreach col $lCols { lappend cols $x($col) }
|
||||
|
||||
set ::pc 0
|
||||
set rowdata [eval $tclexpr]
|
||||
if {$rowdata != ""} {
|
||||
lappend res $x(rowid) $rowdata [fts5_poslist2collist $rowdata]
|
||||
}
|
||||
}
|
||||
|
||||
set res
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Similar to [fts5_query_data], but omit the collist field.
|
||||
#
|
||||
proc fts5_poslist_data {expr tbl {order ASC} {aDictVar ""}} {
|
||||
set res [list]
|
||||
|
||||
if {$aDictVar!=""} {
|
||||
upvar $aDictVar aDict
|
||||
set dict aDict
|
||||
} else {
|
||||
set dict ""
|
||||
}
|
||||
|
||||
foreach {rowid poslist collist} [fts5_query_data $expr $tbl $order $dict] {
|
||||
lappend res $rowid $poslist
|
||||
}
|
||||
set res
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
|
||||
# This command will only work inside a [foreach_detail_mode] block. It tests
|
||||
# whether or not expression $expr run on FTS5 table $tbl is supported by
|
||||
# the current mode. If so, 1 is returned. If not, 0.
|
||||
#
|
||||
# detail=full (all queries supported)
|
||||
# detail=col (all but phrase queries and NEAR queries)
|
||||
# detail=none (all but phrase queries, NEAR queries, and column filters)
|
||||
#
|
||||
proc fts5_expr_ok {expr tbl} {
|
||||
|
||||
if {![detail_is_full]} {
|
||||
set nearset "nearset_rc"
|
||||
if {[detail_is_col]} { set nearset "nearset_rf" }
|
||||
|
||||
set ::expr_not_ok 0
|
||||
db eval "PRAGMA table_info = $tbl" x { lappend lCols $x(name) }
|
||||
|
||||
set cols ""
|
||||
foreach e $lCols { append cols ", '$e'" }
|
||||
set ::pc 0
|
||||
set tclexpr [db one [subst -novar {
|
||||
SELECT fts5_expr_tcl( $expr, '[set nearset] $cols -pc ::pc' [set cols] )
|
||||
}]]
|
||||
eval $tclexpr
|
||||
if {$::expr_not_ok} { return 0 }
|
||||
}
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
# Helper for [fts5_expr_ok]
|
||||
proc nearset_rf {aCol args} {
|
||||
set idx [lsearch -exact $args --]
|
||||
if {$idx != [llength $args]-2 || [llength [lindex $args end]]!=1} {
|
||||
set ::expr_not_ok 1
|
||||
}
|
||||
list
|
||||
}
|
||||
|
||||
# Helper for [fts5_expr_ok]
|
||||
proc nearset_rc {aCol args} {
|
||||
nearset_rf $aCol {*}$args
|
||||
if {[lsearch $args -col]>=0} {
|
||||
set ::expr_not_ok 1
|
||||
}
|
||||
list
|
||||
}
|
||||
|
||||
|
@ -21,6 +21,8 @@ ifcapable !fts5 {
|
||||
return
|
||||
}
|
||||
|
||||
foreach_detail_mode $::testprefix {
|
||||
|
||||
do_execsql_test 1.0 {
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(a, b, c);
|
||||
SELECT name, sql FROM sqlite_master;
|
||||
@ -41,9 +43,9 @@ do_execsql_test 1.1 {
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
reset_db
|
||||
|
||||
do_execsql_test 2.0 {
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(x,y);
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(x, y, detail=%DETAIL%);
|
||||
}
|
||||
do_execsql_test 2.1 {
|
||||
INSERT INTO t1 VALUES('a b c', 'd e f');
|
||||
@ -66,11 +68,12 @@ do_execsql_test 2.4 {
|
||||
INSERT INTO t1(t1) VALUES('integrity-check');
|
||||
}
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 3.0 {
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(x,y);
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(x,y, detail=%DETAIL%);
|
||||
}
|
||||
foreach {i x y} {
|
||||
1 {g f d b f} {h h e i a}
|
||||
@ -93,7 +96,7 @@ foreach {i x y} {
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 4.0 {
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(x,y);
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(x,y, detail=%DETAIL%);
|
||||
INSERT INTO t1(t1, rank) VALUES('pgsz', 32);
|
||||
}
|
||||
foreach {i x y} {
|
||||
@ -117,7 +120,7 @@ foreach {i x y} {
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 5.0 {
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(x,y);
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(x,y, detail=%DETAIL%);
|
||||
INSERT INTO t1(t1, rank) VALUES('pgsz', 32);
|
||||
}
|
||||
foreach {i x y} {
|
||||
@ -141,7 +144,7 @@ foreach {i x y} {
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 6.0 {
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(x,y);
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(x,y, detail=%DETAIL%);
|
||||
INSERT INTO t1(t1, rank) VALUES('pgsz', 32);
|
||||
}
|
||||
|
||||
@ -276,7 +279,7 @@ for {set i 1} {$i <= 10} {incr i} {
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 10.0 {
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(x,y);
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(x,y, detail=%DETAIL%);
|
||||
}
|
||||
set d10 {
|
||||
1 {g f d b f} {h h e i a}
|
||||
@ -309,19 +312,19 @@ do_execsql_test 10.4.2 { INSERT INTO t1(t1) VALUES('integrity-check') }
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
do_catchsql_test 11.1 {
|
||||
CREATE VIRTUAL TABLE t2 USING fts5(a, b, c, rank);
|
||||
CREATE VIRTUAL TABLE t2 USING fts5(a, b, c, rank, detail=%DETAIL%);
|
||||
} {1 {reserved fts5 column name: rank}}
|
||||
do_catchsql_test 11.2 {
|
||||
CREATE VIRTUAL TABLE rank USING fts5(a, b, c);
|
||||
CREATE VIRTUAL TABLE rank USING fts5(a, b, c, detail=%DETAIL%);
|
||||
} {1 {reserved fts5 table name: rank}}
|
||||
do_catchsql_test 11.3 {
|
||||
CREATE VIRTUAL TABLE t2 USING fts5(a, b, c, rowid);
|
||||
CREATE VIRTUAL TABLE t2 USING fts5(a, b, c, rowid, detail=%DETAIL%);
|
||||
} {1 {reserved fts5 column name: rowid}}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
do_execsql_test 12.1 {
|
||||
CREATE VIRTUAL TABLE t2 USING fts5(x,y);
|
||||
CREATE VIRTUAL TABLE t2 USING fts5(x,y, detail=%DETAIL%);
|
||||
} {}
|
||||
|
||||
do_catchsql_test 12.2 {
|
||||
@ -337,7 +340,7 @@ do_test 12.3 {
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 13.1 {
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(x);
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(x, detail=%DETAIL%);
|
||||
INSERT INTO t1(rowid, x) VALUES(1, 'o n e'), (2, 't w o');
|
||||
} {}
|
||||
|
||||
@ -361,7 +364,7 @@ do_execsql_test 13.6 {
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 14.1 {
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(x, y);
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(x, y, detail=%DETAIL%);
|
||||
INSERT INTO t1(t1, rank) VALUES('pgsz', 32);
|
||||
WITH d(x,y) AS (
|
||||
SELECT NULL, 'xyz xyz xyz xyz xyz xyz'
|
||||
@ -371,6 +374,10 @@ do_execsql_test 14.1 {
|
||||
INSERT INTO t1 SELECT * FROM d LIMIT 200;
|
||||
}
|
||||
|
||||
do_execsql_test 15.x {
|
||||
INSERT INTO t1(t1) VALUES('integrity-check');
|
||||
}
|
||||
|
||||
do_test 14.2 {
|
||||
set nRow 0
|
||||
db eval { SELECT * FROM t1 WHERE t1 MATCH 'xyz' } {
|
||||
@ -417,11 +424,11 @@ do_execsql_test 16.1 {
|
||||
}
|
||||
|
||||
proc funk {} {
|
||||
db eval { UPDATE n1_config SET v=50 WHERE k='version' }
|
||||
set fd [db incrblob main n1_data block 10]
|
||||
fconfigure $fd -encoding binary -translation binary
|
||||
puts -nonewline $fd "\x44\x45"
|
||||
close $fd
|
||||
db eval { UPDATE n1_config SET v=50 WHERE k='version' }
|
||||
}
|
||||
db func funk funk
|
||||
|
||||
@ -433,7 +440,7 @@ do_catchsql_test 16.2 {
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 17.1 {
|
||||
CREATE VIRTUAL TABLE b2 USING fts5(x);
|
||||
CREATE VIRTUAL TABLE b2 USING fts5(x, detail=%DETAIL%);
|
||||
INSERT INTO b2 VALUES('a');
|
||||
INSERT INTO b2 VALUES('b');
|
||||
INSERT INTO b2 VALUES('c');
|
||||
@ -447,18 +454,20 @@ do_test 17.2 {
|
||||
set res
|
||||
} {{a b c} {a b c} {a b c}}
|
||||
|
||||
reset_db
|
||||
do_execsql_test 18.1 {
|
||||
CREATE VIRTUAL TABLE c2 USING fts5(x, y);
|
||||
INSERT INTO c2 VALUES('x x x', 'x x x');
|
||||
SELECT rowid FROM c2 WHERE c2 MATCH 'y:x';
|
||||
} {1}
|
||||
if {[string match n* %DETAIL%]==0} {
|
||||
reset_db
|
||||
do_execsql_test 17.3 {
|
||||
CREATE VIRTUAL TABLE c2 USING fts5(x, y, detail=%DETAIL%);
|
||||
INSERT INTO c2 VALUES('x x x', 'x x x');
|
||||
SELECT rowid FROM c2 WHERE c2 MATCH 'y:x';
|
||||
} {1}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 17.1 {
|
||||
CREATE VIRTUAL TABLE uio USING fts5(ttt);
|
||||
CREATE VIRTUAL TABLE uio USING fts5(ttt, detail=%DETAIL%);
|
||||
INSERT INTO uio VALUES(NULL);
|
||||
INSERT INTO uio SELECT NULL FROM uio;
|
||||
INSERT INTO uio SELECT NULL FROM uio;
|
||||
@ -505,8 +514,8 @@ do_execsql_test 17.9 {
|
||||
#--------------------------------------------------------------------
|
||||
#
|
||||
do_execsql_test 18.1 {
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(a, b);
|
||||
CREATE VIRTUAL TABLE t2 USING fts5(c, d);
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(a, b, detail=%DETAIL%);
|
||||
CREATE VIRTUAL TABLE t2 USING fts5(c, d, detail=%DETAIL%);
|
||||
INSERT INTO t1 VALUES('abc*', NULL);
|
||||
INSERT INTO t2 VALUES(1, 'abcdefg');
|
||||
}
|
||||
@ -522,7 +531,7 @@ do_execsql_test 18.3 {
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 19.0 {
|
||||
CREATE VIRTUAL TABLE temp.t1 USING fts5(x);
|
||||
CREATE VIRTUAL TABLE temp.t1 USING fts5(x, detail=%DETAIL%);
|
||||
INSERT INTO t1 VALUES('x y z');
|
||||
INSERT INTO t1 VALUES('w x 1');
|
||||
SELECT rowid FROM t1 WHERE t1 MATCH 'x';
|
||||
@ -533,7 +542,7 @@ do_execsql_test 19.0 {
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 20.0 {
|
||||
CREATE VIRTUAL TABLE temp.tmp USING fts5(x);
|
||||
CREATE VIRTUAL TABLE temp.tmp USING fts5(x, detail=%DETAIL%);
|
||||
}
|
||||
set ::ids [list \
|
||||
0 [expr 1<<36] [expr 2<<36] [expr 1<<43] [expr 2<<43]
|
||||
@ -545,6 +554,7 @@ do_test 20.1 {
|
||||
execsql { SELECT rowid FROM tmp WHERE tmp MATCH 'y' }
|
||||
} $::ids
|
||||
|
||||
}
|
||||
|
||||
|
||||
finish_test
|
||||
|
@ -22,8 +22,10 @@ ifcapable !fts5 {
|
||||
return
|
||||
}
|
||||
|
||||
foreach_detail_mode $testprefix {
|
||||
|
||||
do_execsql_test 1.0 {
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(a, b);
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(a, b, detail=%DETAIL%);
|
||||
INSERT INTO t1 VALUES('hello', 'world');
|
||||
INSERT INTO t1 VALUES('one two', 'three four');
|
||||
INSERT INTO t1(rowid, a, b) VALUES(45, 'forty', 'five');
|
||||
@ -57,7 +59,7 @@ do_execsql_test 1.6 {
|
||||
|
||||
reset_db
|
||||
do_execsql_test 2.1 {
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(x);
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(x, detail=%DETAIL%);
|
||||
INSERT INTO t1(t1, rank) VALUES('pgsz', 32);
|
||||
INSERT INTO t1 VALUES('one');
|
||||
INSERT INTO t1 VALUES('two');
|
||||
@ -159,7 +161,7 @@ foreach {tn expr res} {
|
||||
#
|
||||
|
||||
do_execsql_test 4.0 {
|
||||
CREATE VIRTUAL TABLE s1 USING fts5(x);
|
||||
CREATE VIRTUAL TABLE s1 USING fts5(x, detail=%DETAIL%);
|
||||
}
|
||||
foreach {tn doc} [list \
|
||||
1 [string repeat {a x } 1500000] \
|
||||
@ -172,8 +174,14 @@ do_execsql_test 4.3 {
|
||||
SELECT rowid FROM s1 WHERE s1 MATCH 'x'
|
||||
} {1 2}
|
||||
|
||||
do_execsql_test 4.4 {
|
||||
SELECT rowid FROM s1 WHERE s1 MATCH '"a x"'
|
||||
if {[detail_is_full]} {
|
||||
do_execsql_test 4.4 {
|
||||
SELECT rowid FROM s1 WHERE s1 MATCH '"a x"'
|
||||
} {1 2}
|
||||
}
|
||||
|
||||
do_execsql_test 4.5 {
|
||||
SELECT rowid FROM s1 WHERE s1 MATCH 'a x'
|
||||
} {1 2}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
@ -182,7 +190,7 @@ do_execsql_test 4.4 {
|
||||
# (L-2) is larger than it.
|
||||
#
|
||||
do_execsql_test 5.0 {
|
||||
CREATE VIRTUAL TABLE s2 USING fts5(x);
|
||||
CREATE VIRTUAL TABLE s2 USING fts5(x, detail=%DETAIL%);
|
||||
INSERT INTO s2(s2, rank) VALUES('pgsz', 32);
|
||||
INSERT INTO s2(s2, rank) VALUES('automerge', 0);
|
||||
}
|
||||
@ -222,7 +230,7 @@ do_test 5.2 {
|
||||
do_test 5.3 {
|
||||
execsql {
|
||||
DROP TABLE s2;
|
||||
CREATE VIRTUAL TABLE s2 USING fts5(x);
|
||||
CREATE VIRTUAL TABLE s2 USING fts5(x, detail=%DETAIL%);
|
||||
INSERT INTO s2(s2, rank) VALUES('pgsz', 32);
|
||||
INSERT INTO s2(s2, rank) VALUES('automerge', 0);
|
||||
}
|
||||
@ -241,7 +249,7 @@ do_test 5.4 {
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
do_execsql_test 6.0 {
|
||||
CREATE VIRTUAL TABLE s3 USING fts5(x);
|
||||
CREATE VIRTUAL TABLE s3 USING fts5(x, detail=%DETAIL%);
|
||||
BEGIN;
|
||||
INSERT INTO s3 VALUES('a b c');
|
||||
INSERT INTO s3 VALUES('A B C');
|
||||
@ -276,13 +284,13 @@ do_test 6.4 {
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
set doc [string repeat "a b c " 500]
|
||||
breakpoint
|
||||
do_execsql_test 7.0 {
|
||||
CREATE VIRTUAL TABLE x1 USING fts5(x);
|
||||
CREATE VIRTUAL TABLE x1 USING fts5(x, detail=%DETAIL%);
|
||||
INSERT INTO x1(x1, rank) VALUES('pgsz', 32);
|
||||
INSERT INTO x1 VALUES($doc);
|
||||
}
|
||||
|
||||
} ;# foreach_detail_mode...
|
||||
|
||||
|
||||
finish_test
|
||||
|
@ -22,6 +22,8 @@ ifcapable !fts5 {
|
||||
return
|
||||
}
|
||||
|
||||
foreach_detail_mode $testprefix {
|
||||
|
||||
set data {
|
||||
0 {p o q e z k z p n f y u z y n y} {l o o l v v k}
|
||||
1 {p k h h p y l l h i p v n} {p p l u r i f a j g e r r x w}
|
||||
@ -125,80 +127,21 @@ set data {
|
||||
99 {r c v w i v h a t a c v c r e} {h h u m g o f b a e o}
|
||||
}
|
||||
|
||||
# Argument $expr is an FTS5 match expression designed to be executed against
|
||||
# an FTS5 table with the following schema:
|
||||
#
|
||||
# CREATE VIRTUAL TABLE xy USING fts5(x, y);
|
||||
#
|
||||
# Assuming the table contains the same records as stored int the global
|
||||
# $::data array (see above), this function returns a list containing one
|
||||
# element for each match in the dataset. The elements are themselves lists
|
||||
# formatted as follows:
|
||||
#
|
||||
# <rowid> {<phrase 0 matches> <phrase 1 matches>...}
|
||||
#
|
||||
# where each <phrase X matches> element is a list of phrase matches in the
|
||||
# same form as returned by auxiliary scalar function fts5_test().
|
||||
#
|
||||
proc matchdata {bPos expr {bAsc 1}} {
|
||||
|
||||
set tclexpr [db one {
|
||||
SELECT fts5_expr_tcl($expr, 'nearset $cols -pc ::pc', 'x', 'y')
|
||||
}]
|
||||
set res [list]
|
||||
|
||||
#puts $tclexpr
|
||||
foreach {id x y} $::data {
|
||||
set cols [list $x $y]
|
||||
set ::pc 0
|
||||
#set hits [lsort -command instcompare [eval $tclexpr]]
|
||||
set hits [eval $tclexpr]
|
||||
if {[llength $hits]>0} {
|
||||
if {$bPos} {
|
||||
lappend res [list $id $hits]
|
||||
} else {
|
||||
lappend res $id
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if {$bAsc} {
|
||||
set res [lsort -integer -increasing -index 0 $res]
|
||||
} else {
|
||||
set res [lsort -integer -decreasing -index 0 $res]
|
||||
}
|
||||
|
||||
return [concat {*}$res]
|
||||
}
|
||||
|
||||
#
|
||||
# End of test code
|
||||
#-------------------------------------------------------------------------
|
||||
|
||||
proc fts5_test_poslist {cmd} {
|
||||
set res [list]
|
||||
for {set i 0} {$i < [$cmd xInstCount]} {incr i} {
|
||||
lappend res [string map {{ } .} [$cmd xInst $i]]
|
||||
}
|
||||
set res
|
||||
}
|
||||
|
||||
|
||||
foreach {tn2 sql} {
|
||||
1 {}
|
||||
2 {BEGIN}
|
||||
} {
|
||||
reset_db
|
||||
sqlite3_fts5_create_function db fts5_test_poslist fts5_test_poslist
|
||||
fts5_aux_test_functions db
|
||||
|
||||
do_execsql_test 1.0 {
|
||||
CREATE VIRTUAL TABLE xx USING fts5(x,y);
|
||||
do_execsql_test 1.$tn2.0 {
|
||||
CREATE VIRTUAL TABLE xx USING fts5(x,y, detail=%DETAIL%);
|
||||
INSERT INTO xx(xx, rank) VALUES('pgsz', 32);
|
||||
}
|
||||
|
||||
execsql $sql
|
||||
|
||||
do_test $tn2.1.1 {
|
||||
do_test 1.$tn2.1.1 {
|
||||
foreach {id x y} $data {
|
||||
execsql { INSERT INTO xx(rowid, x, y) VALUES($id, $x, $y) }
|
||||
}
|
||||
@ -207,153 +150,131 @@ foreach {tn2 sql} {
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Test phrase queries.
|
||||
#
|
||||
foreach {tn phrase} {
|
||||
1 "o"
|
||||
2 "b q"
|
||||
3 "e a e"
|
||||
4 "m d g q q b k b w f q q p p"
|
||||
5 "l o o l v v k"
|
||||
6 "a"
|
||||
7 "b"
|
||||
8 "c"
|
||||
9 "no"
|
||||
10 "L O O L V V K"
|
||||
} {
|
||||
set expr "\"$phrase\""
|
||||
set res [matchdata 1 $expr]
|
||||
|
||||
do_execsql_test $tn2.1.2.$tn.[llength $res] {
|
||||
SELECT rowid, fts5_test_poslist(xx) FROM xx WHERE xx match $expr
|
||||
} $res
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Test some AND and OR queries.
|
||||
#
|
||||
foreach {tn expr} {
|
||||
1.1 "a AND b"
|
||||
1.2 "a+b AND c"
|
||||
1.3 "d+c AND u"
|
||||
1.4 "d+c AND u+d"
|
||||
|
||||
2.1 "a OR b"
|
||||
2.2 "a+b OR c"
|
||||
2.3 "d+c OR u"
|
||||
2.4 "d+c OR u+d"
|
||||
|
||||
3.1 { a AND b AND c }
|
||||
} {
|
||||
set res [matchdata 1 $expr]
|
||||
do_execsql_test $tn2.2.$tn.[llength $res] {
|
||||
SELECT rowid, fts5_test_poslist(xx) FROM xx WHERE xx match $expr
|
||||
} $res
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Queries on a specific column.
|
||||
#
|
||||
foreach {tn expr} {
|
||||
1.1 "x:a"
|
||||
1.2 "y:a"
|
||||
1.3 "x:b"
|
||||
1.4 "y:b"
|
||||
2.1 "{x}:a"
|
||||
2.2 "{y}:a"
|
||||
2.3 "{x}:b"
|
||||
2.4 "{y}:b"
|
||||
|
||||
3.1 "{x y}:a"
|
||||
3.2 "{y x}:a"
|
||||
3.3 "{x x}:b"
|
||||
3.4 "{y y}:b"
|
||||
|
||||
4.1 {{"x" "y"}:a}
|
||||
4.2 {{"y" x}:a}
|
||||
4.3 {{x "x"}:b}
|
||||
4.4 {{"y" y}:b}
|
||||
} {
|
||||
set res [matchdata 1 $expr]
|
||||
do_execsql_test $tn2.3.$tn.[llength $res] {
|
||||
SELECT rowid, fts5_test_poslist(xx) FROM xx WHERE xx match $expr
|
||||
} $res
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Some NEAR queries.
|
||||
#
|
||||
foreach {tn expr} {
|
||||
1 "NEAR(a b)"
|
||||
2 "NEAR(r c)"
|
||||
2 { NEAR(r c, 5) }
|
||||
3 { NEAR(r c, 3) }
|
||||
4 { NEAR(r c, 2) }
|
||||
5 { NEAR(r c, 0) }
|
||||
6 { NEAR(a b c) }
|
||||
7 { NEAR(a b c, 8) }
|
||||
8 { x : NEAR(r c) }
|
||||
9 { y : NEAR(r c) }
|
||||
} {
|
||||
set res [matchdata 1 $expr]
|
||||
do_execsql_test $tn2.4.1.$tn.[llength $res] {
|
||||
SELECT rowid, fts5_test_poslist(xx) FROM xx WHERE xx match $expr
|
||||
} $res
|
||||
}
|
||||
|
||||
do_test $tn2.4.1 { nearset {{a b c}} -- a } {0.0.0}
|
||||
do_test $tn2.4.2 { nearset {{a b c}} -- c } {0.0.2}
|
||||
|
||||
foreach {tn expr tclexpr} {
|
||||
1 {a b} {AND [N $x -- {a}] [N $x -- {b}]}
|
||||
} {
|
||||
do_execsql_test $tn2.5.$tn {
|
||||
SELECT fts5_expr_tcl($expr, 'N $x')
|
||||
} [list $tclexpr]
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
do_execsql_test $tn2.6.integrity {
|
||||
do_execsql_test 1.$tn2.integrity {
|
||||
INSERT INTO xx(xx) VALUES('integrity-check');
|
||||
}
|
||||
#db eval {SELECT rowid, fts5_decode(rowid, block) aS r FROM xx_data} {puts $r}
|
||||
foreach {bAsc sql} {
|
||||
1 {SELECT rowid FROM xx WHERE xx MATCH $expr}
|
||||
0 {SELECT rowid FROM xx WHERE xx MATCH $expr ORDER BY rowid DESC}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
foreach {tn expr} {
|
||||
1.2 "a OR b"
|
||||
1.1 "a AND b"
|
||||
1.3 "o"
|
||||
1.4 "b q"
|
||||
1.5 "e a e"
|
||||
1.6 "m d g q q b k b w f q q p p"
|
||||
1.7 "l o o l v v k"
|
||||
1.8 "a"
|
||||
1.9 "b"
|
||||
1.10 "c"
|
||||
1.11 "no"
|
||||
1.12 "L O O L V V K"
|
||||
1.13 "a AND b AND c"
|
||||
1.14 "x:a"
|
||||
|
||||
2.1 "x:a"
|
||||
2.2 "y:a"
|
||||
2.3 "x:b"
|
||||
2.4 "y:b"
|
||||
|
||||
3.1 "{x}:a"
|
||||
3.2 "{y}:a"
|
||||
3.3 "{x}:b"
|
||||
3.4 "{y}:b"
|
||||
|
||||
4.1 "{x y}:a"
|
||||
4.2 "{y x}:a"
|
||||
4.3 "{x x}:b"
|
||||
4.4 "{y y}:b"
|
||||
|
||||
5.1 {{"x" "y"}:a}
|
||||
5.2 {{"y" x}:a}
|
||||
5.3 {{x "x"}:b}
|
||||
5.4 {{"y" y}:b}
|
||||
|
||||
6.1 "b + q"
|
||||
6.2 "e + a + e"
|
||||
6.3 "m + d + g + q + q + b + k + b + w + f + q + q + p + p"
|
||||
6.4 "l + o + o + l + v + v + k"
|
||||
6.5 "L + O + O + L + V + V + K"
|
||||
|
||||
7.1 "a+b AND c"
|
||||
7.2 "d+c AND u"
|
||||
7.3 "d+c AND u+d"
|
||||
7.4 "a+b OR c"
|
||||
7.5 "d+c OR u"
|
||||
7.6 "d+c OR u+d"
|
||||
|
||||
8.1 "NEAR(a b)"
|
||||
8.2 "NEAR(r c)"
|
||||
8.2 { NEAR(r c, 5) }
|
||||
8.3 { NEAR(r c, 3) }
|
||||
8.4 { NEAR(r c, 2) }
|
||||
8.5 { NEAR(r c, 0) }
|
||||
8.6 { NEAR(a b c) }
|
||||
8.7 { NEAR(a b c, 8) }
|
||||
8.8 { x : NEAR(r c) }
|
||||
8.9 { y : NEAR(r c) }
|
||||
|
||||
9.1 { NEAR(r c) }
|
||||
9.2 { NEAR(r c, 5) }
|
||||
9.3 { NEAR(r c, 3) }
|
||||
9.4 { NEAR(r c, 2) }
|
||||
9.5 { NEAR(r c, 0) }
|
||||
9.6 { NEAR(a b c) }
|
||||
9.7 { NEAR(a b c, 8) }
|
||||
9.8 { x : NEAR(r c) }
|
||||
9.9 { y : NEAR(r c) }
|
||||
9.10 { x : "r c" }
|
||||
9.11 { y : "r c" }
|
||||
9.12 { a AND b }
|
||||
9.13 { a AND b AND c }
|
||||
9.14a { a }
|
||||
9.14b { a OR b }
|
||||
9.15 { a OR b AND c }
|
||||
9.16 { c AND b OR a }
|
||||
9.17 { c AND (b OR a) }
|
||||
9.18 { c NOT (b OR a) }
|
||||
9.19 { (c NOT b) OR (a AND d) }
|
||||
} {
|
||||
foreach {tn expr} {
|
||||
0.1 x
|
||||
1 { NEAR(r c) }
|
||||
2 { NEAR(r c, 5) }
|
||||
3 { NEAR(r c, 3) }
|
||||
4 { NEAR(r c, 2) }
|
||||
5 { NEAR(r c, 0) }
|
||||
6 { NEAR(a b c) }
|
||||
7 { NEAR(a b c, 8) }
|
||||
8 { x : NEAR(r c) }
|
||||
9 { y : NEAR(r c) }
|
||||
10 { x : "r c" }
|
||||
11 { y : "r c" }
|
||||
12 { a AND b }
|
||||
13 { a AND b AND c }
|
||||
14a { a }
|
||||
14b { a OR b }
|
||||
15 { a OR b AND c }
|
||||
16 { c AND b OR a }
|
||||
17 { c AND (b OR a) }
|
||||
18 { c NOT (b OR a) }
|
||||
19 { c NOT b OR a AND d }
|
||||
} {
|
||||
set res [matchdata 0 $expr $bAsc]
|
||||
do_execsql_test $tn2.6.$bAsc.$tn.[llength $res] $sql $res
|
||||
|
||||
if {[fts5_expr_ok $expr xx]==0} {
|
||||
do_test 1.$tn2.$tn.OMITTED { list } [list]
|
||||
continue
|
||||
}
|
||||
|
||||
set res [fts5_query_data $expr xx]
|
||||
do_execsql_test 1.$tn2.$tn.[llength $res].asc {
|
||||
SELECT rowid, fts5_test_poslist(xx), fts5_test_collist(xx)
|
||||
FROM xx WHERE xx match $expr
|
||||
} $res
|
||||
|
||||
|
||||
set res [fts5_query_data $expr xx DESC]
|
||||
do_execsql_test 1.$tn2.$tn.[llength $res].desc {
|
||||
SELECT rowid, fts5_test_poslist(xx), fts5_test_collist(xx)
|
||||
FROM xx WHERE xx match $expr ORDER BY 1 DESC
|
||||
} $res
|
||||
}
|
||||
}
|
||||
|
||||
do_execsql_test 3.1 {
|
||||
}
|
||||
|
||||
do_execsql_test 2.1 {
|
||||
SELECT fts5_expr_tcl('a AND b');
|
||||
} {{AND [nearset -- {a}] [nearset -- {b}]}}
|
||||
|
||||
do_test 2.2.1 { nearset {{a b c}} -- a } {0.0.0}
|
||||
do_test 2.2.2 { nearset {{a b c}} -- c } {0.0.2}
|
||||
|
||||
foreach {tn expr tclexpr} {
|
||||
1 {a b} {AND [N $x -- {a}] [N $x -- {b}]}
|
||||
} {
|
||||
do_execsql_test 2.3.$tn {
|
||||
SELECT fts5_expr_tcl($expr, 'N $x')
|
||||
} [list $tclexpr]
|
||||
}
|
||||
|
||||
finish_test
|
||||
|
||||
|
@ -22,8 +22,10 @@ ifcapable !fts5 {
|
||||
return
|
||||
}
|
||||
|
||||
foreach_detail_mode $testprefix {
|
||||
|
||||
do_execsql_test 1.0 {
|
||||
CREATE VIRTUAL TABLE yy USING fts5(x, y);
|
||||
CREATE VIRTUAL TABLE yy USING fts5(x, y, detail=%DETAIL%);
|
||||
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');
|
||||
@ -53,23 +55,23 @@ foreach {tn match res} {
|
||||
|
||||
foreach {T create} {
|
||||
2 {
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(a, b);
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(a, b, detail=%DETAIL%);
|
||||
INSERT INTO t1(t1, rank) VALUES('pgsz', 32);
|
||||
}
|
||||
|
||||
3 {
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(a, b, prefix=1,2,3,4,5);
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(a, b, prefix="1,2,3,4", detail=%DETAIL%);
|
||||
INSERT INTO t1(t1, rank) VALUES('pgsz', 32);
|
||||
}
|
||||
|
||||
4 {
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(a, b);
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(a, b, detail=%DETAIL%);
|
||||
INSERT INTO t1(t1, rank) VALUES('pgsz', 32);
|
||||
BEGIN;
|
||||
}
|
||||
|
||||
5 {
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(a, b, prefix=1,2,3,4,5);
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(a, b, prefix="1,2,3,4", detail=%DETAIL%);
|
||||
INSERT INTO t1(t1, rank) VALUES('pgsz', 32);
|
||||
BEGIN;
|
||||
}
|
||||
@ -235,5 +237,7 @@ foreach {T create} {
|
||||
catchsql COMMIT
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
finish_test
|
||||
|
||||
|
@ -22,8 +22,10 @@ ifcapable !fts5 {
|
||||
return
|
||||
}
|
||||
|
||||
foreach_detail_mode $testprefix {
|
||||
|
||||
do_execsql_test 1.0 {
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(a, b);
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(a, b, detail=%DETAIL%);
|
||||
INSERT INTO t1(t1, rank) VALUES('pgsz', 32);
|
||||
}
|
||||
|
||||
@ -55,7 +57,7 @@ fts5_aux_test_functions db
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
do_execsql_test 2.0 {
|
||||
CREATE VIRTUAL TABLE t2 USING fts5(x, y);
|
||||
CREATE VIRTUAL TABLE t2 USING fts5(x, y, detail=%DETAIL%);
|
||||
INSERT INTO t2 VALUES('u t l w w m s', 'm f m o l t k o p e');
|
||||
INSERT INTO t2 VALUES('f g q e l n d m z x q', 'z s i i i m f w w f n g p');
|
||||
}
|
||||
@ -76,31 +78,35 @@ do_execsql_test 2.2 {
|
||||
2 {1.0.2 1.0.10}
|
||||
}
|
||||
|
||||
do_execsql_test 2.3 {
|
||||
SELECT rowid, fts5_test_poslist(t2) FROM t2
|
||||
WHERE t2 MATCH 'y:o' ORDER BY rowid;
|
||||
} {
|
||||
1 {0.1.3 0.1.7}
|
||||
if {[detail_is_full]} {
|
||||
do_execsql_test 2.3 {
|
||||
SELECT rowid, fts5_test_poslist(t2) FROM t2
|
||||
WHERE t2 MATCH 'y:o' ORDER BY rowid;
|
||||
} {
|
||||
1 {0.1.3 0.1.7}
|
||||
}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
do_execsql_test 3.0 {
|
||||
CREATE VIRTUAL TABLE t3 USING fts5(x, y);
|
||||
CREATE VIRTUAL TABLE t3 USING fts5(x, y, detail=%DETAIL%);
|
||||
INSERT INTO t3 VALUES( 'j f h o x x a z g b a f a m i b', 'j z c z y x w t');
|
||||
INSERT INTO t3 VALUES( 'r c', '');
|
||||
}
|
||||
|
||||
do_execsql_test 3.1 {
|
||||
SELECT rowid, fts5_test_poslist(t3) FROM t3 WHERE t3 MATCH 'NEAR(a b)';
|
||||
} {
|
||||
1 {0.0.6 1.0.9 0.0.10 0.0.12 1.0.15}
|
||||
}
|
||||
if {[detail_is_full]} {
|
||||
do_execsql_test 3.1 {
|
||||
SELECT rowid, fts5_test_poslist(t3) FROM t3 WHERE t3 MATCH 'NEAR(a b)';
|
||||
} {
|
||||
1 {0.0.6 1.0.9 0.0.10 0.0.12 1.0.15}
|
||||
}
|
||||
|
||||
do_execsql_test 3.2 {
|
||||
SELECT rowid, fts5_test_poslist(t3) FROM t3 WHERE t3 MATCH 'NEAR(r c)';
|
||||
} {
|
||||
2 {0.0.0 1.0.1}
|
||||
do_execsql_test 3.2 {
|
||||
SELECT rowid, fts5_test_poslist(t3) FROM t3 WHERE t3 MATCH 'NEAR(r c)';
|
||||
} {
|
||||
2 {0.0.0 1.0.1}
|
||||
}
|
||||
}
|
||||
|
||||
do_execsql_test 3.3 {
|
||||
@ -116,7 +122,7 @@ do_execsql_test 3.3 {
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
do_execsql_test 4.0 {
|
||||
CREATE VIRTUAL TABLE t4 USING fts5(x, y);
|
||||
CREATE VIRTUAL TABLE t4 USING fts5(x, y, detail=%DETAIL%);
|
||||
INSERT INTO t4
|
||||
VALUES('k x j r m a d o i z j', 'r t t t f e b r x i v j v g o');
|
||||
}
|
||||
@ -134,7 +140,7 @@ reset_db
|
||||
fts5_aux_test_functions db
|
||||
|
||||
do_execsql_test 5.1 {
|
||||
CREATE VIRTUAL TABLE t5 USING fts5(x, y);
|
||||
CREATE VIRTUAL TABLE t5 USING fts5(x, y, detail=%DETAIL%);
|
||||
INSERT INTO t5 VALUES('a b c d', 'e f g h i j');
|
||||
INSERT INTO t5 VALUES('', 'a');
|
||||
INSERT INTO t5 VALUES('a', '');
|
||||
@ -182,7 +188,7 @@ do_execsql_test 5.5 {
|
||||
reset_db
|
||||
fts5_aux_test_functions db
|
||||
do_execsql_test 6.1 {
|
||||
CREATE VIRTUAL TABLE t6 USING fts5(x, y);
|
||||
CREATE VIRTUAL TABLE t6 USING fts5(x, y, detail=%DETAIL%);
|
||||
INSERT INTO t6 VALUES('There are more', 'things in heaven and earth');
|
||||
INSERT INTO t6 VALUES(', Horatio, Than are', 'dreamt of in your philosophy.');
|
||||
}
|
||||
@ -200,7 +206,7 @@ do_execsql_test 6.2 {
|
||||
reset_db
|
||||
fts5_aux_test_functions db
|
||||
do_execsql_test 7.1 {
|
||||
CREATE VIRTUAL TABLE t7 USING fts5(x, y);
|
||||
CREATE VIRTUAL TABLE t7 USING fts5(x, y, detail=%DETAIL%);
|
||||
}
|
||||
do_test 7.2 {
|
||||
foreach {x y} {
|
||||
@ -240,7 +246,7 @@ do_execsql_test 7.4 {
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
do_test 8.1 {
|
||||
execsql { CREATE VIRTUAL TABLE t8 USING fts5(x, y) }
|
||||
execsql { CREATE VIRTUAL TABLE t8 USING fts5(x, y, detail=%DETAIL%) }
|
||||
foreach {rowid x y} {
|
||||
0 {A o} {o o o C o o o o o o o o}
|
||||
1 {o o B} {o o o C C o o o o o o o}
|
||||
@ -300,5 +306,7 @@ foreach {tn q cnt} {
|
||||
} $cnt
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
finish_test
|
||||
|
||||
|
@ -24,9 +24,10 @@ ifcapable !fts5 {
|
||||
return
|
||||
}
|
||||
|
||||
foreach_detail_mode $testprefix {
|
||||
|
||||
do_execsql_test 1.0 {
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(x, y);
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(x, y, detail=%DETAIL%);
|
||||
}
|
||||
|
||||
proc do_snippet_test {tn doc match res} {
|
||||
@ -111,34 +112,37 @@ foreach {tn doc res} {
|
||||
do_snippet_test 1.$tn $doc X $res
|
||||
}
|
||||
|
||||
foreach {tn doc res} {
|
||||
1.1 {X Y o o o o o} {[X Y] o o o o o}
|
||||
1.2 {o X Y o o o o} {o [X Y] o o o o}
|
||||
1.3 {o o X Y o o o} {o o [X Y] o o o}
|
||||
1.4 {o o o X Y o o} {o o o [X Y] o o}
|
||||
1.5 {o o o o X Y o} {o o o o [X Y] o}
|
||||
1.6 {o o o o o X Y} {o o o o o [X Y]}
|
||||
if {[detail_is_full]} {
|
||||
foreach {tn doc res} {
|
||||
1.1 {X Y o o o o o} {[X Y] o o o o o}
|
||||
1.2 {o X Y o o o o} {o [X Y] o o o o}
|
||||
1.3 {o o X Y o o o} {o o [X Y] o o o}
|
||||
1.4 {o o o X Y o o} {o o o [X Y] o o}
|
||||
1.5 {o o o o X Y o} {o o o o [X Y] o}
|
||||
1.6 {o o o o o X Y} {o o o o o [X Y]}
|
||||
|
||||
2.1 {X Y o o o o o o} {[X Y] o o o o o...}
|
||||
2.2 {o X Y o o o o o} {o [X Y] o o o o...}
|
||||
2.3 {o o X Y o o o o} {o o [X Y] o o o...}
|
||||
2.4 {o o o X Y o o o} {...o o [X Y] o o o}
|
||||
2.5 {o o o o X Y o o} {...o o o [X Y] o o}
|
||||
2.6 {o o o o o X Y o} {...o o o o [X Y] o}
|
||||
2.7 {o o o o o o X Y} {...o o o o o [X Y]}
|
||||
2.1 {X Y o o o o o o} {[X Y] o o o o o...}
|
||||
2.2 {o X Y o o o o o} {o [X Y] o o o o...}
|
||||
2.3 {o o X Y o o o o} {o o [X Y] o o o...}
|
||||
2.4 {o o o X Y o o o} {...o o [X Y] o o o}
|
||||
2.5 {o o o o X Y o o} {...o o o [X Y] o o}
|
||||
2.6 {o o o o o X Y o} {...o o o o [X Y] o}
|
||||
2.7 {o o o o o o X Y} {...o o o o o [X Y]}
|
||||
|
||||
3.1 {X Y o o o o o o o} {[X Y] o o o o o...}
|
||||
3.2 {o X Y o o o o o o} {o [X Y] o o o o...}
|
||||
3.3 {o o X Y o o o o o} {o o [X Y] o o o...}
|
||||
3.4 {o o o X Y o o o o} {...o o [X Y] o o o...}
|
||||
3.5 {o o o o X Y o o o} {...o o [X Y] o o o}
|
||||
3.6 {o o o o o X Y o o} {...o o o [X Y] o o}
|
||||
3.7 {o o o o o o X Y o} {...o o o o [X Y] o}
|
||||
3.8 {o o o o o o o X Y} {...o o o o o [X Y]}
|
||||
|
||||
} {
|
||||
do_snippet_test 2.$tn $doc "X + Y" $res
|
||||
3.1 {X Y o o o o o o o} {[X Y] o o o o o...}
|
||||
3.2 {o X Y o o o o o o} {o [X Y] o o o o...}
|
||||
3.3 {o o X Y o o o o o} {o o [X Y] o o o...}
|
||||
3.4 {o o o X Y o o o o} {...o o [X Y] o o o...}
|
||||
3.5 {o o o o X Y o o o} {...o o [X Y] o o o}
|
||||
3.6 {o o o o o X Y o o} {...o o o [X Y] o o}
|
||||
3.7 {o o o o o o X Y o} {...o o o o [X Y] o}
|
||||
3.8 {o o o o o o o X Y} {...o o o o o [X Y]}
|
||||
} {
|
||||
do_snippet_test 2.$tn $doc "X + Y" $res
|
||||
}
|
||||
}
|
||||
|
||||
} ;# foreach_detail_mode
|
||||
|
||||
finish_test
|
||||
|
||||
|
@ -33,8 +33,10 @@ ifcapable !fts5 {
|
||||
# ... WHERE fts MATCH ? ORDER BY rank [ASC|DESC]
|
||||
#
|
||||
|
||||
foreach_detail_mode $testprefix {
|
||||
|
||||
do_execsql_test 1.0 {
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(x, y, z);
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(x, y, z, detail=%DETAIL%);
|
||||
}
|
||||
|
||||
do_test 1.1 {
|
||||
@ -119,19 +121,24 @@ foreach {tn expr} {
|
||||
2.3 c
|
||||
2.4 d
|
||||
|
||||
2.5 {"m m"}
|
||||
2.6 {e + s}
|
||||
|
||||
3.0 {a AND b}
|
||||
3.1 {a OR b}
|
||||
3.2 {b OR c AND d}
|
||||
3.3 {NEAR(c d)}
|
||||
} {
|
||||
do_fts5ag_test $tn $expr
|
||||
|
||||
if {[set_test_counter errors]} break
|
||||
}
|
||||
|
||||
if {[detail_is_full]} {
|
||||
foreach {tn expr} {
|
||||
4.1 {"m m"}
|
||||
4.2 {e + s}
|
||||
4.3 {NEAR(c d)}
|
||||
} {
|
||||
do_fts5ag_test $tn $expr
|
||||
}
|
||||
}
|
||||
|
||||
} ;# foreach_detail_mode
|
||||
|
||||
|
||||
finish_test
|
||||
|
@ -21,12 +21,16 @@ ifcapable !fts5 {
|
||||
return
|
||||
}
|
||||
|
||||
foreach_detail_mode $testprefix {
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# This file contains tests for very large doclists.
|
||||
#
|
||||
|
||||
set Y [list]
|
||||
set W [list]
|
||||
do_test 1.0 {
|
||||
execsql { CREATE VIRTUAL TABLE t1 USING fts5(a) }
|
||||
execsql { CREATE VIRTUAL TABLE t1 USING fts5(a, detail=%DETAIL%) }
|
||||
execsql { INSERT INTO t1(t1, rank) VALUES('pgsz', 128) }
|
||||
set v {w w w w w w w w w w w w w w w w w w w w}
|
||||
execsql { INSERT INTO t1(rowid, a) VALUES(0, $v) }
|
||||
@ -70,7 +74,12 @@ do_test 1.4 {
|
||||
set nRead [reads]
|
||||
execsql { SELECT rowid FROM t1 WHERE t1 MATCH 'x' }
|
||||
set nReadX [expr [reads] - $nRead]
|
||||
expr $nReadX>1000
|
||||
#puts -nonewline "(nReadX=$nReadX)"
|
||||
if {[detail_is_full]} { set expect 1000 }
|
||||
if {[detail_is_col]} { set expect 250 }
|
||||
if {[detail_is_none]} { set expect 80 }
|
||||
|
||||
expr $nReadX>$expect
|
||||
} {1}
|
||||
|
||||
do_test 1.5 {
|
||||
@ -87,17 +96,22 @@ foreach {tn q res} "
|
||||
3 { SELECT rowid FROM t1 WHERE t1 MATCH 'x AND w' } [list $W]
|
||||
4 { SELECT rowid FROM t1 WHERE t1 MATCH 'y AND x' } [list $Y]
|
||||
" {
|
||||
if {[detail_is_full]==0 && ($tn==1 || $tn==2)} continue
|
||||
|
||||
if {[detail_is_full]} { set ratio 8 }
|
||||
if {[detail_is_col]} { set ratio 4 }
|
||||
if {[detail_is_none]} { set ratio 2 }
|
||||
|
||||
do_test 1.6.$tn.1 {
|
||||
set n [execsql_reads $q]
|
||||
#puts -nonewline "(n=$n nReadX=$nReadX)"
|
||||
expr {$n < ($nReadX / 8)}
|
||||
expr {$n < ($nReadX / $ratio)}
|
||||
} {1}
|
||||
|
||||
do_test 1.6.$tn.2 {
|
||||
set n [execsql_reads "$q ORDER BY rowid DESC"]
|
||||
#puts -nonewline "(n=$n nReadX=$nReadX)"
|
||||
expr {$n < ($nReadX / 8)}
|
||||
expr {$n < ($nReadX / $ratio)}
|
||||
} {1}
|
||||
|
||||
do_execsql_test 1.6.$tn.3 $q [lsort -int -incr $res]
|
||||
@ -109,21 +123,26 @@ foreach {tn q res} "
|
||||
# number of pages loaded from disk.
|
||||
#
|
||||
foreach {tn fraction tail cnt} {
|
||||
1 0.6 {rowid > 5000} 5000
|
||||
2 0.2 {rowid > 9000} 1000
|
||||
3 0.2 {rowid < 1000} 999
|
||||
4 0.2 {rowid BETWEEN 4000 AND 5000} 1001
|
||||
5 0.6 {rowid >= 5000} 5001
|
||||
6 0.2 {rowid >= 9000} 1001
|
||||
7 0.2 {rowid <= 1000} 1000
|
||||
8 0.6 {rowid > '5000'} 5000
|
||||
9 0.2 {rowid > '9000'} 1000
|
||||
1 0.6 {rowid > 5000} 5000
|
||||
2 0.2 {rowid > 9000} 1000
|
||||
3 0.2 {rowid < 1000} 999
|
||||
4 0.2 {rowid BETWEEN 4000 AND 5000} 1001
|
||||
5 0.6 {rowid >= 5000} 5001
|
||||
6 0.2 {rowid >= 9000} 1001
|
||||
7 0.2 {rowid <= 1000} 1000
|
||||
8 0.6 {rowid > '5000'} 5000
|
||||
9 0.2 {rowid > '9000'} 1000
|
||||
10 0.1 {rowid = 444} 1
|
||||
} {
|
||||
set q "SELECT rowid FROM t1 WHERE t1 MATCH 'x' AND $tail"
|
||||
set n [execsql_reads $q]
|
||||
set ret [llength [execsql $q]]
|
||||
|
||||
# Because the position lists for 'x' are quite long in this db, the
|
||||
# advantage is a bit smaller in detail=none mode. Update $fraction to
|
||||
# reflect this.
|
||||
if {[detail_is_none] && $fraction<0.5} { set fraction [expr $fraction*2] }
|
||||
|
||||
do_test "1.7.$tn.asc.(n=$n ret=$ret)" {
|
||||
expr {$n < ($fraction*$nReadX) && $ret==$cnt}
|
||||
} {1}
|
||||
@ -143,6 +162,7 @@ do_execsql_test 1.8.2 {
|
||||
SELECT count(*) FROM t1 WHERE t1 MATCH 'x' AND rowid < 'text';
|
||||
} {10000}
|
||||
|
||||
} ;# foreach_detail_mode
|
||||
|
||||
#db eval {SELECT rowid, fts5_decode(rowid, block) aS r FROM t1_data} {puts $r}
|
||||
|
||||
|
@ -23,8 +23,10 @@ ifcapable !fts5 {
|
||||
return
|
||||
}
|
||||
|
||||
foreach_detail_mode $testprefix {
|
||||
|
||||
do_execsql_test 1.0 {
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(a);
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(a, detail=%DETAIL%);
|
||||
} {}
|
||||
|
||||
do_execsql_test 1.1 {
|
||||
@ -49,6 +51,7 @@ do_execsql_test 1.1 {
|
||||
do_execsql_test 1.2 {
|
||||
INSERT INTO t1(t1) VALUES('integrity-check');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
finish_test
|
||||
|
@ -23,8 +23,10 @@ ifcapable !fts5 {
|
||||
return
|
||||
}
|
||||
|
||||
foreach_detail_mode $testprefix {
|
||||
|
||||
do_execsql_test 1.1 {
|
||||
CREATE VIRTUAL TABLE ft1 USING fts5(x);
|
||||
CREATE VIRTUAL TABLE ft1 USING fts5(x, detail=%DETAIL%);
|
||||
INSERT INTO ft1 VALUES('i d d a g i b g d d');
|
||||
INSERT INTO ft1 VALUES('h d b j c c g a c a');
|
||||
INSERT INTO ft1 VALUES('e j a e f h b f h h');
|
||||
@ -35,6 +37,9 @@ do_execsql_test 1.1 {
|
||||
INSERT INTO ft1 VALUES('i c c f a d g h j e');
|
||||
INSERT INTO ft1 VALUES('i d i g c d c h b f');
|
||||
INSERT INTO ft1 VALUES('g d a e h a b c f j');
|
||||
|
||||
CREATE VIRTUAL TABLE ft2 USING fts5(x, detail=%DETAIL%);
|
||||
INSERT INTO ft2 VALUES('a b c d e f g h i j');
|
||||
}
|
||||
|
||||
do_execsql_test 1.2 {
|
||||
@ -49,19 +54,6 @@ do_execsql_test 1.2 {
|
||||
}
|
||||
|
||||
do_execsql_test 1.3 {
|
||||
SELECT highlight(ft1, 0, '[', ']') FROM ft1 WHERE ft1 MATCH 'h + d';
|
||||
} {
|
||||
{[h d] b j c c g a c a}
|
||||
{j f [h d] g h i b d f}
|
||||
}
|
||||
|
||||
do_execsql_test 1.4 {
|
||||
SELECT highlight(ft1, 0, '[', ']') FROM ft1 WHERE ft1 MATCH 'd + d';
|
||||
} {
|
||||
{i [d d] a g i b g [d d]}
|
||||
}
|
||||
|
||||
do_execsql_test 1.5 {
|
||||
SELECT highlight(ft1, 0, '[', ']') FROM ft1 WHERE ft1 MATCH 'e e e'
|
||||
} {
|
||||
{[e] j a [e] f h b f h h}
|
||||
@ -72,57 +64,71 @@ do_execsql_test 1.5 {
|
||||
{g d a [e] h a b c f j}
|
||||
}
|
||||
|
||||
do_execsql_test 1.6 {
|
||||
SELECT highlight(ft1, 0, '[', ']') FROM ft1 WHERE ft1 MATCH 'd + d d + d';
|
||||
} {
|
||||
{i [d d] a g i b g [d d]}
|
||||
}
|
||||
|
||||
do_execsql_test 2.1 {
|
||||
CREATE VIRTUAL TABLE ft2 USING fts5(x);
|
||||
INSERT INTO ft2 VALUES('a b c d e f g h i j');
|
||||
}
|
||||
|
||||
do_execsql_test 2.2 {
|
||||
SELECT highlight(ft2, 0, '[', ']') FROM ft2 WHERE ft2 MATCH 'b+c+d c+d+e'
|
||||
} {{a [b c d e] f g h i j}}
|
||||
|
||||
do_execsql_test 2.3 {
|
||||
SELECT highlight(ft2, 0, '[', ']') FROM ft2 WHERE ft2 MATCH 'b+c+d e+f+g'
|
||||
} {
|
||||
{a [b c d] [e f g] h i j}
|
||||
}
|
||||
|
||||
do_execsql_test 2.4 {
|
||||
SELECT highlight(ft2, 0, '[', ']') FROM ft2 WHERE ft2 MATCH 'b+c+d c'
|
||||
} {
|
||||
{a [b c d] e f g h i j}
|
||||
}
|
||||
|
||||
do_execsql_test 2.5 {
|
||||
SELECT highlight(ft2, 0, '[', ']') FROM ft2 WHERE ft2 MATCH 'b+c c+d+e'
|
||||
} {
|
||||
{a [b c d e] f g h i j}
|
||||
}
|
||||
|
||||
do_execsql_test 2.6.1 {
|
||||
do_execsql_test 1.4 {
|
||||
SELECT highlight(ft2, 0, '[', ']') FROM ft2 WHERE ft2 MATCH 'f d'
|
||||
} {
|
||||
{a b c [d] e [f] g h i j}
|
||||
}
|
||||
|
||||
do_execsql_test 2.6.2 {
|
||||
do_execsql_test 1.5 {
|
||||
SELECT highlight(ft2, 0, '[', ']') FROM ft2 WHERE ft2 MATCH 'd f'
|
||||
} {
|
||||
{a b c [d] e [f] g h i j}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Tests below this point require detail=full.
|
||||
#-------------------------------------------------------------------------
|
||||
if {[detail_is_full]==0} continue
|
||||
|
||||
|
||||
do_execsql_test 2.1 {
|
||||
SELECT highlight(ft1, 0, '[', ']') FROM ft1 WHERE ft1 MATCH 'h + d';
|
||||
} {
|
||||
{[h d] b j c c g a c a}
|
||||
{j f [h d] g h i b d f}
|
||||
}
|
||||
|
||||
do_execsql_test 2.2 {
|
||||
SELECT highlight(ft1, 0, '[', ']') FROM ft1 WHERE ft1 MATCH 'd + d';
|
||||
} {
|
||||
{i [d d] a g i b g [d d]}
|
||||
}
|
||||
|
||||
do_execsql_test 2.3 {
|
||||
SELECT highlight(ft1, 0, '[', ']') FROM ft1 WHERE ft1 MATCH 'd + d d + d';
|
||||
} {
|
||||
{i [d d] a g i b g [d d]}
|
||||
}
|
||||
|
||||
do_execsql_test 2.4 {
|
||||
SELECT highlight(ft2, 0, '[', ']') FROM ft2 WHERE ft2 MATCH 'b+c+d c+d+e'
|
||||
} {{a [b c d e] f g h i j}}
|
||||
|
||||
do_execsql_test 2.5 {
|
||||
SELECT highlight(ft2, 0, '[', ']') FROM ft2 WHERE ft2 MATCH 'b+c+d e+f+g'
|
||||
} {
|
||||
{a [b c d] [e f g] h i j}
|
||||
}
|
||||
|
||||
do_execsql_test 2.6 {
|
||||
SELECT highlight(ft2, 0, '[', ']') FROM ft2 WHERE ft2 MATCH 'b+c+d c'
|
||||
} {
|
||||
{a [b c d] e f g h i j}
|
||||
}
|
||||
|
||||
do_execsql_test 2.7 {
|
||||
SELECT highlight(ft2, 0, '[', ']') FROM ft2 WHERE ft2 MATCH 'b+c c+d+e'
|
||||
} {
|
||||
{a [b c d e] f g h i j}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# The example from the docs.
|
||||
#
|
||||
do_execsql_test 3.1 {
|
||||
-- Assuming this:
|
||||
CREATE VIRTUAL TABLE ft USING fts5(a);
|
||||
CREATE VIRTUAL TABLE ft USING fts5(a, detail=%DETAIL%);
|
||||
INSERT INTO ft VALUES('a b c x c d e');
|
||||
INSERT INTO ft VALUES('a b c c d e');
|
||||
INSERT INTO ft VALUES('a b c d e');
|
||||
@ -138,6 +144,7 @@ do_execsql_test 3.1 {
|
||||
{[a b c d e]}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
finish_test
|
||||
|
||||
|
@ -23,8 +23,10 @@ ifcapable !fts5 {
|
||||
return
|
||||
}
|
||||
|
||||
foreach_detail_mode $testprefix {
|
||||
|
||||
do_execsql_test 1.1 {
|
||||
CREATE VIRTUAL TABLE ft1 USING fts5(x);
|
||||
CREATE VIRTUAL TABLE ft1 USING fts5(x, detail=%DETAIL%);
|
||||
SELECT * FROM ft1_config;
|
||||
} {version 4}
|
||||
|
||||
@ -83,7 +85,7 @@ foreach {tn defn} {
|
||||
#
|
||||
|
||||
do_execsql_test 3.1 {
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(x);
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(x, detail=%DETAIL%);
|
||||
INSERT INTO t1 VALUES('q w e r t y');
|
||||
INSERT INTO t1 VALUES('y t r e w q');
|
||||
}
|
||||
@ -122,11 +124,13 @@ do_execsql_test 3.4.1 {
|
||||
{{0 0 5}}
|
||||
}
|
||||
|
||||
do_execsql_test 3.4.2 {
|
||||
SELECT insttest(t1) FROM t1 WHERE t1 MATCH 'r+e OR w'
|
||||
} {
|
||||
{{1 0 1}}
|
||||
{{0 0 2} {1 0 4}}
|
||||
if {[detail_is_full]} {
|
||||
do_execsql_test 3.4.2 {
|
||||
SELECT insttest(t1) FROM t1 WHERE t1 MATCH 'r+e OR w'
|
||||
} {
|
||||
{{1 0 1}}
|
||||
{{0 0 2} {1 0 4}}
|
||||
}
|
||||
}
|
||||
|
||||
proc coltest {cmd} {
|
||||
@ -149,7 +153,7 @@ do_execsql_test 3.5.1 {
|
||||
#
|
||||
|
||||
do_execsql_test 4.0 {
|
||||
CREATE VIRTUAL TABLE t2 USING fts5(a, b);
|
||||
CREATE VIRTUAL TABLE t2 USING fts5(a, b, detail=%DETAIL%);
|
||||
INSERT INTO t2 VALUES('a s h g s b j m r h', 's b p a d b b a o e');
|
||||
INSERT INTO t2 VALUES('r h n t a g r d d i', 'l d n j r c f t o q');
|
||||
INSERT INTO t2 VALUES('q k n i k c a a e m', 'c h n j p g s c i t');
|
||||
@ -218,24 +222,26 @@ proc rowidplus {cmd ival} {
|
||||
}
|
||||
sqlite3_fts5_create_function db rowidplus rowidplus
|
||||
|
||||
do_execsql_test 4.2.1 {
|
||||
INSERT INTO t2(t2, rank) VALUES('rank', 'rowidplus(100) ');
|
||||
SELECT rowid, rank FROM t2 WHERE t2 MATCH 'o + q + g'
|
||||
} {
|
||||
10 110
|
||||
}
|
||||
do_execsql_test 4.2.2 {
|
||||
INSERT INTO t2(t2, rank) VALUES('rank', 'rowidplus(111) ');
|
||||
SELECT rowid, rank FROM t2 WHERE t2 MATCH 'o + q + g'
|
||||
} {
|
||||
10 121
|
||||
}
|
||||
if {[detail_is_full]} {
|
||||
do_execsql_test 4.2.1 {
|
||||
INSERT INTO t2(t2, rank) VALUES('rank', 'rowidplus(100) ');
|
||||
SELECT rowid, rank FROM t2 WHERE t2 MATCH 'o + q + g'
|
||||
} {
|
||||
10 110
|
||||
}
|
||||
do_execsql_test 4.2.2 {
|
||||
INSERT INTO t2(t2, rank) VALUES('rank', 'rowidplus(111) ');
|
||||
SELECT rowid, rank FROM t2 WHERE t2 MATCH 'o + q + g'
|
||||
} {
|
||||
10 121
|
||||
}
|
||||
|
||||
do_execsql_test 4.2.3 {
|
||||
SELECT rowid, rank FROM t2
|
||||
WHERE t2 MATCH 'o + q + g' AND rank MATCH 'rowidplus(112)'
|
||||
} {
|
||||
10 122
|
||||
do_execsql_test 4.2.3 {
|
||||
SELECT rowid, rank FROM t2
|
||||
WHERE t2 MATCH 'o + q + g' AND rank MATCH 'rowidplus(112)'
|
||||
} {
|
||||
10 122
|
||||
}
|
||||
}
|
||||
|
||||
proc rowidmod {cmd imod} {
|
||||
@ -243,7 +249,7 @@ proc rowidmod {cmd imod} {
|
||||
}
|
||||
sqlite3_fts5_create_function db rowidmod rowidmod
|
||||
do_execsql_test 4.3.1 {
|
||||
CREATE VIRTUAL TABLE t3 USING fts5(x);
|
||||
CREATE VIRTUAL TABLE t3 USING fts5(x, detail=%DETAIL%);
|
||||
INSERT INTO t3 VALUES('a one');
|
||||
INSERT INTO t3 VALUES('a two');
|
||||
INSERT INTO t3 VALUES('a three');
|
||||
@ -287,6 +293,7 @@ do_catchsql_test 4.4.4 {
|
||||
SELECT *, rank FROM t3 WHERE t3 MATCH 'a' AND rank MATCH NULL
|
||||
} {1 {parse error in rank function: }}
|
||||
|
||||
} ;# foreach_detail_mode
|
||||
|
||||
|
||||
finish_test
|
||||
|
@ -22,7 +22,6 @@ ifcapable !fts5 {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
set data {
|
||||
-4026076
|
||||
{n x w k b p x b n t t d s} {f j j s p j o}
|
||||
@ -232,37 +231,9 @@ do_execsql_test 1.0 {
|
||||
|
||||
fts5_aux_test_functions db
|
||||
|
||||
proc matchdata {expr tbl collist {order ASC}} {
|
||||
|
||||
set cols ""
|
||||
foreach e $collist {
|
||||
append cols ", '$e'"
|
||||
}
|
||||
|
||||
set tclexpr [db one [subst -novar {
|
||||
SELECT fts5_expr_tcl(
|
||||
$expr, 'nearset $cols -pc ::pc' [set cols]
|
||||
)
|
||||
}]]
|
||||
set res [list]
|
||||
|
||||
db eval "SELECT rowid, * FROM $tbl ORDER BY rowid $order" x {
|
||||
set cols [list]
|
||||
foreach col $x(*) {
|
||||
if {$col != "rowid"} { lappend cols $x($col) }
|
||||
}
|
||||
# set cols [list $a $b $c $d $e $f]
|
||||
set ::pc 0
|
||||
set rowdata [eval $tclexpr]
|
||||
if {$rowdata != ""} { lappend res $x(rowid) $rowdata }
|
||||
}
|
||||
|
||||
set res
|
||||
}
|
||||
|
||||
proc do_auto_test {tn tbl cols expr} {
|
||||
proc do_auto_test {tn tbl expr} {
|
||||
foreach order {asc desc} {
|
||||
set res [matchdata $expr $tbl $cols $order]
|
||||
set res [fts5_poslist_data $expr $tbl $order]
|
||||
set testname "$tn.[string range $order 0 0].rows=[expr [llength $res]/2]"
|
||||
|
||||
set ::autotest_expr $expr
|
||||
@ -271,8 +242,6 @@ proc do_auto_test {tn tbl cols expr} {
|
||||
WHERE [set tbl] MATCH $::autotest_expr ORDER BY rowid [set order]
|
||||
}] $res
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
@ -332,7 +301,7 @@ for {set fold 0} {$fold < 3} {incr fold} {
|
||||
C.1 { a OR (b AND "b c") }
|
||||
C.2 { a OR (b AND "z c") }
|
||||
} {
|
||||
do_auto_test 3.$fold.$tn tt {a b c d e f} $expr
|
||||
do_auto_test 3.$fold.$tn tt $expr
|
||||
}
|
||||
}
|
||||
|
||||
@ -366,11 +335,8 @@ foreach {tn expr} {
|
||||
4 {c1 : x} 5 {c2 : x} 6 {c3 : x}
|
||||
7 {c1 : y} 8 {c2 : y} 9 {c3 : y}
|
||||
10 {c1 : z} 11 {c2 : z} 12 {c3 : z}
|
||||
|
||||
|
||||
} {
|
||||
breakpoint
|
||||
do_auto_test 4.$tn yy {c1 c2 c3} $expr
|
||||
do_auto_test 4.$tn yy $expr
|
||||
}
|
||||
|
||||
|
||||
|
240
ext/fts5/test/fts5detail.test
Normal file
240
ext/fts5/test/fts5detail.test
Normal file
@ -0,0 +1,240 @@
|
||||
# 2015 December 18
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
source [file join [file dirname [info script]] fts5_common.tcl]
|
||||
set testprefix fts5detail
|
||||
|
||||
# If SQLITE_ENABLE_FTS5 is not defined, omit this file.
|
||||
ifcapable !fts5 {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
fts5_aux_test_functions db
|
||||
|
||||
#--------------------------------------------------------------------------
|
||||
# Simple tests.
|
||||
#
|
||||
do_execsql_test 1.0 {
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(a, b, c, detail=col);
|
||||
INSERT INTO t1 VALUES('h d g', 'j b b g b', 'i e i d h g g'); -- 1
|
||||
INSERT INTO t1 VALUES('h j d', 'j h d a h', 'f d d g g f b'); -- 2
|
||||
INSERT INTO t1 VALUES('j c i', 'f f h e f', 'c j i j c h f'); -- 3
|
||||
INSERT INTO t1 VALUES('e g g', 'g e d h i', 'e d b e g d c'); -- 4
|
||||
INSERT INTO t1 VALUES('b c c', 'd i h a f', 'd i j f a b c'); -- 5
|
||||
INSERT INTO t1 VALUES('e d e', 'b c j g d', 'a i f d h b d'); -- 6
|
||||
INSERT INTO t1 VALUES('g h e', 'b c d i d', 'e f c i f i c'); -- 7
|
||||
INSERT INTO t1 VALUES('c f j', 'j j i e a', 'h a c f d h e'); -- 8
|
||||
INSERT INTO t1 VALUES('a h i', 'c i a f a', 'c f d h g d g'); -- 9
|
||||
INSERT INTO t1 VALUES('j g g', 'e f e f f', 'h j b i c g e'); -- 10
|
||||
}
|
||||
|
||||
do_execsql_test 1.1 {
|
||||
INSERT INTO t1(t1) VALUES('integrity-check');
|
||||
}
|
||||
|
||||
foreach {tn match res} {
|
||||
1 "a:a" {9}
|
||||
2 "b:g" {1 4 6}
|
||||
3 "c:h" {1 3 6 8 9 10}
|
||||
} {
|
||||
do_execsql_test 1.2.$tn.1 {
|
||||
SELECT rowid FROM t1($match);
|
||||
} $res
|
||||
|
||||
do_execsql_test 1.2.$tn.2 {
|
||||
SELECT rowid FROM t1($match || '*');
|
||||
} $res
|
||||
}
|
||||
|
||||
do_catchsql_test 1.3.1 {
|
||||
SELECT rowid FROM t1('h + d');
|
||||
} {1 {fts5: phrase queries are not supported (detail!=full)}}
|
||||
|
||||
do_catchsql_test 1.3.2 {
|
||||
SELECT rowid FROM t1('NEAR(h d)');
|
||||
} {1 {fts5: NEAR queries are not supported (detail!=full)}}
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# integrity-check with both detail= and prefix= options.
|
||||
#
|
||||
do_execsql_test 2.0 {
|
||||
CREATE VIRTUAL TABLE t2 USING fts5(a, detail=col, prefix="1");
|
||||
INSERT INTO t2(a) VALUES('aa ab');
|
||||
}
|
||||
|
||||
#db eval {SELECT rowid, fts5_decode(rowid, block) aS r FROM t2_data} {puts $r}
|
||||
|
||||
do_execsql_test 2.1 {
|
||||
INSERT INTO t2(t2) VALUES('integrity-check');
|
||||
}
|
||||
|
||||
do_execsql_test 2.2 {
|
||||
SELECT fts5_test_poslist(t2) FROM t2('aa');
|
||||
} {0.0.0}
|
||||
|
||||
set ::pc 0
|
||||
#puts [nearset {{ax bx cx}} -pc ::pc -near 10 -- b*]
|
||||
#exit
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Check that the xInstCount, xInst, xPhraseFirst and xPhraseNext APIs
|
||||
# work with detail=col tables.
|
||||
#
|
||||
set data {
|
||||
1 {abb aca aca} {aba bab aab aac caa} {abc cbc ccb bcc bab ccb aca}
|
||||
2 {bca aca acb} {ccb bcc bca aab bcc} {bab aaa aac cbb bba aca abc}
|
||||
3 {cca abc cab} {aab aba bcc cac baa} {bab cbb acb aba aab ccc cca}
|
||||
4 {ccb bcb aba} {aba bbb bcc cac bbb} {cbb aaa bca bcc aab cac aca}
|
||||
5 {bca bbc cac} {aba cbb cac cca aca} {cab acb cbc ccb cac bbb bcb}
|
||||
6 {acc bba cba} {bab bbc bbb bcb aca} {bca ccc cbb aca bac ccc ccb}
|
||||
7 {aba bab aaa} {abb bca aac bcb bcc} {bcb bbc aba aaa cba abc acc}
|
||||
8 {cab aba aaa} {ccb aca caa bbc bcc} {aaa abc ccb bbb cac cca abb}
|
||||
9 {bcb bab bac} {bcb cba cac bbb abc} {aba aca cbb acb abb ccc ccb}
|
||||
10 {aba aab ccc} {abc ccc bcc cab bbb} {aab bcc cbb ccc aaa bac baa}
|
||||
11 {bab acb cba} {aac cab cab bca cbc} {aab cbc aac baa ccb acc cac}
|
||||
12 {ccc cbb cbc} {aaa aab bcc aac bbc} {cbc cbc bac bac ccc bbc acc}
|
||||
13 {cab bbc abc} {bbb bab bba aca bab} {baa bbb aab bbb ccb bbb ccc}
|
||||
14 {bbc cab caa} {acb aac abb cba acc} {cba bba bba acb abc abb baa}
|
||||
15 {aba cca bcc} {aaa acb abc aab ccb} {cca bcb acc aaa caa cca cbc}
|
||||
16 {bcb bba aba} {cbc acb cab caa ccb} {aac aaa bbc cab cca cba abc}
|
||||
17 {caa cbb acc} {ccb bcb bca aaa bcc} {bbb aca bcb bca cbc cbc cca}
|
||||
18 {cbb bbc aac} {ccc bbc aaa aab baa} {cab cab cac cca bbc abc bbc}
|
||||
19 {ccc acc aaa} {aab cbb bca cca caa} {bcb aca aca cab acc bac bcc}
|
||||
20 {aab ccc bcb} {bbc cbb bbc aaa bcc} {cbc aab ccc aaa bcb bac cbc}
|
||||
21 {aba cab ccc} {bbc cbc cba acc bbb} {acc aab aac acb aca bca acb}
|
||||
22 {bcb bca baa} {cca bbc aca ccb cbb} {aab abc bbc aaa cab bcc bcc}
|
||||
23 {cac cbb caa} {bbc aba bbb bcc ccb} {bbc bbb cab bbc cac abb acc}
|
||||
24 {ccb acb caa} {cab bba cac bbc aac} {aac bca abc cab bca cab bcb}
|
||||
25 {bbb aca bca} {bcb acc ccc cac aca} {ccc acb acc cac cac bba bbc}
|
||||
26 {bab acc caa} {caa cab cac bac aca} {aba cac caa acc bac ccc aaa}
|
||||
27 {bca bca aaa} {ccb aca bca aaa baa} {bab acc aaa cca cba cca bac}
|
||||
28 {ccb cac cac} {bca abb bba bbc baa} {aca ccb aac cab ccc cab caa}
|
||||
29 {abc bca cab} {cac cbc cbb ccc bcc} {bcc aaa aaa acc aac cac aac}
|
||||
30 {aca acc acb} {aab aac cbb caa acb} {acb bbc bbc acc cbb bbc aac}
|
||||
31 {aba aca baa} {aca bcc cab bab acb} {bcc acb baa bcb bbc acc aba}
|
||||
32 {abb cbc caa} {cba abb bbb cbb aca} {bac aca caa cac caa ccb bbc}
|
||||
33 {bcc bcb bcb} {cca cab cbc abb bab} {caa bbc aac bbb cab cba aaa}
|
||||
34 {caa cab acc} {ccc ccc bcc acb bcc} {bac bba aca bcb bba bcb cac}
|
||||
35 {bac bcb cba} {bcc acb bbc cba bab} {abb cbb abc abc bac acc cbb}
|
||||
36 {cab bab ccb} {bca bba bab cca acc} {acc aab bcc bac acb cbb caa}
|
||||
37 {aca cbc cab} {bba aac aca aac aaa} {baa cbb cba aba cab bca bcb}
|
||||
38 {acb aab baa} {baa bab bca bbc bbb} {abc baa acc aba cab baa cac}
|
||||
39 {bcb aac cba} {bcb baa caa cac bbc} {cbc ccc bab ccb bbb caa aba}
|
||||
40 {cba ccb abc} {cbb caa cba aac bab} {cbb bbb bca bbb bac cac bca}
|
||||
}
|
||||
|
||||
set data {
|
||||
1 {abb aca aca} {aba bab aab aac caa} {abc cbc ccb bcc bab ccb aca}
|
||||
}
|
||||
|
||||
proc matchdata {expr {bAsc 1}} {
|
||||
|
||||
set tclexpr [db one {
|
||||
SELECT fts5_expr_tcl($expr, 'nearset $cols -pc ::pc', 'x', 'y', 'z')
|
||||
}]
|
||||
set res [list]
|
||||
|
||||
#puts "$expr -> $tclexpr"
|
||||
foreach {id x y z} $::data {
|
||||
set cols [list $x $y $z]
|
||||
set ::pc 0
|
||||
#set hits [lsort -command instcompare [eval $tclexpr]]
|
||||
set hits [eval $tclexpr]
|
||||
if {[llength $hits]>0} {
|
||||
lappend res [list $id $hits]
|
||||
}
|
||||
}
|
||||
|
||||
if {$bAsc} {
|
||||
set res [lsort -integer -increasing -index 0 $res]
|
||||
} else {
|
||||
set res [lsort -integer -decreasing -index 0 $res]
|
||||
}
|
||||
|
||||
return [concat {*}$res]
|
||||
}
|
||||
|
||||
foreach {tn tbl} {
|
||||
1 { CREATE VIRTUAL TABLE t3 USING fts5(x, y, z, detail=col) }
|
||||
2 { CREATE VIRTUAL TABLE t3 USING fts5(x, y, z, detail=none) }
|
||||
} {
|
||||
reset_db
|
||||
fts5_aux_test_functions db
|
||||
execsql $tbl
|
||||
foreach {id x y z} $data {
|
||||
execsql { INSERT INTO t3(rowid, x, y, z) VALUES($id, $x, $y, $z) }
|
||||
}
|
||||
foreach {tn2 expr} {
|
||||
1 aaa 2 ccc 3 bab 4 aac
|
||||
5 aa* 6 cc* 7 ba* 8 aa*
|
||||
9 a* 10 b* 11 c*
|
||||
} {
|
||||
|
||||
set res [matchdata $expr]
|
||||
|
||||
do_execsql_test 3.$tn.$tn2.1 {
|
||||
SELECT rowid, fts5_test_poslist(t3) FROM t3($expr)
|
||||
} $res
|
||||
|
||||
do_execsql_test 3.$tn.$tn2.2 {
|
||||
SELECT rowid, fts5_test_poslist2(t3) FROM t3($expr)
|
||||
} $res
|
||||
}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Simple tests for detail=none tables.
|
||||
#
|
||||
do_execsql_test 4.0 {
|
||||
CREATE VIRTUAL TABLE t4 USING fts5(a, b, c, detail=none);
|
||||
INSERT INTO t4 VALUES('a b c', 'b c d', 'e f g');
|
||||
INSERT INTO t4 VALUES('1 2 3', '4 5 6', '7 8 9');
|
||||
}
|
||||
|
||||
do_catchsql_test 4.1 {
|
||||
SELECT * FROM t4('a:a')
|
||||
} {1 {fts5: column queries are not supported (detail=none)}}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Test that for the same content detail=none uses less space than
|
||||
# detail=col, and that detail=col uses less space than detail=full
|
||||
#
|
||||
reset_db
|
||||
do_test 5.1 {
|
||||
foreach {tbl detail} {t1 none t2 col t3 full} {
|
||||
execsql "CREATE VIRTUAL TABLE $tbl USING fts5(x, y, z, detail=$detail)"
|
||||
foreach {rowid x y z} $::data {
|
||||
execsql "INSERT INTO $tbl (rowid, x, y, z) VALUES(\$rowid, \$x, \$y, \$z)"
|
||||
}
|
||||
}
|
||||
} {}
|
||||
|
||||
do_execsql_test 5.2 {
|
||||
SELECT
|
||||
(SELECT sum(length(block)) from t1_data) <
|
||||
(SELECT sum(length(block)) from t2_data)
|
||||
} {1}
|
||||
|
||||
do_execsql_test 5.3 {
|
||||
SELECT
|
||||
(SELECT sum(length(block)) from t2_data) <
|
||||
(SELECT sum(length(block)) from t3_data)
|
||||
} {1}
|
||||
|
||||
|
||||
|
||||
finish_test
|
||||
|
@ -26,7 +26,7 @@ if { $tcl_platform(wordSize)<8 } {
|
||||
return
|
||||
}
|
||||
|
||||
if 1 {
|
||||
foreach_detail_mode $testprefix {
|
||||
|
||||
proc do_fb_test {tn sql res} {
|
||||
set res2 [lsort -integer -decr $res]
|
||||
@ -34,7 +34,7 @@ proc do_fb_test {tn sql res} {
|
||||
uplevel [list do_execsql_test $tn.2 "$sql ORDER BY rowid DESC" $res2]
|
||||
}
|
||||
|
||||
# This test populates the FTS5 table containing $nEntry entries. Rows are
|
||||
# This test populates the FTS5 table with $nEntry entries. Rows are
|
||||
# numbered from 0 to ($nEntry-1). The rowid for row $i is:
|
||||
#
|
||||
# ($iFirst + $i*$nStep)
|
||||
@ -77,10 +77,12 @@ proc do_dlidx_test1 {tn spc1 spc2 nEntry iFirst nStep} {
|
||||
do_fb_test $tn.4.1 { SELECT rowid FROM t1 WHERE t1 MATCH 'a AND y' } $ydoc
|
||||
do_fb_test $tn.4.2 { SELECT rowid FROM t1 WHERE t1 MATCH 'y AND a' } $ydoc
|
||||
|
||||
do_fb_test $tn.5.1 {
|
||||
SELECT rowid FROM t1 WHERE t1 MATCH 'a + b + c + x' } $xdoc
|
||||
do_fb_test $tn.5.2 {
|
||||
SELECT rowid FROM t1 WHERE t1 MATCH 'b + c + x + y' } $ydoc
|
||||
if {[detail_is_full]} {
|
||||
do_fb_test $tn.5.1 {
|
||||
SELECT rowid FROM t1 WHERE t1 MATCH 'a + b + c + x' } $xdoc
|
||||
do_fb_test $tn.5.2 {
|
||||
SELECT rowid FROM t1 WHERE t1 MATCH 'b + c + x + y' } $ydoc
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -90,7 +92,7 @@ foreach {tn pgsz} {
|
||||
} {
|
||||
do_execsql_test $tn.0 {
|
||||
DROP TABLE IF EXISTS t1;
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(x);
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(x, detail=%DETAIL%);
|
||||
INSERT INTO t1(t1, rank) VALUES('pgsz', $pgsz);
|
||||
}
|
||||
|
||||
@ -107,7 +109,7 @@ proc do_dlidx_test2 {tn nEntry iFirst nStep} {
|
||||
execsql {
|
||||
BEGIN;
|
||||
DROP TABLE IF EXISTS t1;
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(x);
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(x, detail=%DETAIL%);
|
||||
INSERT INTO t1(t1, rank) VALUES('pgsz', 64);
|
||||
INSERT INTO t1 VALUES('b a');
|
||||
|
||||
@ -130,8 +132,6 @@ proc do_dlidx_test2 {tn nEntry iFirst nStep} {
|
||||
|
||||
do_dlidx_test2 2.1 [expr 20] [expr 1<<57] [expr (1<<57) + 128]
|
||||
|
||||
}
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
#
|
||||
reset_db
|
||||
@ -158,7 +158,7 @@ proc rnddoc {} {
|
||||
db func rnddoc rnddoc
|
||||
|
||||
do_execsql_test 3.1 {
|
||||
CREATE VIRTUAL TABLE abc USING fts5(a);
|
||||
CREATE VIRTUAL TABLE abc USING fts5(a, detail=%DETAIL%);
|
||||
INSERT INTO abc(abc, rank) VALUES('pgsz', 32);
|
||||
|
||||
INSERT INTO abc VALUES ( rnddoc() );
|
||||
@ -192,6 +192,9 @@ foreach v $vocab {
|
||||
} {1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16}
|
||||
}
|
||||
|
||||
} ;# foreach_detail_mode
|
||||
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
|
@ -42,6 +42,15 @@ foreach {tn expr res} {
|
||||
10 {abc + "" + def} {"abc" + "def"}
|
||||
11 {abc "" def} {"abc" AND "def"}
|
||||
12 {r+e OR w} {"r" + "e" OR "w"}
|
||||
|
||||
13 {a AND b NOT c} {"a" AND ("b" NOT "c")}
|
||||
14 {a OR b NOT c} {"a" OR ("b" NOT "c")}
|
||||
15 {a NOT b AND c} {("a" NOT "b") AND "c"}
|
||||
16 {a NOT b OR c} {("a" NOT "b") OR "c"}
|
||||
|
||||
17 {a AND b OR c} {("a" AND "b") OR "c"}
|
||||
18 {a OR b AND c} {"a" OR ("b" AND "c")}
|
||||
|
||||
} {
|
||||
do_execsql_test 1.$tn {SELECT fts5_expr($expr)} [list $res]
|
||||
}
|
||||
|
49
ext/fts5/test/fts5fault8.test
Normal file
49
ext/fts5/test/fts5fault8.test
Normal file
@ -0,0 +1,49 @@
|
||||
# 2015 September 3
|
||||
#
|
||||
# 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 is focused on OOM errors.
|
||||
#
|
||||
|
||||
source [file join [file dirname [info script]] fts5_common.tcl]
|
||||
source $testdir/malloc_common.tcl
|
||||
set testprefix fts5fault8
|
||||
|
||||
# If SQLITE_ENABLE_FTS3 is defined, omit this file.
|
||||
ifcapable !fts5 {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
foreach_detail_mode $testprefix {
|
||||
|
||||
if {[detail_is_none]==0} continue
|
||||
|
||||
fts5_aux_test_functions db
|
||||
do_execsql_test 1.0 {
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(a, b, detail=%DETAIL%);
|
||||
INSERT INTO t1 VALUES('a b c d', '1 2 3 4');
|
||||
INSERT INTO t1 VALUES('a b a b', NULL);
|
||||
INSERT INTO t1 VALUES(NULL, '1 2 1 2');
|
||||
}
|
||||
|
||||
do_faultsim_test 1 -faults oom-t* -body {
|
||||
execsql {
|
||||
SELECT rowid, fts5_test_poslist(t1) FROM t1 WHERE t1 MATCH 'b OR 2'
|
||||
}
|
||||
} -test {
|
||||
faultsim_test_result {0 {1 {0.0.1 1.1.1} 2 {0.0.1 0.0.3} 3 {1.1.1 1.1.3}}} \
|
||||
{1 SQLITE_NOMEM}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
finish_test
|
||||
|
@ -16,6 +16,8 @@ set testprefix fts5matchinfo
|
||||
# If SQLITE_ENABLE_FTS5 is not defined, omit this file.
|
||||
ifcapable !fts5 { finish_test ; return }
|
||||
|
||||
foreach_detail_mode $testprefix {
|
||||
|
||||
proc mit {blob} {
|
||||
set scan(littleEndian) i*
|
||||
set scan(bigEndian) I*
|
||||
@ -27,7 +29,7 @@ db func mit mit
|
||||
sqlite3_fts5_register_matchinfo db
|
||||
|
||||
do_execsql_test 1.0 {
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(content);
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(content, detail=%DETAIL%);
|
||||
}
|
||||
|
||||
do_execsql_test 1.1 {
|
||||
@ -41,7 +43,7 @@ do_execsql_test 1.1 {
|
||||
# Now create an FTS4 table that does not specify matchinfo=fts3.
|
||||
#
|
||||
do_execsql_test 1.2 {
|
||||
CREATE VIRTUAL TABLE t2 USING fts5(content);
|
||||
CREATE VIRTUAL TABLE t2 USING fts5(content, detail=%DETAIL%);
|
||||
INSERT INTO t2 SELECT * FROM t1;
|
||||
SELECT mit(matchinfo(t2)) FROM t2 WHERE t2 MATCH 'I';
|
||||
} {{1 1 1 2 2} {1 1 1 2 2}}
|
||||
@ -149,9 +151,17 @@ proc normalize2 {list_of_lists} {
|
||||
return $res
|
||||
}
|
||||
|
||||
# Similar to [do_matchinfo_test], except that this is a no-op if the FTS5
|
||||
# mode is not detail=full.
|
||||
#
|
||||
proc do_matchinfo_p_test {tn tbl expr results} {
|
||||
if {[detail_is_full]} {
|
||||
uplevel [list do_matchinfo_test $tn $tbl $expr $results]
|
||||
}
|
||||
}
|
||||
|
||||
do_execsql_test 4.1.0 {
|
||||
CREATE VIRTUAL TABLE t4 USING fts5(x, y);
|
||||
CREATE VIRTUAL TABLE t4 USING fts5(x, y, detail=%DETAIL%);
|
||||
INSERT INTO t4 VALUES('a b c d e', 'f g h i j');
|
||||
INSERT INTO t4 VALUES('f g h i j', 'a b c d e');
|
||||
}
|
||||
@ -185,7 +195,7 @@ do_matchinfo_test 4.1.1 t4 {t4 MATCH 'a b c'} {
|
||||
xpxsscplax -
|
||||
}
|
||||
|
||||
do_matchinfo_test 4.1.2 t4 {t4 MATCH '"g h i"'} {
|
||||
do_matchinfo_p_test 4.1.2 t4 {t4 MATCH '"g h i"'} {
|
||||
p {1 1}
|
||||
c {2 2}
|
||||
x {
|
||||
@ -203,8 +213,8 @@ do_matchinfo_test 4.1.2 t4 {t4 MATCH '"g h i"'} {
|
||||
}
|
||||
|
||||
do_matchinfo_test 4.1.3 t4 {t4 MATCH 'a b'} { s {{2 0} {0 2}} }
|
||||
do_matchinfo_test 4.1.4 t4 {t4 MATCH '"a b" c'} { s {{2 0} {0 2}} }
|
||||
do_matchinfo_test 4.1.5 t4 {t4 MATCH 'a "b c"'} { s {{2 0} {0 2}} }
|
||||
do_matchinfo_p_test 4.1.4 t4 {t4 MATCH '"a b" c'} { s {{2 0} {0 2}} }
|
||||
do_matchinfo_p_test 4.1.5 t4 {t4 MATCH 'a "b c"'} { s {{2 0} {0 2}} }
|
||||
do_matchinfo_test 4.1.6 t4 {t4 MATCH 'd d'} { s {{1 0} {0 1}} }
|
||||
do_matchinfo_test 4.1.7 t4 {t4 MATCH 'f OR abcd'} {
|
||||
x {
|
||||
@ -220,7 +230,7 @@ do_matchinfo_test 4.1.8 t4 {t4 MATCH 'f NOT abcd'} {
|
||||
}
|
||||
|
||||
do_execsql_test 4.2.0 {
|
||||
CREATE VIRTUAL TABLE t5 USING fts5(content);
|
||||
CREATE VIRTUAL TABLE t5 USING fts5(content, detail=%DETAIL%);
|
||||
INSERT INTO t5 VALUES('a a a a a');
|
||||
INSERT INTO t5 VALUES('a b a b a');
|
||||
INSERT INTO t5 VALUES('c b c b c');
|
||||
@ -233,7 +243,7 @@ do_matchinfo_test 4.2.1 t5 {t5 MATCH 'a a'} {
|
||||
do_matchinfo_test 4.2.2 t5 {t5 MATCH 'a b'} { s {2} }
|
||||
do_matchinfo_test 4.2.3 t5 {t5 MATCH 'a b a'} { s {3} }
|
||||
do_matchinfo_test 4.2.4 t5 {t5 MATCH 'a a a'} { s {3 1} }
|
||||
do_matchinfo_test 4.2.5 t5 {t5 MATCH '"a b" "a b"'} { s {2} }
|
||||
do_matchinfo_p_test 4.2.5 t5 {t5 MATCH '"a b" "a b"'} { s {2} }
|
||||
do_matchinfo_test 4.2.6 t5 {t5 MATCH 'a OR b'} { s {1 2 1} }
|
||||
|
||||
do_execsql_test 4.3.0 "INSERT INTO t5 VALUES('x y [string repeat {b } 50000]')";
|
||||
@ -250,7 +260,7 @@ if 0 {
|
||||
do_matchinfo_test 4.3.2 t5 {t5 MATCH 'a b'} { s {2} }
|
||||
do_matchinfo_test 4.3.3 t5 {t5 MATCH 'a b a'} { s {3} }
|
||||
do_matchinfo_test 4.3.4 t5 {t5 MATCH 'a a a'} { s {3 1} }
|
||||
do_matchinfo_test 4.3.5 t5 {t5 MATCH '"a b" "a b"'} { s {2} }
|
||||
do_matchinfo_p_test 4.3.5 t5 {t5 MATCH '"a b" "a b"'} { s {2} }
|
||||
do_matchinfo_test 4.3.6 t5 {t5 MATCH 'a OR b'} { s {1 2 1 1} }
|
||||
|
||||
do_execsql_test 4.4.0.1 { INSERT INTO t5(t5) VALUES('optimize') }
|
||||
@ -260,10 +270,10 @@ do_matchinfo_test 4.4.1 t5 {t5 MATCH 'a a'} { s {2 1} }
|
||||
do_matchinfo_test 4.4.2 t5 {t5 MATCH 'a b'} { s {2} }
|
||||
do_matchinfo_test 4.4.3 t5 {t5 MATCH 'a b a'} { s {3} }
|
||||
do_matchinfo_test 4.4.4 t5 {t5 MATCH 'a a a'} { s {3 1} }
|
||||
do_matchinfo_test 4.4.5 t5 {t5 MATCH '"a b" "a b"'} { s {2} }
|
||||
do_matchinfo_p_test 4.4.5 t5 {t5 MATCH '"a b" "a b"'} { s {2} }
|
||||
|
||||
do_execsql_test 4.5.0 {
|
||||
CREATE VIRTUAL TABLE t6 USING fts5(a, b, c);
|
||||
CREATE VIRTUAL TABLE t6 USING fts5(a, b, c, detail=%DETAIL%);
|
||||
INSERT INTO t6 VALUES('a', 'b', 'c');
|
||||
}
|
||||
do_matchinfo_test 4.5.1 t6 {t6 MATCH 'a b c'} { s {{1 1 1}} }
|
||||
@ -274,7 +284,7 @@ do_matchinfo_test 4.5.1 t6 {t6 MATCH 'a b c'} { s {{1 1 1}} }
|
||||
# use the full-text index (i.e. lookup by rowid or full-table scan).
|
||||
#
|
||||
do_execsql_test 7.1 {
|
||||
CREATE VIRTUAL TABLE t10 USING fts5(content);
|
||||
CREATE VIRTUAL TABLE t10 USING fts5(content, detail=%DETAIL%);
|
||||
INSERT INTO t10 VALUES('first record');
|
||||
INSERT INTO t10 VALUES('second record');
|
||||
}
|
||||
@ -299,7 +309,7 @@ do_execsql_test 7.4 {
|
||||
# SELECT sum(length(content)) < count(*) FROM fts4table;
|
||||
#
|
||||
do_execsql_test 8.1 {
|
||||
CREATE VIRTUAL TABLE t11 USING fts5(content);
|
||||
CREATE VIRTUAL TABLE t11 USING fts5(content, detail=%DETAIL%);
|
||||
INSERT INTO t11(t11, rank) VALUES('pgsz', 32);
|
||||
INSERT INTO t11 VALUES('quitealongstringoftext');
|
||||
INSERT INTO t11 VALUES('anotherquitealongstringoftext');
|
||||
@ -318,22 +328,24 @@ do_execsql_test 8.3 {
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
|
||||
do_execsql_test 9.1 {
|
||||
CREATE VIRTUAL TABLE t12 USING fts5(content);
|
||||
INSERT INTO t12 VALUES('a b c d');
|
||||
SELECT mit(matchinfo(t12, 'x')) FROM t12 WHERE t12 MATCH 'NEAR(a d, 1) OR a';
|
||||
} {{0 1 1 0 1 1 1 1 1}}
|
||||
do_execsql_test 9.2 {
|
||||
INSERT INTO t12 VALUES('a d c d');
|
||||
SELECT mit(matchinfo(t12, 'x')) FROM t12 WHERE t12 MATCH 'NEAR(a d, 1) OR a';
|
||||
} {
|
||||
{0 2 2 0 3 2 1 2 2} {1 2 2 1 3 2 1 2 2}
|
||||
}
|
||||
do_execsql_test 9.3 {
|
||||
INSERT INTO t12 VALUES('a d d a');
|
||||
SELECT mit(matchinfo(t12, 'x')) FROM t12 WHERE t12 MATCH 'NEAR(a d, 1) OR a';
|
||||
} {
|
||||
{0 4 3 0 5 3 1 4 3} {1 4 3 1 5 3 1 4 3} {2 4 3 2 5 3 2 4 3}
|
||||
if {[detail_is_full]} {
|
||||
do_execsql_test 9.1 {
|
||||
CREATE VIRTUAL TABLE t12 USING fts5(content, detail=%DETAIL%);
|
||||
INSERT INTO t12 VALUES('a b c d');
|
||||
SELECT mit(matchinfo(t12,'x')) FROM t12 WHERE t12 MATCH 'NEAR(a d, 1) OR a';
|
||||
} {{0 1 1 0 1 1 1 1 1}}
|
||||
do_execsql_test 9.2 {
|
||||
INSERT INTO t12 VALUES('a d c d');
|
||||
SELECT mit(matchinfo(t12,'x')) FROM t12 WHERE t12 MATCH 'NEAR(a d, 1) OR a';
|
||||
} {
|
||||
{0 2 2 0 3 2 1 2 2} {1 2 2 1 3 2 1 2 2}
|
||||
}
|
||||
do_execsql_test 9.3 {
|
||||
INSERT INTO t12 VALUES('a d d a');
|
||||
SELECT mit(matchinfo(t12,'x')) FROM t12 WHERE t12 MATCH 'NEAR(a d, 1) OR a';
|
||||
} {
|
||||
{0 4 3 0 5 3 1 4 3} {1 4 3 1 5 3 1 4 3} {2 4 3 2 5 3 2 4 3}
|
||||
}
|
||||
}
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
@ -341,7 +353,7 @@ do_execsql_test 9.3 {
|
||||
#
|
||||
do_execsql_test 10.1 {
|
||||
DROP TABLE t10;
|
||||
CREATE VIRTUAL TABLE t10 USING fts5(idx, value);
|
||||
CREATE VIRTUAL TABLE t10 USING fts5(idx, value, detail=%DETAIL%);
|
||||
INSERT INTO t10 values (1, 'one'),(2, 'two'),(3, 'three');
|
||||
SELECT t10.rowid, t10.*
|
||||
FROM t10
|
||||
@ -358,7 +370,7 @@ do_execsql_test 10.1 {
|
||||
reset_db
|
||||
sqlite3_fts5_register_matchinfo db
|
||||
do_execsql_test 11.0 {
|
||||
CREATE VIRTUAL TABLE tt USING fts5(x, y);
|
||||
CREATE VIRTUAL TABLE tt USING fts5(x, y, detail=%DETAIL%);
|
||||
INSERT INTO tt VALUES('c d a c d d', 'e a g b d a'); -- 1
|
||||
INSERT INTO tt VALUES('c c g a e b', 'c g d g e c'); -- 2
|
||||
INSERT INTO tt VALUES('b e f d e g', 'b a c b c g'); -- 3
|
||||
@ -410,6 +422,8 @@ foreach {tn expr res} {
|
||||
}
|
||||
|
||||
} {
|
||||
|
||||
if {[string match *:* $expr] && [detail_is_none]} continue
|
||||
do_execsql_test 11.1.$tn.1 {
|
||||
SELECT rowid, mit(matchinfo(tt, 'y')) FROM tt WHERE tt MATCH $expr
|
||||
} $res
|
||||
@ -443,7 +457,7 @@ db func mit mit
|
||||
do_test 12.0 {
|
||||
set cols [list]
|
||||
for {set i 0} {$i < 50} {incr i} { lappend cols "c$i" }
|
||||
execsql "CREATE VIRTUAL TABLE tt USING fts5([join $cols ,])"
|
||||
execsql "CREATE VIRTUAL TABLE tt USING fts5([join $cols ,], detail=%DETAIL%)"
|
||||
} {}
|
||||
|
||||
do_execsql_test 12.1 {
|
||||
@ -451,5 +465,7 @@ do_execsql_test 12.1 {
|
||||
SELECT mit(matchinfo(tt, 'b')) FROM tt WHERE tt MATCH 'abc';
|
||||
} [list [list [expr 1<<4] [expr 1<<(45-32)]]]
|
||||
|
||||
} ;# foreach_detail_mode
|
||||
|
||||
finish_test
|
||||
|
||||
|
@ -18,7 +18,9 @@ ifcapable !fts5 {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
if 1 {
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
set doc "x x [string repeat {y } 50]z z"
|
||||
@ -350,6 +352,35 @@ do_execsql_test 4.1 {
|
||||
SELECT rowid, x, x1 FROM x1 WHERE x1 MATCH '*reads'
|
||||
} {0 {} 4}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
do_execsql_test 15.0 {
|
||||
CREATE VIRTUAL TABLE x2 USING fts5(x, prefix=1);
|
||||
INSERT INTO x2 VALUES('ab');
|
||||
}
|
||||
|
||||
do_execsql_test 15.1 {
|
||||
INSERT INTO x2(x2) VALUES('integrity-check');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
foreach_detail_mode $testprefix {
|
||||
reset_db
|
||||
fts5_aux_test_functions db
|
||||
do_execsql_test 16.0 {
|
||||
CREATE VIRTUAL TABLE x3 USING fts5(x, detail=%DETAIL%);
|
||||
INSERT INTO x3 VALUES('a b c d e f');
|
||||
}
|
||||
do_execsql_test 16.1 {
|
||||
SELECT fts5_test_poslist(x3) FROM x3('(a NOT b) OR c');
|
||||
} {2.0.2}
|
||||
|
||||
do_execsql_test 16.1 {
|
||||
SELECT fts5_test_poslist(x3) FROM x3('a OR c');
|
||||
} {{0.0.0 1.0.2}}
|
||||
}
|
||||
|
||||
finish_test
|
||||
|
||||
|
304
ext/fts5/test/fts5simple2.test
Normal file
304
ext/fts5/test/fts5simple2.test
Normal file
@ -0,0 +1,304 @@
|
||||
# 2015 September 05
|
||||
#
|
||||
# The author disclaims copyright to this source code. In place of
|
||||
# a legal notice, here is a blessing:
|
||||
#
|
||||
# May you do good and not evil.
|
||||
# May you find forgiveness for yourself and forgive others.
|
||||
# May you share freely, never taking more than you give.
|
||||
#
|
||||
#*************************************************************************
|
||||
#
|
||||
|
||||
source [file join [file dirname [info script]] fts5_common.tcl]
|
||||
set testprefix fts5simple2
|
||||
|
||||
# If SQLITE_ENABLE_FTS5 is defined, omit this file.
|
||||
ifcapable !fts5 {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
if 1 {
|
||||
|
||||
do_execsql_test 1.0 {
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(a, detail=none);
|
||||
INSERT INTO t1 VALUES('a b c');
|
||||
}
|
||||
do_execsql_test 1.1 {
|
||||
SELECT rowid FROM t1('c a b')
|
||||
} {1}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 2.0 {
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(a, detail=none);
|
||||
BEGIN;
|
||||
INSERT INTO t1 VALUES('b c d');
|
||||
INSERT INTO t1 VALUES('b c d');
|
||||
COMMIT;
|
||||
}
|
||||
do_execsql_test 2.1 {
|
||||
SELECT rowid FROM t1('b c d')
|
||||
} {1 2}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 3.0 {
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(a, detail=none);
|
||||
BEGIN;
|
||||
INSERT INTO t1 VALUES('b c d');
|
||||
INSERT INTO t1 VALUES('b c d');
|
||||
}
|
||||
do_execsql_test 3.1 {
|
||||
SELECT rowid FROM t1('b c d'); COMMIT;
|
||||
} {1 2}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 4.0 {
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(a, detail=none);
|
||||
BEGIN;
|
||||
INSERT INTO t1 VALUES('a1 b1 c1');
|
||||
INSERT INTO t1 VALUES('a2 b2 c2');
|
||||
INSERT INTO t1 VALUES('a3 b3 c3');
|
||||
COMMIT;
|
||||
}
|
||||
do_execsql_test 4.1 {
|
||||
SELECT rowid FROM t1('b*');
|
||||
} {1 2 3}
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 5.0 {
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(a, detail=none);
|
||||
BEGIN;
|
||||
INSERT INTO t1 VALUES('a1 b1 c1');
|
||||
INSERT INTO t1 VALUES('a2 b2 c2');
|
||||
INSERT INTO t1 VALUES('a1 b1 c1');
|
||||
COMMIT;
|
||||
}
|
||||
do_execsql_test 5.1 { SELECT rowid FROM t1('b*') } {1 2 3}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 6.0 {
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(a, detail=full);
|
||||
BEGIN;
|
||||
INSERT INTO t1 VALUES('a1 b1 c1');
|
||||
INSERT INTO t1 VALUES('a1 b1 c1');
|
||||
INSERT INTO t1 VALUES('a1 b1 c1');
|
||||
COMMIT;
|
||||
}
|
||||
|
||||
do_execsql_test 6.1 { SELECT rowid FROM t1('a1') ORDER BY rowid DESC } {3 2 1}
|
||||
do_execsql_test 6.2 { SELECT rowid FROM t1('b1') ORDER BY rowid DESC } {3 2 1}
|
||||
do_execsql_test 6.3 { SELECT rowid FROM t1('c1') ORDER BY rowid DESC } {3 2 1}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 7.0 {
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(a, detail=none);
|
||||
BEGIN;
|
||||
INSERT INTO t1 VALUES('a1 b1');
|
||||
INSERT INTO t1 VALUES('a1 b2');
|
||||
COMMIT;
|
||||
}
|
||||
do_execsql_test 7.1 { SELECT rowid FROM t1('b*') ORDER BY rowid DESC } {2 1}
|
||||
do_execsql_test 7.2 { SELECT rowid FROM t1('a1') ORDER BY rowid DESC } {2 1}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 8.0 {
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(a, detail=none);
|
||||
INSERT INTO t1 VALUES('a1 b1 c1');
|
||||
INSERT INTO t1 VALUES('a2 b2 c2');
|
||||
INSERT INTO t1 VALUES('a1 b1 c1');
|
||||
}
|
||||
do_execsql_test 8.0.1 { SELECT rowid FROM t1('b*') } {1 2 3}
|
||||
do_execsql_test 8.0.2 { SELECT rowid FROM t1('a1') } {1 3}
|
||||
do_execsql_test 8.0.3 { SELECT rowid FROM t1('c2') } {2}
|
||||
|
||||
do_execsql_test 8.0.4 { SELECT rowid FROM t1('b*') ORDER BY rowid DESC } {3 2 1}
|
||||
do_execsql_test 8.0.5 { SELECT rowid FROM t1('a1') ORDER BY rowid DESC } {3 1}
|
||||
do_execsql_test 8.0.8 { SELECT rowid FROM t1('c2') ORDER BY rowid DESC } {2}
|
||||
|
||||
do_execsql_test 8.1.0 { INSERT INTO t1(t1) VALUES('optimize') }
|
||||
|
||||
do_execsql_test 8.1.1 { SELECT rowid FROM t1('b*') } {1 2 3}
|
||||
do_execsql_test 8.1.2 { SELECT rowid FROM t1('a1') } {1 3}
|
||||
do_execsql_test 8.1.3 { SELECT rowid FROM t1('c2') } {2}
|
||||
|
||||
do_execsql_test 8.2.1 { SELECT rowid FROM t1('b*') ORDER BY rowid DESC} {3 2 1}
|
||||
do_execsql_test 8.2.2 { SELECT rowid FROM t1('a1') ORDER BY rowid DESC} {3 1}
|
||||
do_execsql_test 8.2.3 { SELECT rowid FROM t1('c2') ORDER BY rowid DESC} {2}
|
||||
|
||||
#--------------------------------------------------------------------------
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 9.0.0 {
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(a, detail=none);
|
||||
INSERT INTO t1 VALUES('a1 b1 c1');
|
||||
INSERT INTO t1 VALUES('a2 b2 c2');
|
||||
INSERT INTO t1 VALUES('a1 b1 c1');
|
||||
}
|
||||
do_execsql_test 9.0.1 {
|
||||
INSERT INTO t1(t1) VALUES('integrity-check');
|
||||
} {}
|
||||
|
||||
reset_db
|
||||
do_execsql_test 9.1.0 {
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(a, b, detail=none);
|
||||
INSERT INTO t1 VALUES('a1 b1 c1', 'x y z');
|
||||
INSERT INTO t1 VALUES('a2 b2 c2', '1 2 3');
|
||||
INSERT INTO t1 VALUES('a1 b1 c1', 'x 2 z');
|
||||
}
|
||||
do_execsql_test 9.2.1 {
|
||||
INSERT INTO t1(t1) VALUES('integrity-check');
|
||||
} {}
|
||||
|
||||
#--------------------------------------------------------------------------
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 10.0 {
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(a, detail=none);
|
||||
INSERT INTO t1 VALUES('b1');
|
||||
INSERT INTO t1 VALUES('b1');
|
||||
DELETE FROM t1 WHERE rowid=1;
|
||||
}
|
||||
|
||||
do_execsql_test 10.1 {
|
||||
SELECT rowid FROM t1('b1');
|
||||
} {2}
|
||||
|
||||
do_execsql_test 10.2 {
|
||||
SELECT rowid FROM t1('b1') ORDER BY rowid DESC;
|
||||
} {2}
|
||||
|
||||
do_execsql_test 10.3 {
|
||||
INSERT INTO t1(t1) VALUES('integrity-check');
|
||||
} {}
|
||||
|
||||
#--------------------------------------------------------------------------
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 11.1 {
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(x, y, detail=none);
|
||||
INSERT INTO t1(t1, rank) VALUES('pgsz', 32);
|
||||
WITH d(x,y) AS (
|
||||
SELECT NULL, 'xyz' UNION ALL SELECT NULL, 'xyz' FROM d
|
||||
)
|
||||
INSERT INTO t1 SELECT * FROM d LIMIT 23;
|
||||
}
|
||||
|
||||
#db eval { SELECT rowid AS r, quote(block) AS b FROM t1_data } { puts "$r: $b" }
|
||||
do_execsql_test 11.2 {
|
||||
SELECT rowid FROM t1;
|
||||
} {1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23}
|
||||
|
||||
do_execsql_test 11.3 {
|
||||
SELECT rowid FROM t1('xyz');
|
||||
} {1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23}
|
||||
|
||||
do_execsql_test 11.4 {
|
||||
INSERT INTO t1(t1) VALUES('integrity-check');
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 12.0 {
|
||||
CREATE VIRTUAL TABLE yy USING fts5(x, detail=none);
|
||||
INSERT INTO yy VALUES('in if');
|
||||
INSERT INTO yy VALUES('if');
|
||||
} {}
|
||||
|
||||
do_execsql_test 12.1 {
|
||||
SELECT rowid FROM yy('i*');
|
||||
} {1 2}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 13.0 {
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(a, prefix=1, detail=none);
|
||||
} {}
|
||||
foreach {rowid a} {
|
||||
0 {f}
|
||||
1 {u}
|
||||
2 {k}
|
||||
3 {a}
|
||||
4 {a}
|
||||
5 {u}
|
||||
6 {u}
|
||||
7 {u}
|
||||
8 {f}
|
||||
9 {f}
|
||||
10 {a}
|
||||
11 {p}
|
||||
12 {f}
|
||||
13 {u}
|
||||
14 {a}
|
||||
15 {a}
|
||||
} {
|
||||
do_execsql_test 13.1.$rowid {
|
||||
INSERT INTO t1(rowid, a) VALUES($rowid, $a);
|
||||
}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
reset_db
|
||||
fts5_aux_test_functions db
|
||||
do_execsql_test 14.0 {
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(a, detail=none);
|
||||
INSERT INTO t1 VALUES('a b c d');
|
||||
} {}
|
||||
|
||||
do_execsql_test 14.1 {
|
||||
SELECT fts5_test_poslist(t1) FROM t1('b') ORDER BY rank;
|
||||
} {0.0.1}
|
||||
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 15.1 {
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(x, detail=none);
|
||||
BEGIN;
|
||||
INSERT INTO t1(rowid, x) VALUES(1, 'sqlite');
|
||||
INSERT INTO t1(rowid, x) VALUES(2, 'sqlite');
|
||||
COMMIT;
|
||||
} {}
|
||||
|
||||
do_test 15.1 {
|
||||
execsql { INSERT INTO t1(t1) VALUES('integrity-check') }
|
||||
} {}
|
||||
|
||||
do_test 15.2 {
|
||||
execsql { DELETE FROM t1 }
|
||||
} {}
|
||||
|
||||
do_execsql_test 15.3.1 {
|
||||
SELECT rowid FROM t1('sqlite');
|
||||
} {}
|
||||
|
||||
do_execsql_test 15.3.2 {
|
||||
SELECT rowid FROM t1('sqlite') ORDER BY rowid DESC;
|
||||
} {}
|
||||
|
||||
do_test 15.4 {
|
||||
execsql { INSERT INTO t1(t1) VALUES('integrity-check') }
|
||||
} {}
|
||||
|
||||
finish_test
|
||||
|
154
ext/fts5/test/fts5synonym2.test
Normal file
154
ext/fts5/test/fts5synonym2.test
Normal file
@ -0,0 +1,154 @@
|
||||
# 2014 Dec 20
|
||||
#
|
||||
# The author disclaims copyright to this source code. In place of
|
||||
# a legal notice, here is a blessing:
|
||||
#
|
||||
# May you do good and not evil.
|
||||
# May you find forgiveness for yourself and forgive others.
|
||||
# May you share freely, never taking more than you give.
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
# Tests focusing on custom tokenizers that support synonyms.
|
||||
#
|
||||
|
||||
source [file join [file dirname [info script]] fts5_common.tcl]
|
||||
set testprefix fts5synonym2
|
||||
|
||||
# If SQLITE_ENABLE_FTS5 is defined, omit this file.
|
||||
ifcapable !fts5 {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Code for a simple Tcl tokenizer that supports synonyms at query time.
|
||||
#
|
||||
foreach SYNDICT {
|
||||
{zero 0}
|
||||
{one 1 i}
|
||||
{two 2 ii}
|
||||
{three 3 iii}
|
||||
{four 4 iv}
|
||||
{five 5 v}
|
||||
{six 6 vi}
|
||||
{seven 7 vii}
|
||||
{eight 8 viii}
|
||||
{nine 9 ix}
|
||||
} {
|
||||
foreach s $SYNDICT {
|
||||
set o [list]
|
||||
foreach x $SYNDICT {if {$x!=$s} {lappend o $x}}
|
||||
set ::syn($s) $o
|
||||
}
|
||||
}
|
||||
|
||||
proc tcl_tokenize {tflags text} {
|
||||
foreach {w iStart iEnd} [fts5_tokenize_split $text] {
|
||||
sqlite3_fts5_token $w $iStart $iEnd
|
||||
if {$tflags == "query"} {
|
||||
foreach s $::syn($w) { sqlite3_fts5_token -colo $s $iStart $iEnd }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
proc tcl_create {args} {
|
||||
return "tcl_tokenize"
|
||||
}
|
||||
|
||||
#
|
||||
# End of tokenizer code.
|
||||
#-------------------------------------------------------------------------
|
||||
|
||||
foreach_detail_mode $testprefix {
|
||||
|
||||
sqlite3_fts5_create_tokenizer db tcl tcl_create
|
||||
fts5_aux_test_functions db
|
||||
|
||||
do_execsql_test 1.0 {
|
||||
CREATE VIRTUAL TABLE ss USING fts5(a, b, tokenize=tcl, detail=%DETAIL%);
|
||||
INSERT INTO ss VALUES('5 5 five seven 3 seven i', '2 1 5 0 two 1 i');
|
||||
INSERT INTO ss VALUES('six ix iii 7 i vii iii', 'one seven nine 4 9 1 vi');
|
||||
INSERT INTO ss VALUES('6 viii i five six zero seven', '5 v iii iv iv 3');
|
||||
INSERT INTO ss VALUES('9 ii six 8 1 6', 'six 4 iv iv 7');
|
||||
INSERT INTO ss VALUES('1 5 4 eight ii iv iii', 'nine 2 eight ix v vii');
|
||||
INSERT INTO ss VALUES('one 7 seven six 2 two', '1 2 four 7 4 3 4');
|
||||
INSERT INTO ss VALUES('eight iv 4 nine vii six 1', '5 6 v one zero 4');
|
||||
INSERT INTO ss VALUES('v 9 8 iii 4', '9 4 seven two vi vii');
|
||||
INSERT INTO ss VALUES('3 ix two 9 0 nine i', 'five ii nine two viii i five');
|
||||
INSERT INTO ss VALUES('six iii 9 two eight 2', 'nine i nine vii nine');
|
||||
INSERT INTO ss VALUES('6 three zero seven vii five', '8 vii ix 0 7 seven');
|
||||
INSERT INTO ss VALUES('8 vii 8 7 3 4', 'eight iii four viii nine iv three');
|
||||
INSERT INTO ss VALUES('4 v 7 two 0 one 8', 'vii 1 two five i zero 9');
|
||||
INSERT INTO ss VALUES('3 ii vii vi eight', '8 4 ix one three eight');
|
||||
INSERT INTO ss VALUES('iv eight seven 6 9 seven', 'one vi two five seven');
|
||||
INSERT INTO ss VALUES('i i 5 i v vii eight', '2 seven i 2 2 four');
|
||||
INSERT INTO ss VALUES('0 i iii nine 3 ix five', '0 eight iv 0 six 2');
|
||||
INSERT INTO ss VALUES('iv vii three 3 9 one 8', '2 ii 6 eight ii six six');
|
||||
INSERT INTO ss VALUES('eight one two nine six', '8 9 3 viii vi');
|
||||
INSERT INTO ss VALUES('one 0 four ii eight one 3', 'iii eight vi vi vi');
|
||||
INSERT INTO ss VALUES('4 0 eight 0 0', '1 four one vii seven ii');
|
||||
INSERT INTO ss VALUES('1 zero nine 2 2', 'viii iv two vi nine v iii');
|
||||
INSERT INTO ss VALUES('5 five viii four four vi', '8 five 7 vii 6 4');
|
||||
INSERT INTO ss VALUES('7 ix four 8 vii', 'nine three nine ii ix vii');
|
||||
INSERT INTO ss VALUES('nine iv v i 0 v', 'two iv vii six i ix 4');
|
||||
INSERT INTO ss VALUES('one v v one viii 3 8', '2 1 3 five iii');
|
||||
INSERT INTO ss VALUES('six ii 5 nine 4 viii seven', 'eight i ix ix 7 four');
|
||||
INSERT INTO ss VALUES('9 ii two seven three 7 0', 'six viii seven 7 five');
|
||||
INSERT INTO ss VALUES('five two 4 viii nine', '9 7 nine zero 1 two one');
|
||||
INSERT INTO ss VALUES('viii 8 iii i ii 8 3', '4 2 7 v 8 8');
|
||||
INSERT INTO ss VALUES('four vii 4 iii zero 0 vii', '3 viii iii zero 9 i');
|
||||
INSERT INTO ss VALUES('0 seven v five i five v', 'one 4 2 ix 9');
|
||||
INSERT INTO ss VALUES('two 5 two two ix 4 1', '3 nine ii v nine 3 five');
|
||||
INSERT INTO ss VALUES('five 5 7 4 6 vii', 'three 2 ix 2 8 6');
|
||||
INSERT INTO ss VALUES('six iii vi iv seven eight', '8 six 7 0 4');
|
||||
INSERT INTO ss VALUES('vi vi iv 3 0 one one', '9 6 eight ix iv');
|
||||
INSERT INTO ss VALUES('7 2 2 iii 0', '0 0 seven 1 nine');
|
||||
INSERT INTO ss VALUES('8 6 iv six ii', 'iv 6 3 4 ii five');
|
||||
INSERT INTO ss VALUES('0 two two seven ii', 'vii ix four 4 zero vi vi');
|
||||
INSERT INTO ss VALUES('2 one eight 8 9 7', 'vi 3 0 3 vii');
|
||||
INSERT INTO ss VALUES('iii ii ix iv three', 'vi i 6 1 two');
|
||||
INSERT INTO ss VALUES('eight four nine 8 seven', 'one three i nine iii one');
|
||||
INSERT INTO ss VALUES('iii seven five ix 8', 'ii 7 seven 0 four ii');
|
||||
INSERT INTO ss VALUES('four 0 1 5 two', 'iii 9 5 ii ii 2 4');
|
||||
INSERT INTO ss VALUES('iii nine four vi 8 five six', 'i i ii seven vi vii');
|
||||
INSERT INTO ss VALUES('eight vii eight six 3', 'i vii 1 six 9 vii');
|
||||
INSERT INTO ss VALUES('9 0 viii viii five', 'i 1 viii ix 3 4');
|
||||
INSERT INTO ss VALUES('three nine 5 nine viii four zero', 'ii i 1 5 2 viii');
|
||||
INSERT INTO ss VALUES('5 vii three 9 four', 'three five one 7 2 eight one');
|
||||
}
|
||||
|
||||
foreach {tn expr} {
|
||||
1.1 "one" 1.2 "two" 1.3 "three" 1.4 "four"
|
||||
1.5 "v" 1.6 "vi" 1.7 "vii" 1.8 "viii"
|
||||
1.9 "9" 1.10 "0" 1.11 "1" 1.12 "2"
|
||||
|
||||
2.1 "one OR two OR three OR four"
|
||||
2.2 "(one AND two) OR (three AND four)"
|
||||
2.3 "(one AND two) OR (three AND four) NOT five"
|
||||
2.4 "(one AND two) NOT 6"
|
||||
|
||||
3.1 "b:one AND a:two"
|
||||
3.2 "b:one OR a:two"
|
||||
3.3 "a:one OR b:1 OR {a b} : i"
|
||||
|
||||
4.1 "NEAR(one two, 2)"
|
||||
4.2 "NEAR(one two three, 2)"
|
||||
4.3 "NEAR(eight nine, 1) OR NEAR(six seven, 1)"
|
||||
} {
|
||||
if {[fts5_expr_ok $expr ss]==0} {
|
||||
do_test 1.$tn.OMITTED { list } [list]
|
||||
continue
|
||||
}
|
||||
|
||||
set res [fts5_query_data $expr ss ASC ::syn]
|
||||
breakpoint
|
||||
do_execsql_test 1.$tn.[llength $res].asc {
|
||||
SELECT rowid, fts5_test_poslist(ss), fts5_test_collist(ss) FROM ss($expr)
|
||||
} $res
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
finish_test
|
||||
|
@ -21,9 +21,44 @@ ifcapable !fts5 {
|
||||
return
|
||||
}
|
||||
|
||||
foreach_detail_mode $testprefix {
|
||||
|
||||
proc null_list_entries {iFirst nInterval L} {
|
||||
for {set i $iFirst} {$i < [llength $L]} {incr i $nInterval} {
|
||||
lset L $i {}
|
||||
}
|
||||
return $L
|
||||
}
|
||||
|
||||
proc star_from_row {L} {
|
||||
if {[detail_is_full]==0} {
|
||||
set L [null_list_entries 2 3 $L]
|
||||
}
|
||||
return $L
|
||||
}
|
||||
|
||||
proc star_from_col {L} {
|
||||
if {[detail_is_col]} {
|
||||
set L [null_list_entries 3 4 $L]
|
||||
}
|
||||
if {[detail_is_none]} {
|
||||
set L [null_list_entries 1 4 $L]
|
||||
set L [null_list_entries 3 4 $L]
|
||||
}
|
||||
return $L
|
||||
}
|
||||
|
||||
proc row_to_col {L} {
|
||||
if {[detail_is_none]==0} { error "this is for detail=none mode" }
|
||||
set ret [list]
|
||||
foreach {a b c} $L {
|
||||
lappend ret $a {} $b {}
|
||||
}
|
||||
set ret
|
||||
}
|
||||
|
||||
do_execsql_test 1.1.1 {
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(one, prefix=1);
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(one, prefix=1, detail=%DETAIL%);
|
||||
CREATE VIRTUAL TABLE v1 USING fts5vocab(t1, 'row');
|
||||
PRAGMA table_info = v1;
|
||||
} {
|
||||
@ -52,32 +87,32 @@ do_execsql_test 1.3 {
|
||||
|
||||
do_execsql_test 1.4.1 {
|
||||
SELECT * FROM v1;
|
||||
} {x 2 4 y 1 1 z 1 1}
|
||||
} [star_from_row {x 2 4 y 1 1 z 1 1}]
|
||||
|
||||
do_execsql_test 1.4.2 {
|
||||
SELECT * FROM v2;
|
||||
} {x one 2 4 y one 1 1 z one 1 1}
|
||||
} [star_from_col {x one 2 4 y one 1 1 z one 1 1}]
|
||||
|
||||
do_execsql_test 1.5.1 {
|
||||
BEGIN;
|
||||
INSERT INTO t1 VALUES('a b c');
|
||||
SELECT * FROM v1 WHERE term<'d';
|
||||
} {a 1 1 b 1 1 c 1 1}
|
||||
} [star_from_row {a 1 1 b 1 1 c 1 1}]
|
||||
|
||||
do_execsql_test 1.5.2 {
|
||||
SELECT * FROM v2 WHERE term<'d';
|
||||
COMMIT;
|
||||
} {a one 1 1 b one 1 1 c one 1 1}
|
||||
} [star_from_col {a one 1 1 b one 1 1 c one 1 1}]
|
||||
|
||||
do_execsql_test 1.6 {
|
||||
DELETE FROM t1 WHERE one = 'a b c';
|
||||
SELECT * FROM v1;
|
||||
} {x 2 4 y 1 1 z 1 1}
|
||||
} [star_from_row {x 2 4 y 1 1 z 1 1}]
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
do_execsql_test 2.0 {
|
||||
CREATE VIRTUAL TABLE tt USING fts5(a, b);
|
||||
CREATE VIRTUAL TABLE tt USING fts5(a, b, detail=%DETAIL%);
|
||||
INSERT INTO tt VALUES('d g b f d f', 'f c e c d a');
|
||||
INSERT INTO tt VALUES('f a e a a b', 'e d c f d d');
|
||||
INSERT INTO tt VALUES('b c a a a b', 'f f c c b c');
|
||||
@ -90,7 +125,12 @@ do_execsql_test 2.0 {
|
||||
INSERT INTO tt VALUES('c c a a c f', 'd g a e b g');
|
||||
}
|
||||
|
||||
set res_col {
|
||||
set res_row [star_from_row {
|
||||
a 10 20 b 9 14 c 9 20 d 9 19
|
||||
e 8 13 f 10 20 g 7 14 x 1 1
|
||||
y 1 1
|
||||
}]
|
||||
set res_col [star_from_col {
|
||||
a a 6 11 a b 7 9
|
||||
b a 6 7 b b 7 7
|
||||
c a 6 12 c b 5 8
|
||||
@ -99,11 +139,9 @@ set res_col {
|
||||
f a 9 10 f b 7 10
|
||||
g a 5 7 g b 5 7
|
||||
x a 1 1 y b 1 1
|
||||
}
|
||||
set res_row {
|
||||
a 10 20 b 9 14 c 9 20 d 9 19
|
||||
e 8 13 f 10 20 g 7 14 x 1 1
|
||||
y 1 1
|
||||
}]
|
||||
if {[detail_is_none]} {
|
||||
set res_col [row_to_col $res_row]
|
||||
}
|
||||
|
||||
foreach {tn tbl resname} {
|
||||
@ -153,9 +191,9 @@ reset_db
|
||||
forcedelete test.db2
|
||||
do_execsql_test 5.0 {
|
||||
ATTACH 'test.db2' AS aux;
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(x);
|
||||
CREATE VIRTUAL TABLE temp.t1 USING fts5(x);
|
||||
CREATE VIRTUAL TABLE aux.t1 USING fts5(x);
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(x, detail=%DETAIL%);
|
||||
CREATE VIRTUAL TABLE temp.t1 USING fts5(x, detail=%DETAIL%);
|
||||
CREATE VIRTUAL TABLE aux.t1 USING fts5(x, detail=%DETAIL%);
|
||||
|
||||
INSERT INTO main.t1 VALUES('a b c');
|
||||
INSERT INTO main.t1 VALUES('d e f');
|
||||
@ -178,18 +216,18 @@ do_execsql_test 5.1 {
|
||||
CREATE VIRTUAL TABLE temp.va USING fts5vocab(aux, t1, row);
|
||||
}
|
||||
|
||||
do_execsql_test 5.2 { SELECT * FROM vm } {
|
||||
do_execsql_test 5.2 { SELECT * FROM vm } [star_from_row {
|
||||
a 2 2 b 1 1 c 2 2 d 1 1 e 2 2 f 1 1
|
||||
}
|
||||
do_execsql_test 5.3 { SELECT * FROM vt1 } {
|
||||
}]
|
||||
do_execsql_test 5.3 { SELECT * FROM vt1 } [star_from_row {
|
||||
1 2 2 2 1 1 3 2 2 4 1 1 5 2 2 6 1 1
|
||||
}
|
||||
do_execsql_test 5.4 { SELECT * FROM vt2 } {
|
||||
}]
|
||||
do_execsql_test 5.4 { SELECT * FROM vt2 } [star_from_row {
|
||||
1 2 2 2 1 1 3 2 2 4 1 1 5 2 2 6 1 1
|
||||
}
|
||||
do_execsql_test 5.5 { SELECT * FROM va } {
|
||||
}]
|
||||
do_execsql_test 5.5 { SELECT * FROM va } [star_from_row {
|
||||
m 1 1 n 2 2 o 1 1 x 2 2 y 1 1 z 2 2
|
||||
}
|
||||
}]
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
@ -218,7 +256,7 @@ do_catchsql_test 6.2 {
|
||||
# constraints in the WHERE clause).
|
||||
#
|
||||
do_execsql_test 7.0 {
|
||||
CREATE VIRTUAL TABLE tx USING fts5(one, two);
|
||||
CREATE VIRTUAL TABLE tx USING fts5(one, two, detail=%DETAIL%);
|
||||
INSERT INTO tx VALUES('g a ggg g a b eee', 'cc d aa ff g ee');
|
||||
INSERT INTO tx VALUES('dd fff i a i jjj', 'f fff hh jj e f');
|
||||
INSERT INTO tx VALUES('ggg a f f fff dd aa', 'd ggg f f j gg ddd');
|
||||
@ -276,6 +314,10 @@ foreach {term} {
|
||||
if {[lindex $r2 2]==0} {set r2 [list]}
|
||||
|
||||
set resc [concat $r1 $r2]
|
||||
|
||||
set resc [star_from_col $resc]
|
||||
set resr [star_from_row $resr]
|
||||
if {[detail_is_none]} { set resc [row_to_col $resr] }
|
||||
do_execsql_test 7.$term.1 {SELECT * FROM txc WHERE term=$term} $resc
|
||||
do_execsql_test 7.$term.2 {SELECT * FROM txr WHERE term=$term} $resr
|
||||
}
|
||||
@ -340,10 +382,14 @@ do_execsql_test 7.3.1 {
|
||||
SELECT count(*) FROM txr, txr_c WHERE txr.term = txr_c.term;
|
||||
} {30}
|
||||
|
||||
do_execsql_test 7.3.2 {
|
||||
SELECT count(*) FROM txc, txc_c
|
||||
WHERE txc.term = txc_c.term AND txc.col=txc_c.col;
|
||||
} {57}
|
||||
if {![detail_is_none]} {
|
||||
do_execsql_test 7.3.2 {
|
||||
SELECT count(*) FROM txc, txc_c
|
||||
WHERE txc.term = txc_c.term AND txc.col=txc_c.col;
|
||||
} {57}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
finish_test
|
||||
|
||||
|
59
ext/fts5/tool/fts5speed.tcl
Normal file
59
ext/fts5/tool/fts5speed.tcl
Normal file
@ -0,0 +1,59 @@
|
||||
|
||||
|
||||
set Q {
|
||||
{1 "SELECT count(*) FROM t1 WHERE t1 MATCH 'enron'"}
|
||||
{25 "SELECT count(*) FROM t1 WHERE t1 MATCH 'hours'"}
|
||||
{300 "SELECT count(*) FROM t1 WHERE t1 MATCH 'acid'"}
|
||||
{100 "SELECT count(*) FROM t1 WHERE t1 MATCH 'loaned OR mobility OR popcore OR sunk'"}
|
||||
{100 "SELECT count(*) FROM t1 WHERE t1 MATCH 'enron AND myapps'"}
|
||||
{1 "SELECT count(*) FROM t1 WHERE t1 MATCH 'en* AND my*'"}
|
||||
|
||||
{1 "SELECT count(*) FROM t1 WHERE t1 MATCH 'c:t*'"}
|
||||
{1 "SELECT count(*) FROM t1 WHERE t1 MATCH 'a:t* OR b:t* OR c:t* OR d:t* OR e:t* OR f:t* OR g:t*'"}
|
||||
{1 "SELECT count(*) FROM t1 WHERE t1 MATCH 'a:t*'"}
|
||||
}
|
||||
|
||||
proc usage {} {
|
||||
global Q
|
||||
puts stderr "Usage: $::argv0 DATABASE QUERY"
|
||||
puts stderr ""
|
||||
for {set i 1} {$i <= [llength $Q]} {incr i} {
|
||||
puts stderr " $i. [lindex $Q [expr $i-1]]"
|
||||
}
|
||||
puts stderr ""
|
||||
exit -1
|
||||
}
|
||||
|
||||
|
||||
set nArg [llength $argv]
|
||||
if {$nArg!=2 && $nArg!=3} usage
|
||||
set database [lindex $argv 0]
|
||||
set iquery [lindex $argv 1]
|
||||
if {$iquery<1 || $iquery>[llength $Q]} usage
|
||||
set nRepeat 0
|
||||
if {$nArg==3} { set nRepeat [lindex $argv 2] }
|
||||
|
||||
|
||||
sqlite3 db $database
|
||||
catch { load_static_extension db fts5 }
|
||||
|
||||
incr iquery -1
|
||||
set sql [lindex $Q $iquery 1]
|
||||
if {$nRepeat==0} {
|
||||
set nRepeat [lindex $Q $iquery 0]
|
||||
}
|
||||
|
||||
puts "sql: $sql"
|
||||
puts "nRepeat: $nRepeat"
|
||||
if {[regexp matchinfo $sql]} {
|
||||
sqlite3_fts5_register_matchinfo db
|
||||
db eval $sql
|
||||
} else {
|
||||
puts "result: [db eval $sql]"
|
||||
}
|
||||
|
||||
for {set i 1} {$i < $nRepeat} {incr i} {
|
||||
db eval $sql
|
||||
}
|
||||
|
||||
|
78
manifest
78
manifest
@ -1,5 +1,5 @@
|
||||
C Add\sthe\s".vfslist"\scommand\sto\sthe\scommand-line\sshell.
|
||||
D 2016-01-12T19:37:20.761
|
||||
C Add\sthe\s"detail"\soption\sto\sfts5.\sUsed\sto\sreduce\sthe\samount\sof\sinformation\sstored\sin\san\sfts5\sindex.
|
||||
D 2016-01-12T19:45:01.185
|
||||
F Makefile.in 7c8cc4c2f0179efc6fa9492141d1fb65f4807054
|
||||
F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434
|
||||
F Makefile.msc e45d8b9b56dfa3f2cd860b2c28bd9d304513b042
|
||||
@ -96,39 +96,39 @@ F ext/fts3/unicode/UnicodeData.txt cd07314edb62d49fde34debdaf92fa2aa69011e7
|
||||
F ext/fts3/unicode/mkunicode.tcl 95cf7ec186e48d4985e433ff8a1c89090a774252
|
||||
F ext/fts3/unicode/parseunicode.tcl da577d1384810fb4e2b209bf3313074353193e95
|
||||
F ext/fts5/extract_api_docs.tcl a36e54ec777172ddd3f9a88daf593b00848368e0
|
||||
F ext/fts5/fts5.h 8b9a13b309b180e9fb88ea5666c0d8d73c6102d9
|
||||
F ext/fts5/fts5Int.h acf968e43d57b6b1caf7554d34ec35d6ed3b4fe8
|
||||
F ext/fts5/fts5.h 88fec577a2148f34df75930dc2b0c45b0bad72c3
|
||||
F ext/fts5/fts5Int.h 3918045ebceb1d600c9c1c1b460489ff0c788e96
|
||||
F ext/fts5/fts5_aux.c 2dafc3aee0c70d643140c77d8d70daffa51a9e9e
|
||||
F ext/fts5/fts5_buffer.c 1e49512a535045e621246dc7f4f65f3593fa0fc2
|
||||
F ext/fts5/fts5_config.c 0ee66188609a62342e9f9aeefa3c3e44518a4dd6
|
||||
F ext/fts5/fts5_expr.c 8228aca3e9af626a1ca9b093d4d24b4f2488ad23
|
||||
F ext/fts5/fts5_hash.c 25838d525e97f8662ff3504be94d0bad24f9a37e
|
||||
F ext/fts5/fts5_index.c 578f46697080f11a1e26cd45a1c039c043a3111d
|
||||
F ext/fts5/fts5_main.c e11b525778e6fce416d916bfa96926bd1ce4616d
|
||||
F ext/fts5/fts5_storage.c 57c636d87cbb829d6147c8c8bf78fa53b9ebb526
|
||||
F ext/fts5/fts5_tcl.c 3bf445e66de32137d4693694ff7b1fd6074e32bd
|
||||
F ext/fts5/fts5_test_mi.c e96be827aa8f571031e65e481251dc1981d608bf
|
||||
F ext/fts5/fts5_buffer.c 87204c8b3b8bc62b27376eab09b74d6d5acc41f1
|
||||
F ext/fts5/fts5_config.c b0ed7b0ddd785fb4d4e6f9037d357f8aa95918e6
|
||||
F ext/fts5/fts5_expr.c 6eba2220747ea1b20a358fb3b34b2ab78323e285
|
||||
F ext/fts5/fts5_hash.c 1b113977296cf4212c6ec667d5e3f2bd18036955
|
||||
F ext/fts5/fts5_index.c 5ab044a67919e2c9f42f0288a39778136154511e
|
||||
F ext/fts5/fts5_main.c 03bd44e4bd0ba16213ca9259ad5df1d4d743fd7e
|
||||
F ext/fts5/fts5_storage.c f7b2d330dd7b29a9f4da09f6d85879ca8c41b2e8
|
||||
F ext/fts5/fts5_tcl.c 18e9382d8cdad4c05b49559c68494968b9b4a4fb
|
||||
F ext/fts5/fts5_test_mi.c 1ec66ffdf7632077fbd773b7a6df5153272ec070
|
||||
F ext/fts5/fts5_tokenize.c 504984ac6993323247221eebe3cd55bead01b5f8
|
||||
F ext/fts5/fts5_unicode2.c 78273fbd588d1d9bd0a7e4e0ccc9207348bae33c
|
||||
F ext/fts5/fts5_varint.c 3f86ce09cab152e3d45490d7586b7ed2e40c13f1
|
||||
F ext/fts5/fts5_vocab.c 3742d0abfe8aa8c3cb4a7df56aa38f2e3c3fb1c2
|
||||
F ext/fts5/fts5_vocab.c da64ecbd217625980a1721fbd588a1e4118a51b6
|
||||
F ext/fts5/fts5parse.y 1647eba089b9b3fc058b4dc989d9da87d15b9580
|
||||
F ext/fts5/mkportersteps.tcl 5acf962d2e0074f701620bb5308155fa1e4a63ba
|
||||
F ext/fts5/test/fts5_common.tcl 51f7ef3af444b89c6f6ce3896a0ac349ff4e996d
|
||||
F ext/fts5/test/fts5aa.test 2c553eea4dab4bc5a75928f56729277c7bc1d206
|
||||
F ext/fts5/test/fts5ab.test 6fe3a56731d15978afbb74ae51b355fc9310f2ad
|
||||
F ext/fts5/test/fts5ac.test 9737992d08c56bfd4803e933744d2d764e23795c
|
||||
F ext/fts5/test/fts5ad.test e3dfb150fce971b4fd832498c29f56924d451b63
|
||||
F ext/fts5/test/fts5ae.test 0a9984fc3479f89f8c63d9848d6ed0c465dfcebe
|
||||
F ext/fts5/test/fts5af.test c2501ec2b61d6b179c305f5d2b8782ab3d4f832a
|
||||
F ext/fts5/test/fts5ag.test ec3e119b728196620a31507ef503c455a7a73505
|
||||
F ext/fts5/test/fts5ah.test e592c4978622dbc4de552cd0f9395df60ac5d54c
|
||||
F ext/fts5/test/fts5ai.test f20e53bbf0c55bc596f1fd47f2740dae028b8f37
|
||||
F ext/fts5/test/fts5_common.tcl 393882afb225a21edf033043bbf936951e9198c1
|
||||
F ext/fts5/test/fts5aa.test 7e814df4a0e6c22a6fe2d84f210fdc0b5068a084
|
||||
F ext/fts5/test/fts5ab.test 30325a89453280160106be411bba3acf138e6d1b
|
||||
F ext/fts5/test/fts5ac.test d5073ca7bd2d9fe8aab0c82c6c75a7e4b0d70ced
|
||||
F ext/fts5/test/fts5ad.test 0ddaa5b692ff220100ee396228838f4331399eaa
|
||||
F ext/fts5/test/fts5ae.test 612dcb51f4069226791ff14c17dbfb3138c56f20
|
||||
F ext/fts5/test/fts5af.test be858a96b1f5de66ba6d64f0021bd8b2408e126c
|
||||
F ext/fts5/test/fts5ag.test 27180de76c03036be75ee80b93d8c5f540014071
|
||||
F ext/fts5/test/fts5ah.test dfb7897711dbcda1dacb038aec310daca139fcf5
|
||||
F ext/fts5/test/fts5ai.test 3909d0b949b2afcaae4d5795cd79153da75381df
|
||||
F ext/fts5/test/fts5aj.test 05b569f5c16ea3098fb1984eec5cf50dbdaae5d8
|
||||
F ext/fts5/test/fts5ak.test 7b8c5df96df599293f920b7e5521ebc79f647592
|
||||
F ext/fts5/test/fts5al.test a1b7b6393376bc2adc216527a28f5ae5594069df
|
||||
F ext/fts5/test/fts5ak.test fb26389985407826f6076bb9f382c67d3db6b5d9
|
||||
F ext/fts5/test/fts5al.test 18c277f5986df0a3d9071dfd7128afeb16fe9d5d
|
||||
F ext/fts5/test/fts5alter.test 6022c61467a82aa11c70822ccad22b328dcf0d04
|
||||
F ext/fts5/test/fts5auto.test caa5bcf917db11944655a2a9bd38c67c520376ca
|
||||
F ext/fts5/test/fts5auto.test 401c20e89f1114d733b94809be1e6f893e16c09e
|
||||
F ext/fts5/test/fts5aux.test 8c687c948cc98e9a94be014df7d518acc1b3b74f
|
||||
F ext/fts5/test/fts5auxdata.test 141a7cbffcceb1bd2799b4b29c183ff8780d586e
|
||||
F ext/fts5/test/fts5bigpl.test 04ee0d7eebbebf17c31f5a0b5c5f9494eac3a0cb
|
||||
@ -139,10 +139,11 @@ F ext/fts5/test/fts5content.test 9a952c95518a14182dc3b59e3c8fa71cda82a4e1
|
||||
F ext/fts5/test/fts5corrupt.test c2ad090192708150d50d961278df10ae7a4b8b62
|
||||
F ext/fts5/test/fts5corrupt2.test 26c0a39dd9ff73207e6229f83b50b21d37c7658c
|
||||
F ext/fts5/test/fts5corrupt3.test a2b537c120bdd43c79c42fe2438d7b8c81fe5599
|
||||
F ext/fts5/test/fts5dlidx.test ecba5e62ea8b26c33829961602069c546228046d
|
||||
F ext/fts5/test/fts5detail.test 4e971d28e7336c61ab916fc287900355dab7054d
|
||||
F ext/fts5/test/fts5dlidx.test 13871a14641017ae42f6f1055a8067bafd44cb3d
|
||||
F ext/fts5/test/fts5doclist.test 8edb5b57e5f144030ed74ec00ef6fa4294fed79b
|
||||
F ext/fts5/test/fts5ea.test b01e3a18cdfabbff8104a96a5242a06a68a998a0
|
||||
F ext/fts5/test/fts5eb.test 3e5869af2008cbc4ad03a175a0b6f6e58134cd43
|
||||
F ext/fts5/test/fts5eb.test 021aa80b7ac09b964249aa32ced9ee908703e4aa
|
||||
F ext/fts5/test/fts5fault1.test 4b39c47ca3544615daa8a2f733b911fa08022c77
|
||||
F ext/fts5/test/fts5fault2.test 28c36c843bb39ae855ba79827417ecc37f114341
|
||||
F ext/fts5/test/fts5fault3.test d6e9577d4312e331a913c72931bf131704efc8f3
|
||||
@ -150,10 +151,11 @@ F ext/fts5/test/fts5fault4.test 4864f2b5c2c083440dbe85aff60897bc1aa04603
|
||||
F ext/fts5/test/fts5fault5.test f2b8645053d48982e8979749e93994c43011c118
|
||||
F ext/fts5/test/fts5fault6.test 9682664d679643ac6736e90c225526cc84073cda
|
||||
F ext/fts5/test/fts5fault7.test 01be274bfc8d9bf22451a3bf5892e9399d044f1b
|
||||
F ext/fts5/test/fts5fault8.test aeb4717b7b293678bc4d2f3c0159206a525375d9
|
||||
F ext/fts5/test/fts5full.test 6f6143af0c6700501d9fd597189dfab1555bb741
|
||||
F ext/fts5/test/fts5hash.test 7cf4607b8657c383f0b520668a99971e95d8b139
|
||||
F ext/fts5/test/fts5integrity.test 87db5d4e7da0ce04a1dcba5ba91658673c997a65
|
||||
F ext/fts5/test/fts5matchinfo.test 2163b0013e824bba65499da9e34ea4da41349cc2
|
||||
F ext/fts5/test/fts5matchinfo.test 86569026d20f1ed748236587ce798de8a96615f1
|
||||
F ext/fts5/test/fts5merge.test 8f3cdba2ec9c5e7e568246e81b700ad37f764367
|
||||
F ext/fts5/test/fts5near.test b214cddb1c1f1bddf45c75af768f20145f7e71cc
|
||||
F ext/fts5/test/fts5onepass.test 7ed9608e258132cb8d55e7c479b08676ad68810c
|
||||
@ -168,15 +170,18 @@ F ext/fts5/test/fts5rank.test 7e9e64eac7245637f6f2033aec4b292aaf611aab
|
||||
F ext/fts5/test/fts5rebuild.test 03935f617ace91ed23a6099c7c74d905227ff29b
|
||||
F ext/fts5/test/fts5restart.test c17728fdea26e7d0f617d22ad5b4b2862b994c17
|
||||
F ext/fts5/test/fts5rowid.test 400384798349d658eaf06aefa1e364957d5d4821
|
||||
F ext/fts5/test/fts5simple.test 9bded45827b4ab8933c87b7b3bcc3cd47f7378a4
|
||||
F ext/fts5/test/fts5simple.test 2bc6451cbe887a9215f5b14ae307c70d850344c9
|
||||
F ext/fts5/test/fts5simple2.test 843f1f7fe439ff32bf74f4fd6430632f9636ef3a
|
||||
F ext/fts5/test/fts5synonym.test cf88c0a56d5ea9591e3939ef1f6e294f7f2d0671
|
||||
F ext/fts5/test/fts5synonym2.test d2d9099d9d105b55ea03fd52d61ae2847d534129
|
||||
F ext/fts5/test/fts5tokenizer.test ea4df698b35cc427ebf2ba22829d0e28386d8c89
|
||||
F ext/fts5/test/fts5unicode.test fbef8d8a3b4b88470536cc57604a82ca52e51841
|
||||
F ext/fts5/test/fts5unicode2.test c1dd890ba32b7609adba78e420faa847abe43b59
|
||||
F ext/fts5/test/fts5unicode3.test 35c3d02aa7acf7d43d8de3bfe32c15ba96e8928e
|
||||
F ext/fts5/test/fts5unindexed.test e9539d5b78c677315e7ed8ea911d4fd25437c680
|
||||
F ext/fts5/test/fts5version.test 978f59541d8cef7e8591f8be2115ec5ccb863e2e
|
||||
F ext/fts5/test/fts5vocab.test c88a5554d0409494da95ba647bbdb4879b2624b0
|
||||
F ext/fts5/test/fts5vocab.test e4b12f238f113795615ba6343b63fb326d6a360e
|
||||
F ext/fts5/tool/fts5speed.tcl aaee41894b552df8fbf8616aad003b2ea9ba3221
|
||||
F ext/fts5/tool/fts5txt2db.tcl c374c4c4797e8cdfadabdfaeeb5412dcd6686e84
|
||||
F ext/fts5/tool/loadfts5.tcl 4cc2d6af43b58d4fac05bc4fdabd0e5862c3b2c1
|
||||
F ext/fts5/tool/mkfts5c.tcl d1c2a9ab8e0ec690a52316f33dd9b1d379942f45
|
||||
@ -1407,7 +1412,8 @@ F tool/vdbe_profile.tcl 246d0da094856d72d2c12efec03250d71639d19f
|
||||
F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4
|
||||
F tool/warnings.sh 48bd54594752d5be3337f12c72f28d2080cb630b
|
||||
F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f
|
||||
P 4bb2c1df105c2d21f5c4c7ef656ff1d6e15f78bc
|
||||
R 74300fef6ffb708c0011be0eb0839258
|
||||
U drh
|
||||
Z 0c47ae55d5f3f2500ab8561940e3da5a
|
||||
P 5727562b75edf25102cd72607c420d245379c96d bc5118f40a11f64ffb4e1c086277fa80b9764745
|
||||
R 32b6994fdd76448ebd10466a9fb05893
|
||||
T +closed bc5118f40a11f64ffb4e1c086277fa80b9764745
|
||||
U dan
|
||||
Z d6aecf5b74614ca62cb2aeb2f23739db
|
||||
|
@ -1 +1 @@
|
||||
5727562b75edf25102cd72607c420d245379c96d
|
||||
a220e85fe535af5ef2da6ef5fb76abe5a96b5abf
|
Loading…
x
Reference in New Issue
Block a user