| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
|
|
| #include "fts5Int.h" |
|
|
|
|
| typedef struct Fts5VocabTable Fts5VocabTable; |
| typedef struct Fts5VocabCursor Fts5VocabCursor; |
|
|
| struct Fts5VocabTable { |
| sqlite3_vtab base; |
| char *zFts5Tbl; |
| char *zFts5Db; |
| sqlite3 *db; |
| Fts5Global *pGlobal; |
| int eType; |
| unsigned bBusy; |
| }; |
|
|
| struct Fts5VocabCursor { |
| sqlite3_vtab_cursor base; |
| sqlite3_stmt *pStmt; |
| Fts5Table *pFts5; |
|
|
| int bEof; |
| Fts5IndexIter *pIter; |
| void *pStruct; |
|
|
| int nLeTerm; |
| char *zLeTerm; |
| int colUsed; |
|
|
| |
| int iCol; |
| i64 *aCnt; |
| i64 *aDoc; |
|
|
| |
| i64 rowid; |
| Fts5Buffer term; |
|
|
| |
| i64 iInstPos; |
| int iInstOff; |
| }; |
|
|
| #define FTS5_VOCAB_COL 0 |
| #define FTS5_VOCAB_ROW 1 |
| #define FTS5_VOCAB_INSTANCE 2 |
|
|
| #define FTS5_VOCAB_COL_SCHEMA "term, col, doc, cnt" |
| #define FTS5_VOCAB_ROW_SCHEMA "term, doc, cnt" |
| #define FTS5_VOCAB_INST_SCHEMA "term, doc, col, offset" |
|
|
| |
| |
| |
| #define FTS5_VOCAB_TERM_EQ 0x0100 |
| #define FTS5_VOCAB_TERM_GE 0x0200 |
| #define FTS5_VOCAB_TERM_LE 0x0400 |
|
|
| #define FTS5_VOCAB_COLUSED_MASK 0xFF |
|
|
|
|
| |
| |
| |
| |
| |
| |
| static int fts5VocabTableType(const char *zType, char **pzErr, int *peType){ |
| int rc = SQLITE_OK; |
| char *zCopy = sqlite3Fts5Strndup(&rc, zType, -1); |
| if( rc==SQLITE_OK ){ |
| sqlite3Fts5Dequote(zCopy); |
| if( sqlite3_stricmp(zCopy, "col")==0 ){ |
| *peType = FTS5_VOCAB_COL; |
| }else |
|
|
| if( sqlite3_stricmp(zCopy, "row")==0 ){ |
| *peType = FTS5_VOCAB_ROW; |
| }else |
| if( sqlite3_stricmp(zCopy, "instance")==0 ){ |
| *peType = FTS5_VOCAB_INSTANCE; |
| }else |
| { |
| *pzErr = sqlite3_mprintf("fts5vocab: unknown table type: %Q", zCopy); |
| rc = SQLITE_ERROR; |
| } |
| sqlite3_free(zCopy); |
| } |
|
|
| return rc; |
| } |
|
|
|
|
| |
| |
| |
| static int fts5VocabDisconnectMethod(sqlite3_vtab *pVtab){ |
| Fts5VocabTable *pTab = (Fts5VocabTable*)pVtab; |
| sqlite3_free(pTab); |
| return SQLITE_OK; |
| } |
|
|
| |
| |
| |
| static int fts5VocabDestroyMethod(sqlite3_vtab *pVtab){ |
| Fts5VocabTable *pTab = (Fts5VocabTable*)pVtab; |
| sqlite3_free(pTab); |
| return SQLITE_OK; |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| static int fts5VocabInitVtab( |
| sqlite3 *db, |
| void *pAux, |
| int argc, |
| const char * const *argv, |
| sqlite3_vtab **ppVTab, |
| char **pzErr |
| ){ |
| const char *azSchema[] = { |
| "CREATE TABlE vocab(" FTS5_VOCAB_COL_SCHEMA ")", |
| "CREATE TABlE vocab(" FTS5_VOCAB_ROW_SCHEMA ")", |
| "CREATE TABlE vocab(" FTS5_VOCAB_INST_SCHEMA ")" |
| }; |
|
|
| Fts5VocabTable *pRet = 0; |
| int rc = SQLITE_OK; |
| int bDb; |
|
|
| bDb = (argc==6 && strlen(argv[1])==4 && memcmp("temp", argv[1], 4)==0); |
|
|
| if( argc!=5 && bDb==0 ){ |
| *pzErr = sqlite3_mprintf("wrong number of vtable arguments"); |
| rc = SQLITE_ERROR; |
| }else{ |
| i64 nByte; |
| const char *zDb = bDb ? argv[3] : argv[1]; |
| const char *zTab = bDb ? argv[4] : argv[3]; |
| const char *zType = bDb ? argv[5] : argv[4]; |
| i64 nDb = strlen(zDb)+1; |
| i64 nTab = strlen(zTab)+1; |
| int eType = 0; |
| |
| rc = fts5VocabTableType(zType, pzErr, &eType); |
| if( rc==SQLITE_OK ){ |
| assert( eType>=0 && eType<ArraySize(azSchema) ); |
| rc = sqlite3_declare_vtab(db, azSchema[eType]); |
| } |
|
|
| nByte = sizeof(Fts5VocabTable) + nDb + nTab; |
| pRet = sqlite3Fts5MallocZero(&rc, nByte); |
| if( pRet ){ |
| pRet->pGlobal = (Fts5Global*)pAux; |
| pRet->eType = eType; |
| pRet->db = db; |
| pRet->zFts5Tbl = (char*)&pRet[1]; |
| pRet->zFts5Db = &pRet->zFts5Tbl[nTab]; |
| memcpy(pRet->zFts5Tbl, zTab, nTab); |
| memcpy(pRet->zFts5Db, zDb, nDb); |
| sqlite3Fts5Dequote(pRet->zFts5Tbl); |
| sqlite3Fts5Dequote(pRet->zFts5Db); |
| } |
| } |
|
|
| *ppVTab = (sqlite3_vtab*)pRet; |
| return rc; |
| } |
|
|
|
|
| |
| |
| |
| |
| static int fts5VocabConnectMethod( |
| sqlite3 *db, |
| void *pAux, |
| int argc, |
| const char * const *argv, |
| sqlite3_vtab **ppVtab, |
| char **pzErr |
| ){ |
| return fts5VocabInitVtab(db, pAux, argc, argv, ppVtab, pzErr); |
| } |
| static int fts5VocabCreateMethod( |
| sqlite3 *db, |
| void *pAux, |
| int argc, |
| const char * const *argv, |
| sqlite3_vtab **ppVtab, |
| char **pzErr |
| ){ |
| return fts5VocabInitVtab(db, pAux, argc, argv, ppVtab, pzErr); |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| static int fts5VocabBestIndexMethod( |
| sqlite3_vtab *pUnused, |
| sqlite3_index_info *pInfo |
| ){ |
| int i; |
| int iTermEq = -1; |
| int iTermGe = -1; |
| int iTermLe = -1; |
| int idxNum = (int)pInfo->colUsed; |
| int nArg = 0; |
|
|
| UNUSED_PARAM(pUnused); |
|
|
| assert( (pInfo->colUsed & FTS5_VOCAB_COLUSED_MASK)==pInfo->colUsed ); |
|
|
| for(i=0; i<pInfo->nConstraint; i++){ |
| struct sqlite3_index_constraint *p = &pInfo->aConstraint[i]; |
| if( p->usable==0 ) continue; |
| if( p->iColumn==0 ){ |
| if( p->op==SQLITE_INDEX_CONSTRAINT_EQ ) iTermEq = i; |
| if( p->op==SQLITE_INDEX_CONSTRAINT_LE ) iTermLe = i; |
| if( p->op==SQLITE_INDEX_CONSTRAINT_LT ) iTermLe = i; |
| if( p->op==SQLITE_INDEX_CONSTRAINT_GE ) iTermGe = i; |
| if( p->op==SQLITE_INDEX_CONSTRAINT_GT ) iTermGe = i; |
| } |
| } |
|
|
| if( iTermEq>=0 ){ |
| idxNum |= FTS5_VOCAB_TERM_EQ; |
| pInfo->aConstraintUsage[iTermEq].argvIndex = ++nArg; |
| pInfo->estimatedCost = 100; |
| }else{ |
| pInfo->estimatedCost = 1000000; |
| if( iTermGe>=0 ){ |
| idxNum |= FTS5_VOCAB_TERM_GE; |
| pInfo->aConstraintUsage[iTermGe].argvIndex = ++nArg; |
| pInfo->estimatedCost = pInfo->estimatedCost / 2; |
| } |
| if( iTermLe>=0 ){ |
| idxNum |= FTS5_VOCAB_TERM_LE; |
| pInfo->aConstraintUsage[iTermLe].argvIndex = ++nArg; |
| pInfo->estimatedCost = pInfo->estimatedCost / 2; |
| } |
| } |
|
|
| |
| |
| |
| |
| |
| if( pInfo->nOrderBy==1 |
| && pInfo->aOrderBy[0].iColumn==0 |
| && pInfo->aOrderBy[0].desc==0 |
| ){ |
| pInfo->orderByConsumed = 1; |
| } |
|
|
| pInfo->idxNum = idxNum; |
| return SQLITE_OK; |
| } |
|
|
| |
| |
| |
| static int fts5VocabOpenMethod( |
| sqlite3_vtab *pVTab, |
| sqlite3_vtab_cursor **ppCsr |
| ){ |
| Fts5VocabTable *pTab = (Fts5VocabTable*)pVTab; |
| Fts5Table *pFts5 = 0; |
| Fts5VocabCursor *pCsr = 0; |
| int rc = SQLITE_OK; |
| sqlite3_stmt *pStmt = 0; |
| char *zSql = 0; |
|
|
| if( pTab->bBusy ){ |
| pVTab->zErrMsg = sqlite3_mprintf( |
| "recursive definition for %s.%s", pTab->zFts5Db, pTab->zFts5Tbl |
| ); |
| return SQLITE_ERROR; |
| } |
| zSql = sqlite3Fts5Mprintf(&rc, |
| "SELECT t.%Q FROM %Q.%Q AS t WHERE t.%Q MATCH '*id'", |
| pTab->zFts5Tbl, pTab->zFts5Db, pTab->zFts5Tbl, pTab->zFts5Tbl |
| ); |
| if( zSql ){ |
| rc = sqlite3_prepare_v2(pTab->db, zSql, -1, &pStmt, 0); |
| } |
| sqlite3_free(zSql); |
| assert( rc==SQLITE_OK || pStmt==0 ); |
| if( rc==SQLITE_ERROR ) rc = SQLITE_OK; |
|
|
| pTab->bBusy = 1; |
| if( pStmt && sqlite3_step(pStmt)==SQLITE_ROW ){ |
| i64 iId = sqlite3_column_int64(pStmt, 0); |
| pFts5 = sqlite3Fts5TableFromCsrid(pTab->pGlobal, iId); |
| } |
| pTab->bBusy = 0; |
|
|
| if( rc==SQLITE_OK ){ |
| if( pFts5==0 ){ |
| rc = sqlite3_finalize(pStmt); |
| pStmt = 0; |
| if( rc==SQLITE_OK ){ |
| pVTab->zErrMsg = sqlite3_mprintf( |
| "no such fts5 table: %s.%s", pTab->zFts5Db, pTab->zFts5Tbl |
| ); |
| rc = SQLITE_ERROR; |
| } |
| }else{ |
| rc = sqlite3Fts5FlushToDisk(pFts5); |
| } |
| } |
|
|
| if( rc==SQLITE_OK ){ |
| i64 nByte = pFts5->pConfig->nCol * sizeof(i64)*2 + sizeof(Fts5VocabCursor); |
| pCsr = (Fts5VocabCursor*)sqlite3Fts5MallocZero(&rc, nByte); |
| } |
|
|
| if( pCsr ){ |
| pCsr->pFts5 = pFts5; |
| pCsr->pStmt = pStmt; |
| pCsr->aCnt = (i64*)&pCsr[1]; |
| pCsr->aDoc = &pCsr->aCnt[pFts5->pConfig->nCol]; |
| }else{ |
| sqlite3_finalize(pStmt); |
| } |
|
|
| *ppCsr = (sqlite3_vtab_cursor*)pCsr; |
| return rc; |
| } |
|
|
| |
| |
| |
| |
| static void fts5VocabResetCursor(Fts5VocabCursor *pCsr){ |
| int nCol = pCsr->pFts5->pConfig->nCol; |
| pCsr->rowid = 0; |
| sqlite3Fts5IterClose(pCsr->pIter); |
| sqlite3Fts5StructureRelease(pCsr->pStruct); |
| pCsr->pStruct = 0; |
| pCsr->pIter = 0; |
| sqlite3_free(pCsr->zLeTerm); |
| pCsr->nLeTerm = -1; |
| pCsr->zLeTerm = 0; |
| pCsr->bEof = 0; |
| pCsr->iCol = 0; |
| pCsr->iInstPos = 0; |
| pCsr->iInstOff = 0; |
| pCsr->colUsed = 0; |
| memset(pCsr->aCnt, 0, sizeof(i64)*nCol); |
| memset(pCsr->aDoc, 0, sizeof(i64)*nCol); |
| } |
|
|
| |
| |
| |
| |
| static int fts5VocabCloseMethod(sqlite3_vtab_cursor *pCursor){ |
| Fts5VocabCursor *pCsr = (Fts5VocabCursor*)pCursor; |
| fts5VocabResetCursor(pCsr); |
| sqlite3Fts5BufferFree(&pCsr->term); |
| sqlite3_finalize(pCsr->pStmt); |
| sqlite3_free(pCsr); |
| return SQLITE_OK; |
| } |
|
|
| static int fts5VocabInstanceNewTerm(Fts5VocabCursor *pCsr){ |
| int rc = SQLITE_OK; |
| |
| if( sqlite3Fts5IterEof(pCsr->pIter) ){ |
| pCsr->bEof = 1; |
| }else{ |
| const char *zTerm; |
| int nTerm; |
| zTerm = sqlite3Fts5IterTerm(pCsr->pIter, &nTerm); |
| if( pCsr->nLeTerm>=0 ){ |
| int nCmp = MIN(nTerm, pCsr->nLeTerm); |
| int bCmp = memcmp(pCsr->zLeTerm, zTerm, nCmp); |
| if( bCmp<0 || (bCmp==0 && pCsr->nLeTerm<nTerm) ){ |
| pCsr->bEof = 1; |
| } |
| } |
|
|
| sqlite3Fts5BufferSet(&rc, &pCsr->term, nTerm, (const u8*)zTerm); |
| } |
| return rc; |
| } |
|
|
| static int fts5VocabInstanceNext(Fts5VocabCursor *pCsr){ |
| int eDetail = pCsr->pFts5->pConfig->eDetail; |
| int rc = SQLITE_OK; |
| Fts5IndexIter *pIter = pCsr->pIter; |
| i64 *pp = &pCsr->iInstPos; |
| int *po = &pCsr->iInstOff; |
| |
| assert( sqlite3Fts5IterEof(pIter)==0 ); |
| assert( pCsr->bEof==0 ); |
| while( eDetail==FTS5_DETAIL_NONE |
| || sqlite3Fts5PoslistNext64(pIter->pData, pIter->nData, po, pp) |
| ){ |
| pCsr->iInstPos = 0; |
| pCsr->iInstOff = 0; |
|
|
| rc = sqlite3Fts5IterNextScan(pCsr->pIter); |
| if( rc==SQLITE_OK ){ |
| rc = fts5VocabInstanceNewTerm(pCsr); |
| if( pCsr->bEof || eDetail==FTS5_DETAIL_NONE ) break; |
| } |
| if( rc ){ |
| pCsr->bEof = 1; |
| break; |
| } |
| } |
|
|
| return rc; |
| } |
|
|
| |
| |
| |
| static int fts5VocabNextMethod(sqlite3_vtab_cursor *pCursor){ |
| Fts5VocabCursor *pCsr = (Fts5VocabCursor*)pCursor; |
| Fts5VocabTable *pTab = (Fts5VocabTable*)pCursor->pVtab; |
| int nCol = pCsr->pFts5->pConfig->nCol; |
| int rc; |
|
|
| rc = sqlite3Fts5StructureTest(pCsr->pFts5->pIndex, pCsr->pStruct); |
| if( rc!=SQLITE_OK ) return rc; |
| pCsr->rowid++; |
|
|
| if( pTab->eType==FTS5_VOCAB_INSTANCE ){ |
| return fts5VocabInstanceNext(pCsr); |
| } |
|
|
| if( pTab->eType==FTS5_VOCAB_COL ){ |
| for(pCsr->iCol++; pCsr->iCol<nCol; pCsr->iCol++){ |
| if( pCsr->aDoc[pCsr->iCol] ) break; |
| } |
| } |
|
|
| if( pTab->eType!=FTS5_VOCAB_COL || pCsr->iCol>=nCol ){ |
| if( sqlite3Fts5IterEof(pCsr->pIter) ){ |
| pCsr->bEof = 1; |
| }else{ |
| const char *zTerm; |
| int nTerm; |
|
|
| zTerm = sqlite3Fts5IterTerm(pCsr->pIter, &nTerm); |
| assert( nTerm>=0 ); |
| if( pCsr->nLeTerm>=0 ){ |
| int nCmp = MIN(nTerm, pCsr->nLeTerm); |
| int bCmp = memcmp(pCsr->zLeTerm, zTerm, nCmp); |
| if( bCmp<0 || (bCmp==0 && pCsr->nLeTerm<nTerm) ){ |
| pCsr->bEof = 1; |
| return SQLITE_OK; |
| } |
| } |
|
|
| sqlite3Fts5BufferSet(&rc, &pCsr->term, nTerm, (const u8*)zTerm); |
| memset(pCsr->aCnt, 0, nCol * sizeof(i64)); |
| memset(pCsr->aDoc, 0, nCol * sizeof(i64)); |
| pCsr->iCol = 0; |
|
|
| assert( pTab->eType==FTS5_VOCAB_COL || pTab->eType==FTS5_VOCAB_ROW ); |
| while( rc==SQLITE_OK ){ |
| int eDetail = pCsr->pFts5->pConfig->eDetail; |
| const u8 *pPos; int nPos; |
| i64 iPos = 0; |
| int iOff = 0; |
|
|
| pPos = pCsr->pIter->pData; |
| nPos = pCsr->pIter->nData; |
|
|
| switch( pTab->eType ){ |
| case FTS5_VOCAB_ROW: |
| |
| |
| if( eDetail==FTS5_DETAIL_FULL && (pCsr->colUsed & 0x04) ){ |
| while( iPos<nPos ){ |
| u32 ii; |
| fts5FastGetVarint32(pPos, iPos, ii); |
| if( ii==1 ){ |
| |
| fts5FastGetVarint32(pPos, iPos, ii); |
| }else{ |
| |
| pCsr->aCnt[0]++; |
| } |
| } |
| } |
| pCsr->aDoc[0]++; |
| break; |
|
|
| case FTS5_VOCAB_COL: |
| if( eDetail==FTS5_DETAIL_FULL ){ |
| int iCol = -1; |
| while( 0==sqlite3Fts5PoslistNext64(pPos, nPos, &iOff, &iPos) ){ |
| int ii = FTS5_POS2COLUMN(iPos); |
| if( iCol!=ii ){ |
| if( ii>=nCol ){ |
| rc = FTS5_CORRUPT; |
| break; |
| } |
| pCsr->aDoc[ii]++; |
| iCol = ii; |
| } |
| pCsr->aCnt[ii]++; |
| } |
| }else if( eDetail==FTS5_DETAIL_COLUMNS ){ |
| while( 0==sqlite3Fts5PoslistNext64(pPos, nPos, &iOff,&iPos) ){ |
| assert_nc( iPos>=0 && iPos<nCol ); |
| if( iPos>=nCol ){ |
| rc = FTS5_CORRUPT; |
| break; |
| } |
| pCsr->aDoc[iPos]++; |
| } |
| }else{ |
| assert( eDetail==FTS5_DETAIL_NONE ); |
| pCsr->aDoc[0]++; |
| } |
| break; |
|
|
| default: |
| assert( pTab->eType==FTS5_VOCAB_INSTANCE ); |
| break; |
| } |
|
|
| if( rc==SQLITE_OK ){ |
| rc = sqlite3Fts5IterNextScan(pCsr->pIter); |
| } |
| if( pTab->eType==FTS5_VOCAB_INSTANCE ) break; |
|
|
| if( rc==SQLITE_OK ){ |
| zTerm = sqlite3Fts5IterTerm(pCsr->pIter, &nTerm); |
| if( nTerm!=pCsr->term.n |
| || (nTerm>0 && memcmp(zTerm, pCsr->term.p, nTerm)) |
| ){ |
| break; |
| } |
| if( sqlite3Fts5IterEof(pCsr->pIter) ) break; |
| } |
| } |
| } |
| } |
|
|
| if( rc==SQLITE_OK && pCsr->bEof==0 && pTab->eType==FTS5_VOCAB_COL ){ |
| for(; pCsr->iCol<nCol && pCsr->aDoc[pCsr->iCol]==0; pCsr->iCol++); |
| if( pCsr->iCol==nCol ){ |
| rc = FTS5_CORRUPT; |
| } |
| } |
| return rc; |
| } |
|
|
| |
| |
| |
| static int fts5VocabFilterMethod( |
| sqlite3_vtab_cursor *pCursor, |
| int idxNum, |
| const char *zUnused, |
| int nUnused, |
| sqlite3_value **apVal |
| ){ |
| Fts5VocabTable *pTab = (Fts5VocabTable*)pCursor->pVtab; |
| Fts5VocabCursor *pCsr = (Fts5VocabCursor*)pCursor; |
| int eType = pTab->eType; |
| int rc = SQLITE_OK; |
|
|
| int iVal = 0; |
| int f = FTS5INDEX_QUERY_SCAN; |
| const char *zTerm = 0; |
| int nTerm = 0; |
|
|
| sqlite3_value *pEq = 0; |
| sqlite3_value *pGe = 0; |
| sqlite3_value *pLe = 0; |
|
|
| UNUSED_PARAM2(zUnused, nUnused); |
|
|
| fts5VocabResetCursor(pCsr); |
| if( idxNum & FTS5_VOCAB_TERM_EQ ) pEq = apVal[iVal++]; |
| if( idxNum & FTS5_VOCAB_TERM_GE ) pGe = apVal[iVal++]; |
| if( idxNum & FTS5_VOCAB_TERM_LE ) pLe = apVal[iVal++]; |
| pCsr->colUsed = (idxNum & FTS5_VOCAB_COLUSED_MASK); |
|
|
| if( pEq ){ |
| zTerm = (const char *)sqlite3_value_text(pEq); |
| nTerm = sqlite3_value_bytes(pEq); |
| f = FTS5INDEX_QUERY_NOTOKENDATA; |
| }else{ |
| if( pGe ){ |
| zTerm = (const char *)sqlite3_value_text(pGe); |
| nTerm = sqlite3_value_bytes(pGe); |
| } |
| if( pLe ){ |
| const char *zCopy = (const char *)sqlite3_value_text(pLe); |
| if( zCopy==0 ) zCopy = ""; |
| pCsr->nLeTerm = sqlite3_value_bytes(pLe); |
| pCsr->zLeTerm = sqlite3_malloc(pCsr->nLeTerm+1); |
| if( pCsr->zLeTerm==0 ){ |
| rc = SQLITE_NOMEM; |
| }else{ |
| memcpy(pCsr->zLeTerm, zCopy, pCsr->nLeTerm+1); |
| } |
| } |
| } |
|
|
| if( rc==SQLITE_OK ){ |
| Fts5Index *pIndex = pCsr->pFts5->pIndex; |
| rc = sqlite3Fts5IndexQuery(pIndex, zTerm, nTerm, f, 0, &pCsr->pIter); |
| if( rc==SQLITE_OK ){ |
| pCsr->pStruct = sqlite3Fts5StructureRef(pIndex); |
| } |
| } |
| if( rc==SQLITE_OK && eType==FTS5_VOCAB_INSTANCE ){ |
| rc = fts5VocabInstanceNewTerm(pCsr); |
| } |
| if( rc==SQLITE_OK && !pCsr->bEof |
| && (eType!=FTS5_VOCAB_INSTANCE |
| || pCsr->pFts5->pConfig->eDetail!=FTS5_DETAIL_NONE) |
| ){ |
| rc = fts5VocabNextMethod(pCursor); |
| } |
|
|
| return rc; |
| } |
|
|
| |
| |
| |
| |
| static int fts5VocabEofMethod(sqlite3_vtab_cursor *pCursor){ |
| Fts5VocabCursor *pCsr = (Fts5VocabCursor*)pCursor; |
| return pCsr->bEof; |
| } |
|
|
| static int fts5VocabColumnMethod( |
| sqlite3_vtab_cursor *pCursor, |
| sqlite3_context *pCtx, |
| int iCol |
| ){ |
| Fts5VocabCursor *pCsr = (Fts5VocabCursor*)pCursor; |
| int eDetail = pCsr->pFts5->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( eType==FTS5_VOCAB_COL ){ |
| assert( iCol==1 || iCol==2 || iCol==3 ); |
| if( iCol==1 ){ |
| if( eDetail!=FTS5_DETAIL_NONE ){ |
| const char *z = pCsr->pFts5->pConfig->azCol[pCsr->iCol]; |
| sqlite3_result_text(pCtx, z, -1, SQLITE_STATIC); |
| } |
| }else if( iCol==2 ){ |
| iVal = pCsr->aDoc[pCsr->iCol]; |
| }else{ |
| iVal = pCsr->aCnt[pCsr->iCol]; |
| } |
| }else if( eType==FTS5_VOCAB_ROW ){ |
| assert( iCol==1 || iCol==2 ); |
| if( iCol==1 ){ |
| iVal = pCsr->aDoc[0]; |
| }else{ |
| iVal = pCsr->aCnt[0]; |
| } |
| }else{ |
| assert( eType==FTS5_VOCAB_INSTANCE ); |
| switch( iCol ){ |
| case 1: |
| sqlite3_result_int64(pCtx, pCsr->pIter->iRowid); |
| break; |
| case 2: { |
| int ii = -1; |
| if( eDetail==FTS5_DETAIL_FULL ){ |
| ii = FTS5_POS2COLUMN(pCsr->iInstPos); |
| }else if( eDetail==FTS5_DETAIL_COLUMNS ){ |
| ii = (int)pCsr->iInstPos; |
| } |
| if( ii>=0 && ii<pCsr->pFts5->pConfig->nCol ){ |
| const char *z = pCsr->pFts5->pConfig->azCol[ii]; |
| sqlite3_result_text(pCtx, z, -1, SQLITE_STATIC); |
| } |
| break; |
| } |
| default: { |
| assert( iCol==3 ); |
| if( eDetail==FTS5_DETAIL_FULL ){ |
| int ii = FTS5_POS2OFFSET(pCsr->iInstPos); |
| sqlite3_result_int(pCtx, ii); |
| } |
| break; |
| } |
| } |
| } |
|
|
| if( iVal>0 ) sqlite3_result_int64(pCtx, iVal); |
| return SQLITE_OK; |
| } |
|
|
| |
| |
| |
| |
| |
| static int fts5VocabRowidMethod( |
| sqlite3_vtab_cursor *pCursor, |
| sqlite_int64 *pRowid |
| ){ |
| Fts5VocabCursor *pCsr = (Fts5VocabCursor*)pCursor; |
| *pRowid = pCsr->rowid; |
| return SQLITE_OK; |
| } |
|
|
| int sqlite3Fts5VocabInit(Fts5Global *pGlobal, sqlite3 *db){ |
| static const sqlite3_module fts5Vocab = { |
| 2, |
| fts5VocabCreateMethod, |
| fts5VocabConnectMethod, |
| fts5VocabBestIndexMethod, |
| fts5VocabDisconnectMethod, |
| fts5VocabDestroyMethod, |
| fts5VocabOpenMethod, |
| fts5VocabCloseMethod, |
| fts5VocabFilterMethod, |
| fts5VocabNextMethod, |
| fts5VocabEofMethod, |
| fts5VocabColumnMethod, |
| fts5VocabRowidMethod, |
| 0, |
| 0, |
| 0, |
| 0, |
| 0, |
| 0, |
| 0, |
| 0, |
| 0, |
| 0, |
| 0, |
| 0 |
| }; |
| void *p = (void*)pGlobal; |
|
|
| return sqlite3_create_module_v2(db, "fts5vocab", &fts5Vocab, p, 0); |
| } |
|
|