Index: third_party/sqlite/src/ext/fts5/fts5_main.c |
diff --git a/third_party/sqlite/src/ext/fts5/fts5_main.c b/third_party/sqlite/src/ext/fts5/fts5_main.c |
index 70912de4f8da4d1bdfdd379b2b49752583e1226a..384d3dd8f72ccc8f3f3cf91855aeccbcb7976997 100644 |
--- a/third_party/sqlite/src/ext/fts5/fts5_main.c |
+++ b/third_party/sqlite/src/ext/fts5/fts5_main.c |
@@ -220,12 +220,13 @@ struct Fts5Cursor { |
/* |
** Values for Fts5Cursor.csrflags |
*/ |
-#define FTS5CSR_REQUIRE_CONTENT 0x01 |
-#define FTS5CSR_REQUIRE_DOCSIZE 0x02 |
-#define FTS5CSR_REQUIRE_INST 0x04 |
-#define FTS5CSR_EOF 0x08 |
+#define FTS5CSR_EOF 0x01 |
+#define FTS5CSR_REQUIRE_CONTENT 0x02 |
+#define FTS5CSR_REQUIRE_DOCSIZE 0x04 |
+#define FTS5CSR_REQUIRE_INST 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) |
@@ -537,7 +538,7 @@ static int fts5BestIndexMethod(sqlite3_vtab *pVTab, sqlite3_index_info *pInfo){ |
for(i=0; i<pInfo->nConstraint; i++){ |
struct sqlite3_index_constraint *p = &pInfo->aConstraint[i]; |
int j; |
- for(j=0; j<(int)ArraySize(aConstraint); j++){ |
+ for(j=0; j<ArraySize(aConstraint); j++){ |
struct Constraint *pC = &aConstraint[j]; |
if( p->iColumn==aColMap[pC->iCol] && p->op & pC->op ){ |
if( p->usable ){ |
@@ -584,7 +585,7 @@ static int fts5BestIndexMethod(sqlite3_vtab *pVTab, sqlite3_index_info *pInfo){ |
/* Assign argvIndex values to each constraint in use. */ |
iNext = 1; |
- for(i=0; i<(int)ArraySize(aConstraint); i++){ |
+ for(i=0; i<ArraySize(aConstraint); i++){ |
struct Constraint *pC = &aConstraint[i]; |
if( pC->iConsIndex>=0 ){ |
pInfo->aConstraintUsage[pC->iConsIndex].argvIndex = iNext++; |
@@ -596,27 +597,38 @@ static int fts5BestIndexMethod(sqlite3_vtab *pVTab, sqlite3_index_info *pInfo){ |
return SQLITE_OK; |
} |
+static int fts5NewTransaction(Fts5Table *pTab){ |
+ Fts5Cursor *pCsr; |
+ for(pCsr=pTab->pGlobal->pCsr; pCsr; pCsr=pCsr->pNext){ |
+ if( pCsr->base.pVtab==(sqlite3_vtab*)pTab ) return SQLITE_OK; |
+ } |
+ return sqlite3Fts5StorageReset(pTab->pStorage); |
+} |
+ |
/* |
** Implementation of xOpen method. |
*/ |
static int fts5OpenMethod(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCsr){ |
Fts5Table *pTab = (Fts5Table*)pVTab; |
Fts5Config *pConfig = pTab->pConfig; |
- Fts5Cursor *pCsr; /* New cursor object */ |
+ Fts5Cursor *pCsr = 0; /* New cursor object */ |
int nByte; /* Bytes of space to allocate */ |
- int rc = SQLITE_OK; /* Return code */ |
+ int rc; /* Return code */ |
- nByte = sizeof(Fts5Cursor) + pConfig->nCol * sizeof(int); |
- pCsr = (Fts5Cursor*)sqlite3_malloc(nByte); |
- if( pCsr ){ |
- Fts5Global *pGlobal = pTab->pGlobal; |
- memset(pCsr, 0, nByte); |
- pCsr->aColumnSize = (int*)&pCsr[1]; |
- pCsr->pNext = pGlobal->pCsr; |
- pGlobal->pCsr = pCsr; |
- pCsr->iCsrId = ++pGlobal->iNextId; |
- }else{ |
- rc = SQLITE_NOMEM; |
+ rc = fts5NewTransaction(pTab); |
+ if( rc==SQLITE_OK ){ |
+ nByte = sizeof(Fts5Cursor) + pConfig->nCol * sizeof(int); |
+ pCsr = (Fts5Cursor*)sqlite3_malloc(nByte); |
+ if( pCsr ){ |
+ Fts5Global *pGlobal = pTab->pGlobal; |
+ memset(pCsr, 0, nByte); |
+ pCsr->aColumnSize = (int*)&pCsr[1]; |
+ pCsr->pNext = pGlobal->pCsr; |
+ pGlobal->pCsr = pCsr; |
+ pCsr->iCsrId = ++pGlobal->iNextId; |
+ }else{ |
+ rc = SQLITE_NOMEM; |
+ } |
} |
*ppCsr = (sqlite3_vtab_cursor*)pCsr; |
return rc; |
@@ -639,6 +651,7 @@ static void fts5CsrNewrow(Fts5Cursor *pCsr){ |
FTS5CSR_REQUIRE_CONTENT |
| FTS5CSR_REQUIRE_DOCSIZE |
| FTS5CSR_REQUIRE_INST |
+ | FTS5CSR_REQUIRE_POSLIST |
); |
} |
@@ -721,15 +734,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); |
} |
@@ -773,7 +789,7 @@ static int fts5CursorReseek(Fts5Cursor *pCsr, int *pbSkip){ |
i64 iRowid = sqlite3Fts5ExprRowid(pCsr->pExpr); |
rc = sqlite3Fts5ExprFirst(pCsr->pExpr, pTab->pIndex, iRowid, bDesc); |
- if( rc==SQLITE_OK && iRowid!=sqlite3Fts5ExprRowid(pCsr->pExpr) ){ |
+ if( rc==SQLITE_OK && iRowid!=sqlite3Fts5ExprRowid(pCsr->pExpr) ){ |
*pbSkip = 1; |
} |
@@ -781,6 +797,7 @@ static int fts5CursorReseek(Fts5Cursor *pCsr, int *pbSkip){ |
fts5CsrNewrow(pCsr); |
if( sqlite3Fts5ExprEof(pCsr->pExpr) ){ |
CsrFlagSet(pCsr, FTS5CSR_EOF); |
+ *pbSkip = 1; |
} |
} |
return rc; |
@@ -797,24 +814,24 @@ static int fts5CursorReseek(Fts5Cursor *pCsr, int *pbSkip){ |
*/ |
static int fts5NextMethod(sqlite3_vtab_cursor *pCursor){ |
Fts5Cursor *pCsr = (Fts5Cursor*)pCursor; |
- int rc = SQLITE_OK; |
+ int rc; |
assert( (pCsr->ePlan<3)== |
(pCsr->ePlan==FTS5_PLAN_MATCH || pCsr->ePlan==FTS5_PLAN_SOURCE) |
); |
+ assert( !CsrFlagTest(pCsr, FTS5CSR_EOF) ); |
if( pCsr->ePlan<3 ){ |
int bSkip = 0; |
if( (rc = fts5CursorReseek(pCsr, &bSkip)) || bSkip ) return rc; |
rc = sqlite3Fts5ExprNext(pCsr->pExpr, pCsr->iLastRowid); |
- if( sqlite3Fts5ExprEof(pCsr->pExpr) ){ |
- CsrFlagSet(pCsr, FTS5CSR_EOF); |
- } |
+ CsrFlagSet(pCsr, sqlite3Fts5ExprEof(pCsr->pExpr)); |
fts5CsrNewrow(pCsr); |
}else{ |
switch( pCsr->ePlan ){ |
case FTS5_PLAN_SPECIAL: { |
CsrFlagSet(pCsr, FTS5CSR_EOF); |
+ rc = SQLITE_OK; |
break; |
} |
@@ -839,33 +856,32 @@ static int fts5NextMethod(sqlite3_vtab_cursor *pCursor){ |
} |
-static sqlite3_stmt *fts5PrepareStatement( |
- int *pRc, |
+static int fts5PrepareStatement( |
+ sqlite3_stmt **ppStmt, |
Fts5Config *pConfig, |
const char *zFmt, |
... |
){ |
sqlite3_stmt *pRet = 0; |
+ int rc; |
+ char *zSql; |
va_list ap; |
- va_start(ap, zFmt); |
- if( *pRc==SQLITE_OK ){ |
- int rc; |
- char *zSql = sqlite3_vmprintf(zFmt, ap); |
- if( zSql==0 ){ |
- rc = SQLITE_NOMEM; |
- }else{ |
- rc = sqlite3_prepare_v2(pConfig->db, zSql, -1, &pRet, 0); |
- if( rc!=SQLITE_OK ){ |
- *pConfig->pzErrmsg = sqlite3_mprintf("%s", sqlite3_errmsg(pConfig->db)); |
- } |
- sqlite3_free(zSql); |
+ va_start(ap, zFmt); |
+ zSql = sqlite3_vmprintf(zFmt, ap); |
+ if( zSql==0 ){ |
+ rc = SQLITE_NOMEM; |
+ }else{ |
+ rc = sqlite3_prepare_v2(pConfig->db, zSql, -1, &pRet, 0); |
+ if( rc!=SQLITE_OK ){ |
+ *pConfig->pzErrmsg = sqlite3_mprintf("%s", sqlite3_errmsg(pConfig->db)); |
} |
- *pRc = rc; |
+ sqlite3_free(zSql); |
} |
va_end(ap); |
- return pRet; |
+ *ppStmt = pRet; |
+ return rc; |
} |
static int fts5CursorFirstSorted(Fts5Table *pTab, Fts5Cursor *pCsr, int bDesc){ |
@@ -873,7 +889,7 @@ static int fts5CursorFirstSorted(Fts5Table *pTab, Fts5Cursor *pCsr, int bDesc){ |
Fts5Sorter *pSorter; |
int nPhrase; |
int nByte; |
- int rc = SQLITE_OK; |
+ int rc; |
const char *zRank = pCsr->zRank; |
const char *zRankArgs = pCsr->zRankArgs; |
@@ -891,7 +907,7 @@ static int fts5CursorFirstSorted(Fts5Table *pTab, Fts5Cursor *pCsr, int bDesc){ |
** table, saving it creates a circular reference. |
** |
** If SQLite a built-in statement cache, this wouldn't be a problem. */ |
- pSorter->pStmt = fts5PrepareStatement(&rc, pConfig, |
+ rc = fts5PrepareStatement(&pSorter->pStmt, pConfig, |
"SELECT rowid, rank FROM %Q.%Q ORDER BY %s(%s%s%s) %s", |
pConfig->zDb, pConfig->zName, zRank, pConfig->zName, |
(zRankArgs ? ", " : ""), |
@@ -1091,7 +1107,7 @@ static i64 fts5GetRowidLimit(sqlite3_value *pVal, i64 iDefault){ |
static int fts5FilterMethod( |
sqlite3_vtab_cursor *pCursor, /* The cursor used for this query */ |
int idxNum, /* Strategy index */ |
- const char *idxStr, /* Unused */ |
+ const char *zUnused, /* Unused */ |
int nVal, /* Number of elements in apVal */ |
sqlite3_value **apVal /* Arguments for the indexing scheme */ |
){ |
@@ -1109,6 +1125,9 @@ static int fts5FilterMethod( |
sqlite3_value *pRowidGe = 0; /* rowid >= ? expression (or NULL) */ |
char **pzErrmsg = pConfig->pzErrmsg; |
+ UNUSED_PARAM(zUnused); |
+ UNUSED_PARAM(nVal); |
+ |
if( pCsr->ePlan ){ |
fts5FreeCursorComponents(pCsr); |
memset(&pCsr->ePlan, 0, sizeof(Fts5Cursor) - ((u8*)&pCsr->ePlan-(u8*)pCsr)); |
@@ -1392,14 +1411,13 @@ static int fts5SpecialInsert( |
static int fts5SpecialDelete( |
Fts5Table *pTab, |
- sqlite3_value **apVal, |
- sqlite3_int64 *piRowid |
+ sqlite3_value **apVal |
){ |
int rc = SQLITE_OK; |
int eType1 = sqlite3_value_type(apVal[1]); |
if( eType1==SQLITE_INTEGER ){ |
sqlite3_int64 iDel = sqlite3_value_int64(apVal[1]); |
- rc = sqlite3Fts5StorageSpecialDelete(pTab->pStorage, iDel, &apVal[2]); |
+ rc = sqlite3Fts5StorageDelete(pTab->pStorage, iDel, &apVal[2]); |
} |
return rc; |
} |
@@ -1469,7 +1487,7 @@ static int fts5UpdateMethod( |
if( pConfig->eContent!=FTS5_CONTENT_NORMAL |
&& 0==sqlite3_stricmp("delete", z) |
){ |
- rc = fts5SpecialDelete(pTab, apVal, pRowid); |
+ rc = fts5SpecialDelete(pTab, apVal); |
}else{ |
rc = fts5SpecialInsert(pTab, z, apVal[2 + pConfig->nCol + 1]); |
} |
@@ -1503,46 +1521,46 @@ static int fts5UpdateMethod( |
rc = SQLITE_ERROR; |
} |
- /* Case 1: DELETE */ |
+ /* DELETE */ |
else if( nArg==1 ){ |
i64 iDel = sqlite3_value_int64(apVal[0]); /* Rowid to delete */ |
- rc = sqlite3Fts5StorageDelete(pTab->pStorage, iDel); |
+ rc = sqlite3Fts5StorageDelete(pTab->pStorage, iDel, 0); |
} |
- /* Case 2: INSERT */ |
+ /* INSERT */ |
else if( eType0!=SQLITE_INTEGER ){ |
/* If this is a REPLACE, first remove the current entry (if any) */ |
if( eConflict==SQLITE_REPLACE |
&& sqlite3_value_type(apVal[1])==SQLITE_INTEGER |
){ |
i64 iNew = sqlite3_value_int64(apVal[1]); /* Rowid to delete */ |
- rc = sqlite3Fts5StorageDelete(pTab->pStorage, iNew); |
+ rc = sqlite3Fts5StorageDelete(pTab->pStorage, iNew, 0); |
} |
fts5StorageInsert(&rc, pTab, apVal, pRowid); |
} |
- /* Case 2: UPDATE */ |
+ /* UPDATE */ |
else{ |
i64 iOld = sqlite3_value_int64(apVal[0]); /* Old rowid */ |
i64 iNew = sqlite3_value_int64(apVal[1]); /* New rowid */ |
if( iOld!=iNew ){ |
if( eConflict==SQLITE_REPLACE ){ |
- rc = sqlite3Fts5StorageDelete(pTab->pStorage, iOld); |
+ rc = sqlite3Fts5StorageDelete(pTab->pStorage, iOld, 0); |
if( rc==SQLITE_OK ){ |
- rc = sqlite3Fts5StorageDelete(pTab->pStorage, iNew); |
+ rc = sqlite3Fts5StorageDelete(pTab->pStorage, iNew, 0); |
} |
fts5StorageInsert(&rc, pTab, apVal, pRowid); |
}else{ |
rc = sqlite3Fts5StorageContentInsert(pTab->pStorage, apVal, pRowid); |
if( rc==SQLITE_OK ){ |
- rc = sqlite3Fts5StorageDelete(pTab->pStorage, iOld); |
+ rc = sqlite3Fts5StorageDelete(pTab->pStorage, iOld, 0); |
} |
if( rc==SQLITE_OK ){ |
rc = sqlite3Fts5StorageIndexInsert(pTab->pStorage, apVal, *pRowid); |
} |
} |
}else{ |
- rc = sqlite3Fts5StorageDelete(pTab->pStorage, iOld); |
+ rc = sqlite3Fts5StorageDelete(pTab->pStorage, iOld, 0); |
fts5StorageInsert(&rc, pTab, apVal, pRowid); |
} |
} |
@@ -1571,6 +1589,7 @@ static int fts5SyncMethod(sqlite3_vtab *pVtab){ |
*/ |
static int fts5BeginMethod(sqlite3_vtab *pVtab){ |
fts5CheckTransactionState((Fts5Table*)pVtab, FTS5_BEGIN, 0); |
+ fts5NewTransaction((Fts5Table*)pVtab); |
return SQLITE_OK; |
} |
@@ -1580,6 +1599,7 @@ static int fts5BeginMethod(sqlite3_vtab *pVtab){ |
** by fts5SyncMethod(). |
*/ |
static int fts5CommitMethod(sqlite3_vtab *pVtab){ |
+ UNUSED_PARAM(pVtab); /* Call below is a no-op for NDEBUG builds */ |
fts5CheckTransactionState((Fts5Table*)pVtab, FTS5_COMMIT, 0); |
return SQLITE_OK; |
} |
@@ -1596,6 +1616,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 +1667,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 +1757,48 @@ 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); |
- sqlite3Fts5PoslistReaderInit(a, n, &aIter[i]); |
+ int n; |
+ rc = fts5CsrPoslist(pCsr, i, &a, &n); |
+ if( rc==SQLITE_OK ){ |
+ 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; |
- |
- 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; |
+ 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; |
+ } |
} |
- } |
- 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 +1831,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,36 +1850,17 @@ 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, |
- const char *pToken, /* Buffer containing token */ |
- int nToken, /* Size of token in bytes */ |
- int iStart, /* Start offset of token */ |
- int iEnd /* End offset of token */ |
+ const char *pUnused, /* Buffer containing token */ |
+ int nUnused, /* Size of token in bytes */ |
+ int iUnused1, /* Start offset of token */ |
+ int iUnused2 /* End offset of token */ |
){ |
int *pCnt = (int*)pContext; |
+ UNUSED_PARAM2(pUnused, nUnused); |
+ UNUSED_PARAM2(iUnused1, iUnused2); |
if( (tflags & FTS5_TOKEN_COLOCATED)==0 ){ |
(*pCnt)++; |
} |
@@ -1907,10 +1976,11 @@ static void *fts5ApiGetAuxdata(Fts5Context *pCtx, int bClear){ |
} |
static void fts5ApiPhraseNext( |
- Fts5Context *pCtx, |
+ Fts5Context *pUnused, |
Fts5PhraseIter *pIter, |
int *piCol, int *piOff |
){ |
+ UNUSED_PARAM(pUnused); |
if( pIter->a>=pIter->b ){ |
*piCol = -1; |
*piOff = -1; |
@@ -1927,20 +1997,98 @@ 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 ){ |
+ Fts5Sorter *pSorter = pCsr->pSorter; |
+ int n; |
+ if( pSorter ){ |
+ int i1 = (iPhrase==0 ? 0 : pSorter->aIdx[iPhrase-1]); |
+ n = pSorter->aIdx[iPhrase] - i1; |
+ pIter->a = &pSorter->aPoslist[i1]; |
+ }else{ |
+ 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 +2112,10 @@ static const Fts5ExtensionApi sFts5Api = { |
fts5ApiGetAuxdata, |
fts5ApiPhraseFirst, |
fts5ApiPhraseNext, |
+ fts5ApiPhraseFirstColumn, |
+ fts5ApiPhraseNextColumn, |
}; |
- |
/* |
** Implementation of API function xQueryPhrase(). |
*/ |
@@ -1983,12 +2132,11 @@ static int fts5ApiQueryPhrase( |
rc = fts5OpenMethod(pCsr->base.pVtab, (sqlite3_vtab_cursor**)&pNew); |
if( rc==SQLITE_OK ){ |
- Fts5Config *pConf = pTab->pConfig; |
pNew->ePlan = FTS5_PLAN_MATCH; |
pNew->iFirstRowid = SMALLEST_INT64; |
pNew->iLastRowid = LARGEST_INT64; |
pNew->base.pVtab = (sqlite3_vtab*)pTab; |
- rc = sqlite3Fts5ExprClonePhrase(pConf, pCsr->pExpr, iPhrase, &pNew->pExpr); |
+ rc = sqlite3Fts5ExprClonePhrase(pCsr->pExpr, iPhrase, &pNew->pExpr); |
} |
if( rc==SQLITE_OK ){ |
@@ -2098,20 +2246,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); |
+ } |
+ 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; 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; 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); |
@@ -2175,7 +2349,7 @@ static int fts5ColumnMethod( |
*/ |
static int fts5FindFunctionMethod( |
sqlite3_vtab *pVtab, /* Virtual table handle */ |
- int nArg, /* Number of SQL function arguments */ |
+ int nUnused, /* Number of SQL function arguments */ |
const char *zName, /* Name of SQL function */ |
void (**pxFunc)(sqlite3_context*,int,sqlite3_value**), /* OUT: Result */ |
void **ppArg /* OUT: User data for *pxFunc */ |
@@ -2183,6 +2357,7 @@ static int fts5FindFunctionMethod( |
Fts5Table *pTab = (Fts5Table*)pVtab; |
Fts5Auxiliary *pAux; |
+ UNUSED_PARAM(nUnused); |
pAux = fts5FindAuxiliary(pTab, zName); |
if( pAux ){ |
*pxFunc = fts5ApiCallback; |
@@ -2212,6 +2387,7 @@ static int fts5RenameMethod( |
*/ |
static int fts5SavepointMethod(sqlite3_vtab *pVtab, int iSavepoint){ |
Fts5Table *pTab = (Fts5Table*)pVtab; |
+ UNUSED_PARAM(iSavepoint); /* Call below is a no-op for NDEBUG builds */ |
fts5CheckTransactionState(pTab, FTS5_SAVEPOINT, iSavepoint); |
fts5TripCursors(pTab); |
return sqlite3Fts5StorageSync(pTab->pStorage, 0); |
@@ -2224,6 +2400,7 @@ static int fts5SavepointMethod(sqlite3_vtab *pVtab, int iSavepoint){ |
*/ |
static int fts5ReleaseMethod(sqlite3_vtab *pVtab, int iSavepoint){ |
Fts5Table *pTab = (Fts5Table*)pVtab; |
+ UNUSED_PARAM(iSavepoint); /* Call below is a no-op for NDEBUG builds */ |
fts5CheckTransactionState(pTab, FTS5_RELEASE, iSavepoint); |
fts5TripCursors(pTab); |
return sqlite3Fts5StorageSync(pTab->pStorage, 0); |
@@ -2236,6 +2413,7 @@ static int fts5ReleaseMethod(sqlite3_vtab *pVtab, int iSavepoint){ |
*/ |
static int fts5RollbackToMethod(sqlite3_vtab *pVtab, int iSavepoint){ |
Fts5Table *pTab = (Fts5Table*)pVtab; |
+ UNUSED_PARAM(iSavepoint); /* Call below is a no-op for NDEBUG builds */ |
fts5CheckTransactionState(pTab, FTS5_ROLLBACKTO, iSavepoint); |
fts5TripCursors(pTab); |
return sqlite3Fts5StorageRollback(pTab->pStorage); |
@@ -2415,10 +2593,11 @@ static void fts5ModuleDestroy(void *pCtx){ |
static void fts5Fts5Func( |
sqlite3_context *pCtx, /* Function call context */ |
int nArg, /* Number of args */ |
- sqlite3_value **apVal /* Function arguments */ |
+ sqlite3_value **apUnused /* Function arguments */ |
){ |
Fts5Global *pGlobal = (Fts5Global*)sqlite3_user_data(pCtx); |
char buf[8]; |
+ UNUSED_PARAM2(nArg, apUnused); |
assert( nArg==0 ); |
assert( sizeof(buf)>=sizeof(pGlobal) ); |
memcpy(buf, (void*)&pGlobal, sizeof(pGlobal)); |
@@ -2431,9 +2610,10 @@ static void fts5Fts5Func( |
static void fts5SourceIdFunc( |
sqlite3_context *pCtx, /* Function call context */ |
int nArg, /* Number of args */ |
- sqlite3_value **apVal /* Function arguments */ |
+ sqlite3_value **apUnused /* Function arguments */ |
){ |
assert( nArg==0 ); |
+ UNUSED_PARAM2(nArg, apUnused); |
sqlite3_result_text(pCtx, "--FTS5-SOURCE-ID--", -1, SQLITE_TRANSIENT); |
} |
@@ -2495,6 +2675,17 @@ static int fts5Init(sqlite3 *db){ |
); |
} |
} |
+ |
+ /* If SQLITE_FTS5_ENABLE_TEST_MI is defined, assume that the file |
+ ** fts5_test_mi.c is compiled and linked into the executable. And call |
+ ** its entry point to enable the matchinfo() demo. */ |
+#ifdef SQLITE_FTS5_ENABLE_TEST_MI |
+ if( rc==SQLITE_OK ){ |
+ extern int sqlite3Fts5TestRegisterMatchinfo(sqlite3*); |
+ rc = sqlite3Fts5TestRegisterMatchinfo(db); |
+ } |
+#endif |
+ |
return rc; |
} |