4 ** The author disclaims copyright to this source code. In place of
5 ** a legal notice, here is a blessing:
7 ** May you do good and not evil.
8 ** May you find forgiveness for yourself and forgive others.
9 ** May you share freely, never taking more than you give.
11 ******************************************************************************
13 ** This is an SQLite virtual table module implementing direct access to an
14 ** existing FTS5 index. The module may create several different types of
18 ** CREATE TABLE vocab(term, col, doc, cnt, PRIMARY KEY(term, col));
20 ** One row for each term/column combination. The value of $doc is set to
21 ** the number of fts5 rows that contain at least one instance of term
22 ** $term within column $col. Field $cnt is set to the total number of
23 ** instances of term $term in column $col (in any row of the fts5 table).
26 ** CREATE TABLE vocab(term, doc, cnt, PRIMARY KEY(term));
28 ** One row for each term in the database. The value of $doc is set to
29 ** the number of fts5 rows that contain at least one instance of term
30 ** $term. Field $cnt is set to the total number of instances of term
31 ** $term in the database.
34 ** CREATE TABLE vocab(term, doc, col, offset, PRIMARY KEY(<all-fields>));
36 ** One row for each term instance in the database.
43 typedef struct Fts5VocabTable Fts5VocabTable
;
44 typedef struct Fts5VocabCursor Fts5VocabCursor
;
46 struct Fts5VocabTable
{
48 char *zFts5Tbl
; /* Name of fts5 table */
49 char *zFts5Db
; /* Db containing fts5 table */
50 sqlite3
*db
; /* Database handle */
51 Fts5Global
*pGlobal
; /* FTS5 global object for this database */
52 int eType
; /* FTS5_VOCAB_COL, ROW or INSTANCE */
55 struct Fts5VocabCursor
{
56 sqlite3_vtab_cursor base
;
57 sqlite3_stmt
*pStmt
; /* Statement holding lock on pIndex */
58 Fts5Index
*pIndex
; /* Associated FTS5 index */
60 int bEof
; /* True if this cursor is at EOF */
61 Fts5IndexIter
*pIter
; /* Term/rowid iterator object */
63 int nLeTerm
; /* Size of zLeTerm in bytes */
64 char *zLeTerm
; /* (term <= $zLeTerm) paramater, or NULL */
66 /* These are used by 'col' tables only */
67 Fts5Config
*pConfig
; /* Fts5 table configuration */
72 /* Output values used by all tables. */
73 i64 rowid
; /* This table's current rowid value */
74 Fts5Buffer term
; /* Current value of 'term' column */
76 /* Output values Used by 'instance' tables only */
81 #define FTS5_VOCAB_COL 0
82 #define FTS5_VOCAB_ROW 1
83 #define FTS5_VOCAB_INSTANCE 2
85 #define FTS5_VOCAB_COL_SCHEMA "term, col, doc, cnt"
86 #define FTS5_VOCAB_ROW_SCHEMA "term, doc, cnt"
87 #define FTS5_VOCAB_INST_SCHEMA "term, doc, col, offset"
90 ** Bits for the mask used as the idxNum value by xBestIndex/xFilter.
92 #define FTS5_VOCAB_TERM_EQ 0x01
93 #define FTS5_VOCAB_TERM_GE 0x02
94 #define FTS5_VOCAB_TERM_LE 0x04
98 ** Translate a string containing an fts5vocab table type to an
99 ** FTS5_VOCAB_XXX constant. If successful, set *peType to the output
100 ** value and return SQLITE_OK. Otherwise, set *pzErr to an error message
101 ** and return SQLITE_ERROR.
103 static int fts5VocabTableType(const char *zType
, char **pzErr
, int *peType
){
105 char *zCopy
= sqlite3Fts5Strndup(&rc
, zType
, -1);
107 sqlite3Fts5Dequote(zCopy
);
108 if( sqlite3_stricmp(zCopy
, "col")==0 ){
109 *peType
= FTS5_VOCAB_COL
;
112 if( sqlite3_stricmp(zCopy
, "row")==0 ){
113 *peType
= FTS5_VOCAB_ROW
;
115 if( sqlite3_stricmp(zCopy
, "instance")==0 ){
116 *peType
= FTS5_VOCAB_INSTANCE
;
119 *pzErr
= sqlite3_mprintf("fts5vocab: unknown table type: %Q", zCopy
);
130 ** The xDisconnect() virtual table method.
132 static int fts5VocabDisconnectMethod(sqlite3_vtab
*pVtab
){
133 Fts5VocabTable
*pTab
= (Fts5VocabTable
*)pVtab
;
139 ** The xDestroy() virtual table method.
141 static int fts5VocabDestroyMethod(sqlite3_vtab
*pVtab
){
142 Fts5VocabTable
*pTab
= (Fts5VocabTable
*)pVtab
;
148 ** This function is the implementation of both the xConnect and xCreate
149 ** methods of the FTS3 virtual table.
151 ** The argv[] array contains the following:
153 ** argv[0] -> module name ("fts5vocab")
154 ** argv[1] -> database name
155 ** argv[2] -> table name
159 ** argv[3] -> name of fts5 table
160 ** argv[4] -> type of fts5vocab table
162 ** or, for tables in the TEMP schema only.
164 ** argv[3] -> name of fts5 tables database
165 ** argv[4] -> name of fts5 table
166 ** argv[5] -> type of fts5vocab table
168 static int fts5VocabInitVtab(
169 sqlite3
*db
, /* The SQLite database connection */
170 void *pAux
, /* Pointer to Fts5Global object */
171 int argc
, /* Number of elements in argv array */
172 const char * const *argv
, /* xCreate/xConnect argument array */
173 sqlite3_vtab
**ppVTab
, /* Write the resulting vtab structure here */
174 char **pzErr
/* Write any error message here */
176 const char *azSchema
[] = {
177 "CREATE TABlE vocab(" FTS5_VOCAB_COL_SCHEMA
")",
178 "CREATE TABlE vocab(" FTS5_VOCAB_ROW_SCHEMA
")",
179 "CREATE TABlE vocab(" FTS5_VOCAB_INST_SCHEMA
")"
182 Fts5VocabTable
*pRet
= 0;
183 int rc
= SQLITE_OK
; /* Return code */
186 bDb
= (argc
==6 && strlen(argv
[1])==4 && memcmp("temp", argv
[1], 4)==0);
188 if( argc
!=5 && bDb
==0 ){
189 *pzErr
= sqlite3_mprintf("wrong number of vtable arguments");
192 int nByte
; /* Bytes of space to allocate */
193 const char *zDb
= bDb
? argv
[3] : argv
[1];
194 const char *zTab
= bDb
? argv
[4] : argv
[3];
195 const char *zType
= bDb
? argv
[5] : argv
[4];
196 int nDb
= (int)strlen(zDb
)+1;
197 int nTab
= (int)strlen(zTab
)+1;
200 rc
= fts5VocabTableType(zType
, pzErr
, &eType
);
202 assert( eType
>=0 && eType
<ArraySize(azSchema
) );
203 rc
= sqlite3_declare_vtab(db
, azSchema
[eType
]);
206 nByte
= sizeof(Fts5VocabTable
) + nDb
+ nTab
;
207 pRet
= sqlite3Fts5MallocZero(&rc
, nByte
);
209 pRet
->pGlobal
= (Fts5Global
*)pAux
;
212 pRet
->zFts5Tbl
= (char*)&pRet
[1];
213 pRet
->zFts5Db
= &pRet
->zFts5Tbl
[nTab
];
214 memcpy(pRet
->zFts5Tbl
, zTab
, nTab
);
215 memcpy(pRet
->zFts5Db
, zDb
, nDb
);
216 sqlite3Fts5Dequote(pRet
->zFts5Tbl
);
217 sqlite3Fts5Dequote(pRet
->zFts5Db
);
221 *ppVTab
= (sqlite3_vtab
*)pRet
;
227 ** The xConnect() and xCreate() methods for the virtual table. All the
228 ** work is done in function fts5VocabInitVtab().
230 static int fts5VocabConnectMethod(
231 sqlite3
*db
, /* Database connection */
232 void *pAux
, /* Pointer to tokenizer hash table */
233 int argc
, /* Number of elements in argv array */
234 const char * const *argv
, /* xCreate/xConnect argument array */
235 sqlite3_vtab
**ppVtab
, /* OUT: New sqlite3_vtab object */
236 char **pzErr
/* OUT: sqlite3_malloc'd error message */
238 return fts5VocabInitVtab(db
, pAux
, argc
, argv
, ppVtab
, pzErr
);
240 static int fts5VocabCreateMethod(
241 sqlite3
*db
, /* Database connection */
242 void *pAux
, /* Pointer to tokenizer hash table */
243 int argc
, /* Number of elements in argv array */
244 const char * const *argv
, /* xCreate/xConnect argument array */
245 sqlite3_vtab
**ppVtab
, /* OUT: New sqlite3_vtab object */
246 char **pzErr
/* OUT: sqlite3_malloc'd error message */
248 return fts5VocabInitVtab(db
, pAux
, argc
, argv
, ppVtab
, pzErr
);
252 ** Implementation of the xBestIndex method.
254 ** Only constraints of the form:
260 ** are interpreted. Less-than and less-than-or-equal are treated
261 ** identically, as are greater-than and greater-than-or-equal.
263 static int fts5VocabBestIndexMethod(
264 sqlite3_vtab
*pUnused
,
265 sqlite3_index_info
*pInfo
274 UNUSED_PARAM(pUnused
);
276 for(i
=0; i
<pInfo
->nConstraint
; i
++){
277 struct sqlite3_index_constraint
*p
= &pInfo
->aConstraint
[i
];
278 if( p
->usable
==0 ) continue;
279 if( p
->iColumn
==0 ){ /* term column */
280 if( p
->op
==SQLITE_INDEX_CONSTRAINT_EQ
) iTermEq
= i
;
281 if( p
->op
==SQLITE_INDEX_CONSTRAINT_LE
) iTermLe
= i
;
282 if( p
->op
==SQLITE_INDEX_CONSTRAINT_LT
) iTermLe
= i
;
283 if( p
->op
==SQLITE_INDEX_CONSTRAINT_GE
) iTermGe
= i
;
284 if( p
->op
==SQLITE_INDEX_CONSTRAINT_GT
) iTermGe
= i
;
289 idxNum
|= FTS5_VOCAB_TERM_EQ
;
290 pInfo
->aConstraintUsage
[iTermEq
].argvIndex
= ++nArg
;
291 pInfo
->estimatedCost
= 100;
293 pInfo
->estimatedCost
= 1000000;
295 idxNum
|= FTS5_VOCAB_TERM_GE
;
296 pInfo
->aConstraintUsage
[iTermGe
].argvIndex
= ++nArg
;
297 pInfo
->estimatedCost
= pInfo
->estimatedCost
/ 2;
300 idxNum
|= FTS5_VOCAB_TERM_LE
;
301 pInfo
->aConstraintUsage
[iTermLe
].argvIndex
= ++nArg
;
302 pInfo
->estimatedCost
= pInfo
->estimatedCost
/ 2;
306 /* This virtual table always delivers results in ascending order of
307 ** the "term" column (column 0). So if the user has requested this
308 ** specifically - "ORDER BY term" or "ORDER BY term ASC" - set the
309 ** sqlite3_index_info.orderByConsumed flag to tell the core the results
310 ** are already in sorted order. */
311 if( pInfo
->nOrderBy
==1
312 && pInfo
->aOrderBy
[0].iColumn
==0
313 && pInfo
->aOrderBy
[0].desc
==0
315 pInfo
->orderByConsumed
= 1;
318 pInfo
->idxNum
= idxNum
;
323 ** Implementation of xOpen method.
325 static int fts5VocabOpenMethod(
327 sqlite3_vtab_cursor
**ppCsr
329 Fts5VocabTable
*pTab
= (Fts5VocabTable
*)pVTab
;
330 Fts5Index
*pIndex
= 0;
331 Fts5Config
*pConfig
= 0;
332 Fts5VocabCursor
*pCsr
= 0;
334 sqlite3_stmt
*pStmt
= 0;
337 zSql
= sqlite3Fts5Mprintf(&rc
,
338 "SELECT t.%Q FROM %Q.%Q AS t WHERE t.%Q MATCH '*id'",
339 pTab
->zFts5Tbl
, pTab
->zFts5Db
, pTab
->zFts5Tbl
, pTab
->zFts5Tbl
342 rc
= sqlite3_prepare_v2(pTab
->db
, zSql
, -1, &pStmt
, 0);
345 assert( rc
==SQLITE_OK
|| pStmt
==0 );
346 if( rc
==SQLITE_ERROR
) rc
= SQLITE_OK
;
348 if( pStmt
&& sqlite3_step(pStmt
)==SQLITE_ROW
){
349 i64 iId
= sqlite3_column_int64(pStmt
, 0);
350 pIndex
= sqlite3Fts5IndexFromCsrid(pTab
->pGlobal
, iId
, &pConfig
);
353 if( rc
==SQLITE_OK
&& pIndex
==0 ){
354 rc
= sqlite3_finalize(pStmt
);
357 pVTab
->zErrMsg
= sqlite3_mprintf(
358 "no such fts5 table: %s.%s", pTab
->zFts5Db
, pTab
->zFts5Tbl
365 int nByte
= pConfig
->nCol
* sizeof(i64
) * 2 + sizeof(Fts5VocabCursor
);
366 pCsr
= (Fts5VocabCursor
*)sqlite3Fts5MallocZero(&rc
, nByte
);
370 pCsr
->pIndex
= pIndex
;
372 pCsr
->pConfig
= pConfig
;
373 pCsr
->aCnt
= (i64
*)&pCsr
[1];
374 pCsr
->aDoc
= &pCsr
->aCnt
[pConfig
->nCol
];
376 sqlite3_finalize(pStmt
);
379 *ppCsr
= (sqlite3_vtab_cursor
*)pCsr
;
383 static void fts5VocabResetCursor(Fts5VocabCursor
*pCsr
){
385 sqlite3Fts5IterClose(pCsr
->pIter
);
387 sqlite3_free(pCsr
->zLeTerm
);
393 ** Close the cursor. For additional information see the documentation
394 ** on the xClose method of the virtual table interface.
396 static int fts5VocabCloseMethod(sqlite3_vtab_cursor
*pCursor
){
397 Fts5VocabCursor
*pCsr
= (Fts5VocabCursor
*)pCursor
;
398 fts5VocabResetCursor(pCsr
);
399 sqlite3Fts5BufferFree(&pCsr
->term
);
400 sqlite3_finalize(pCsr
->pStmt
);
405 static int fts5VocabInstanceNewTerm(Fts5VocabCursor
*pCsr
){
408 if( sqlite3Fts5IterEof(pCsr
->pIter
) ){
413 zTerm
= sqlite3Fts5IterTerm(pCsr
->pIter
, &nTerm
);
414 if( pCsr
->nLeTerm
>=0 ){
415 int nCmp
= MIN(nTerm
, pCsr
->nLeTerm
);
416 int bCmp
= memcmp(pCsr
->zLeTerm
, zTerm
, nCmp
);
417 if( bCmp
<0 || (bCmp
==0 && pCsr
->nLeTerm
<nTerm
) ){
422 sqlite3Fts5BufferSet(&rc
, &pCsr
->term
, nTerm
, (const u8
*)zTerm
);
427 static int fts5VocabInstanceNext(Fts5VocabCursor
*pCsr
){
428 int eDetail
= pCsr
->pConfig
->eDetail
;
430 Fts5IndexIter
*pIter
= pCsr
->pIter
;
431 i64
*pp
= &pCsr
->iInstPos
;
432 int *po
= &pCsr
->iInstOff
;
434 while( eDetail
==FTS5_DETAIL_NONE
435 || sqlite3Fts5PoslistNext64(pIter
->pData
, pIter
->nData
, po
, pp
)
440 rc
= sqlite3Fts5IterNextScan(pCsr
->pIter
);
442 rc
= fts5VocabInstanceNewTerm(pCsr
);
443 if( eDetail
==FTS5_DETAIL_NONE
) break;
455 ** Advance the cursor to the next row in the table.
457 static int fts5VocabNextMethod(sqlite3_vtab_cursor
*pCursor
){
458 Fts5VocabCursor
*pCsr
= (Fts5VocabCursor
*)pCursor
;
459 Fts5VocabTable
*pTab
= (Fts5VocabTable
*)pCursor
->pVtab
;
461 int nCol
= pCsr
->pConfig
->nCol
;
465 if( pTab
->eType
==FTS5_VOCAB_INSTANCE
){
466 return fts5VocabInstanceNext(pCsr
);
469 if( pTab
->eType
==FTS5_VOCAB_COL
){
470 for(pCsr
->iCol
++; pCsr
->iCol
<nCol
; pCsr
->iCol
++){
471 if( pCsr
->aDoc
[pCsr
->iCol
] ) break;
475 if( pTab
->eType
!=FTS5_VOCAB_COL
|| pCsr
->iCol
>=nCol
){
476 if( sqlite3Fts5IterEof(pCsr
->pIter
) ){
482 zTerm
= sqlite3Fts5IterTerm(pCsr
->pIter
, &nTerm
);
483 if( pCsr
->nLeTerm
>=0 ){
484 int nCmp
= MIN(nTerm
, pCsr
->nLeTerm
);
485 int bCmp
= memcmp(pCsr
->zLeTerm
, zTerm
, nCmp
);
486 if( bCmp
<0 || (bCmp
==0 && pCsr
->nLeTerm
<nTerm
) ){
492 sqlite3Fts5BufferSet(&rc
, &pCsr
->term
, nTerm
, (const u8
*)zTerm
);
493 memset(pCsr
->aCnt
, 0, nCol
* sizeof(i64
));
494 memset(pCsr
->aDoc
, 0, nCol
* sizeof(i64
));
497 assert( pTab
->eType
==FTS5_VOCAB_COL
|| pTab
->eType
==FTS5_VOCAB_ROW
);
498 while( rc
==SQLITE_OK
){
499 int eDetail
= pCsr
->pConfig
->eDetail
;
500 const u8
*pPos
; int nPos
; /* Position list */
501 i64 iPos
= 0; /* 64-bit position read from poslist */
502 int iOff
= 0; /* Current offset within position list */
504 pPos
= pCsr
->pIter
->pData
;
505 nPos
= pCsr
->pIter
->nData
;
507 switch( pTab
->eType
){
509 if( eDetail
==FTS5_DETAIL_FULL
){
510 while( 0==sqlite3Fts5PoslistNext64(pPos
, nPos
, &iOff
, &iPos
) ){
518 if( eDetail
==FTS5_DETAIL_FULL
){
520 while( 0==sqlite3Fts5PoslistNext64(pPos
, nPos
, &iOff
, &iPos
) ){
521 int ii
= FTS5_POS2COLUMN(iPos
);
532 }else if( eDetail
==FTS5_DETAIL_COLUMNS
){
533 while( 0==sqlite3Fts5PoslistNext64(pPos
, nPos
, &iOff
,&iPos
) ){
534 assert_nc( iPos
>=0 && iPos
<nCol
);
542 assert( eDetail
==FTS5_DETAIL_NONE
);
548 assert( pTab
->eType
==FTS5_VOCAB_INSTANCE
);
553 rc
= sqlite3Fts5IterNextScan(pCsr
->pIter
);
555 if( pTab
->eType
==FTS5_VOCAB_INSTANCE
) break;
558 zTerm
= sqlite3Fts5IterTerm(pCsr
->pIter
, &nTerm
);
559 if( nTerm
!=pCsr
->term
.n
|| memcmp(zTerm
, pCsr
->term
.p
, nTerm
) ){
562 if( sqlite3Fts5IterEof(pCsr
->pIter
) ) break;
568 if( rc
==SQLITE_OK
&& pCsr
->bEof
==0 && pTab
->eType
==FTS5_VOCAB_COL
){
569 while( pCsr
->aDoc
[pCsr
->iCol
]==0 ) pCsr
->iCol
++;
570 assert( pCsr
->iCol
<pCsr
->pConfig
->nCol
);
576 ** This is the xFilter implementation for the virtual table.
578 static int fts5VocabFilterMethod(
579 sqlite3_vtab_cursor
*pCursor
, /* The cursor used for this query */
580 int idxNum
, /* Strategy index */
581 const char *zUnused
, /* Unused */
582 int nUnused
, /* Number of elements in apVal */
583 sqlite3_value
**apVal
/* Arguments for the indexing scheme */
585 Fts5VocabTable
*pTab
= (Fts5VocabTable
*)pCursor
->pVtab
;
586 Fts5VocabCursor
*pCsr
= (Fts5VocabCursor
*)pCursor
;
587 int eType
= pTab
->eType
;
591 int f
= FTS5INDEX_QUERY_SCAN
;
592 const char *zTerm
= 0;
595 sqlite3_value
*pEq
= 0;
596 sqlite3_value
*pGe
= 0;
597 sqlite3_value
*pLe
= 0;
599 UNUSED_PARAM2(zUnused
, nUnused
);
601 fts5VocabResetCursor(pCsr
);
602 if( idxNum
& FTS5_VOCAB_TERM_EQ
) pEq
= apVal
[iVal
++];
603 if( idxNum
& FTS5_VOCAB_TERM_GE
) pGe
= apVal
[iVal
++];
604 if( idxNum
& FTS5_VOCAB_TERM_LE
) pLe
= apVal
[iVal
++];
607 zTerm
= (const char *)sqlite3_value_text(pEq
);
608 nTerm
= sqlite3_value_bytes(pEq
);
612 zTerm
= (const char *)sqlite3_value_text(pGe
);
613 nTerm
= sqlite3_value_bytes(pGe
);
616 const char *zCopy
= (const char *)sqlite3_value_text(pLe
);
617 pCsr
->nLeTerm
= sqlite3_value_bytes(pLe
);
618 pCsr
->zLeTerm
= sqlite3_malloc(pCsr
->nLeTerm
+1);
619 if( pCsr
->zLeTerm
==0 ){
622 memcpy(pCsr
->zLeTerm
, zCopy
, pCsr
->nLeTerm
+1);
628 rc
= sqlite3Fts5IndexQuery(pCsr
->pIndex
, zTerm
, nTerm
, f
, 0, &pCsr
->pIter
);
630 if( rc
==SQLITE_OK
&& eType
==FTS5_VOCAB_INSTANCE
){
631 rc
= fts5VocabInstanceNewTerm(pCsr
);
635 && (eType
!=FTS5_VOCAB_INSTANCE
|| pCsr
->pConfig
->eDetail
!=FTS5_DETAIL_NONE
)
637 rc
= fts5VocabNextMethod(pCursor
);
644 ** This is the xEof method of the virtual table. SQLite calls this
645 ** routine to find out if it has reached the end of a result set.
647 static int fts5VocabEofMethod(sqlite3_vtab_cursor
*pCursor
){
648 Fts5VocabCursor
*pCsr
= (Fts5VocabCursor
*)pCursor
;
652 static int fts5VocabColumnMethod(
653 sqlite3_vtab_cursor
*pCursor
, /* Cursor to retrieve value from */
654 sqlite3_context
*pCtx
, /* Context for sqlite3_result_xxx() calls */
655 int iCol
/* Index of column to read value from */
657 Fts5VocabCursor
*pCsr
= (Fts5VocabCursor
*)pCursor
;
658 int eDetail
= pCsr
->pConfig
->eDetail
;
659 int eType
= ((Fts5VocabTable
*)(pCursor
->pVtab
))->eType
;
664 pCtx
, (const char*)pCsr
->term
.p
, pCsr
->term
.n
, SQLITE_TRANSIENT
666 }else if( eType
==FTS5_VOCAB_COL
){
667 assert( iCol
==1 || iCol
==2 || iCol
==3 );
669 if( eDetail
!=FTS5_DETAIL_NONE
){
670 const char *z
= pCsr
->pConfig
->azCol
[pCsr
->iCol
];
671 sqlite3_result_text(pCtx
, z
, -1, SQLITE_STATIC
);
674 iVal
= pCsr
->aDoc
[pCsr
->iCol
];
676 iVal
= pCsr
->aCnt
[pCsr
->iCol
];
678 }else if( eType
==FTS5_VOCAB_ROW
){
679 assert( iCol
==1 || iCol
==2 );
681 iVal
= pCsr
->aDoc
[0];
683 iVal
= pCsr
->aCnt
[0];
686 int eDetail
= pCsr
->pConfig
->eDetail
;
687 assert( eType
==FTS5_VOCAB_INSTANCE
);
690 sqlite3_result_int64(pCtx
, pCsr
->pIter
->iRowid
);
694 if( eDetail
==FTS5_DETAIL_FULL
){
695 ii
= FTS5_POS2COLUMN(pCsr
->iInstPos
);
696 }else if( eDetail
==FTS5_DETAIL_COLUMNS
){
699 if( ii
>=0 && ii
<pCsr
->pConfig
->nCol
){
700 const char *z
= pCsr
->pConfig
->azCol
[ii
];
701 sqlite3_result_text(pCtx
, z
, -1, SQLITE_STATIC
);
707 if( eDetail
==FTS5_DETAIL_FULL
){
708 int ii
= FTS5_POS2OFFSET(pCsr
->iInstPos
);
709 sqlite3_result_int(pCtx
, ii
);
716 if( iVal
>0 ) sqlite3_result_int64(pCtx
, iVal
);
721 ** This is the xRowid method. The SQLite core calls this routine to
722 ** retrieve the rowid for the current row of the result set. The
723 ** rowid should be written to *pRowid.
725 static int fts5VocabRowidMethod(
726 sqlite3_vtab_cursor
*pCursor
,
729 Fts5VocabCursor
*pCsr
= (Fts5VocabCursor
*)pCursor
;
730 *pRowid
= pCsr
->rowid
;
734 int sqlite3Fts5VocabInit(Fts5Global
*pGlobal
, sqlite3
*db
){
735 static const sqlite3_module fts5Vocab
= {
737 /* xCreate */ fts5VocabCreateMethod
,
738 /* xConnect */ fts5VocabConnectMethod
,
739 /* xBestIndex */ fts5VocabBestIndexMethod
,
740 /* xDisconnect */ fts5VocabDisconnectMethod
,
741 /* xDestroy */ fts5VocabDestroyMethod
,
742 /* xOpen */ fts5VocabOpenMethod
,
743 /* xClose */ fts5VocabCloseMethod
,
744 /* xFilter */ fts5VocabFilterMethod
,
745 /* xNext */ fts5VocabNextMethod
,
746 /* xEof */ fts5VocabEofMethod
,
747 /* xColumn */ fts5VocabColumnMethod
,
748 /* xRowid */ fts5VocabRowidMethod
,
754 /* xFindFunction */ 0,
760 void *p
= (void*)pGlobal
;
762 return sqlite3_create_module_v2(db
, "fts5vocab", &fts5Vocab
, p
, 0);