| 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;
|
| }
|
|
|
|
|