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 ******************************************************************************
15 #if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3)
20 typedef struct Fts3auxTable Fts3auxTable
;
21 typedef struct Fts3auxCursor Fts3auxCursor
;
24 sqlite3_vtab base
; /* Base class used by SQLite core */
28 struct Fts3auxCursor
{
29 sqlite3_vtab_cursor base
; /* Base class used by SQLite core */
30 Fts3MultiSegReader csr
; /* Must be right after "base" */
33 int nStop
; /* Byte-length of string zStop */
34 int iLangid
; /* Language id to query */
35 int isEof
; /* True if cursor is at EOF */
36 sqlite3_int64 iRowid
; /* Current rowid */
38 int iCol
; /* Current value of 'col' column */
39 int nStat
; /* Size of aStat[] array */
40 struct Fts3auxColstats
{
41 sqlite3_int64 nDoc
; /* 'documents' values for current csr row */
42 sqlite3_int64 nOcc
; /* 'occurrences' values for current csr row */
47 ** Schema of the terms table.
49 #define FTS3_AUX_SCHEMA \
50 "CREATE TABLE x(term, col, documents, occurrences, languageid HIDDEN)"
53 ** This function does all the work for both the xConnect and xCreate methods.
54 ** These tables have no persistent representation of their own, so xConnect
55 ** and xCreate are identical operations.
57 static int fts3auxConnectMethod(
58 sqlite3
*db
, /* Database connection */
59 void *pUnused
, /* Unused */
60 int argc
, /* Number of elements in argv array */
61 const char * const *argv
, /* xCreate/xConnect argument array */
62 sqlite3_vtab
**ppVtab
, /* OUT: New sqlite3_vtab object */
63 char **pzErr
/* OUT: sqlite3_malloc'd error message */
65 char const *zDb
; /* Name of database (e.g. "main") */
66 char const *zFts3
; /* Name of fts3 table */
67 int nDb
; /* Result of strlen(zDb) */
68 int nFts3
; /* Result of strlen(zFts3) */
69 sqlite3_int64 nByte
; /* Bytes of space to allocate here */
70 int rc
; /* value returned by declare_vtab() */
71 Fts3auxTable
*p
; /* Virtual table object to return */
73 UNUSED_PARAMETER(pUnused
);
75 /* The user should invoke this in one of two forms:
77 ** CREATE VIRTUAL TABLE xxx USING fts4aux(fts4-table);
78 ** CREATE VIRTUAL TABLE xxx USING fts4aux(fts4-table-db, fts4-table);
80 if( argc
!=4 && argc
!=5 ) goto bad_args
;
83 nDb
= (int)strlen(zDb
);
85 if( nDb
==4 && 0==sqlite3_strnicmp("temp", zDb
, 4) ){
87 nDb
= (int)strlen(zDb
);
95 nFts3
= (int)strlen(zFts3
);
97 rc
= sqlite3_declare_vtab(db
, FTS3_AUX_SCHEMA
);
98 if( rc
!=SQLITE_OK
) return rc
;
100 nByte
= sizeof(Fts3auxTable
) + sizeof(Fts3Table
) + nDb
+ nFts3
+ 2;
101 p
= (Fts3auxTable
*)sqlite3_malloc64(nByte
);
102 if( !p
) return SQLITE_NOMEM
;
105 p
->pFts3Tab
= (Fts3Table
*)&p
[1];
106 p
->pFts3Tab
->zDb
= (char *)&p
->pFts3Tab
[1];
107 p
->pFts3Tab
->zName
= &p
->pFts3Tab
->zDb
[nDb
+1];
108 p
->pFts3Tab
->db
= db
;
109 p
->pFts3Tab
->nIndex
= 1;
111 memcpy((char *)p
->pFts3Tab
->zDb
, zDb
, nDb
);
112 memcpy((char *)p
->pFts3Tab
->zName
, zFts3
, nFts3
);
113 sqlite3Fts3Dequote((char *)p
->pFts3Tab
->zName
);
115 *ppVtab
= (sqlite3_vtab
*)p
;
119 sqlite3Fts3ErrMsg(pzErr
, "invalid arguments to fts4aux constructor");
124 ** This function does the work for both the xDisconnect and xDestroy methods.
125 ** These tables have no persistent representation of their own, so xDisconnect
126 ** and xDestroy are identical operations.
128 static int fts3auxDisconnectMethod(sqlite3_vtab
*pVtab
){
129 Fts3auxTable
*p
= (Fts3auxTable
*)pVtab
;
130 Fts3Table
*pFts3
= p
->pFts3Tab
;
133 /* Free any prepared statements held */
134 for(i
=0; i
<SizeofArray(pFts3
->aStmt
); i
++){
135 sqlite3_finalize(pFts3
->aStmt
[i
]);
137 sqlite3_free(pFts3
->zSegmentsTbl
);
142 #define FTS4AUX_EQ_CONSTRAINT 1
143 #define FTS4AUX_GE_CONSTRAINT 2
144 #define FTS4AUX_LE_CONSTRAINT 4
147 ** xBestIndex - Analyze a WHERE and ORDER BY clause.
149 static int fts3auxBestIndexMethod(
151 sqlite3_index_info
*pInfo
158 int iNext
= 1; /* Next free argvIndex value */
160 UNUSED_PARAMETER(pVTab
);
162 /* This vtab delivers always results in "ORDER BY term ASC" order. */
163 if( pInfo
->nOrderBy
==1
164 && pInfo
->aOrderBy
[0].iColumn
==0
165 && pInfo
->aOrderBy
[0].desc
==0
167 pInfo
->orderByConsumed
= 1;
170 /* Search for equality and range constraints on the "term" column.
171 ** And equality constraints on the hidden "languageid" column. */
172 for(i
=0; i
<pInfo
->nConstraint
; i
++){
173 if( pInfo
->aConstraint
[i
].usable
){
174 int op
= pInfo
->aConstraint
[i
].op
;
175 int iCol
= pInfo
->aConstraint
[i
].iColumn
;
178 if( op
==SQLITE_INDEX_CONSTRAINT_EQ
) iEq
= i
;
179 if( op
==SQLITE_INDEX_CONSTRAINT_LT
) iLe
= i
;
180 if( op
==SQLITE_INDEX_CONSTRAINT_LE
) iLe
= i
;
181 if( op
==SQLITE_INDEX_CONSTRAINT_GT
) iGe
= i
;
182 if( op
==SQLITE_INDEX_CONSTRAINT_GE
) iGe
= i
;
185 if( op
==SQLITE_INDEX_CONSTRAINT_EQ
) iLangid
= i
;
191 pInfo
->idxNum
= FTS4AUX_EQ_CONSTRAINT
;
192 pInfo
->aConstraintUsage
[iEq
].argvIndex
= iNext
++;
193 pInfo
->estimatedCost
= 5;
196 pInfo
->estimatedCost
= 20000;
198 pInfo
->idxNum
+= FTS4AUX_GE_CONSTRAINT
;
199 pInfo
->aConstraintUsage
[iGe
].argvIndex
= iNext
++;
200 pInfo
->estimatedCost
/= 2;
203 pInfo
->idxNum
+= FTS4AUX_LE_CONSTRAINT
;
204 pInfo
->aConstraintUsage
[iLe
].argvIndex
= iNext
++;
205 pInfo
->estimatedCost
/= 2;
209 pInfo
->aConstraintUsage
[iLangid
].argvIndex
= iNext
++;
210 pInfo
->estimatedCost
--;
217 ** xOpen - Open a cursor.
219 static int fts3auxOpenMethod(sqlite3_vtab
*pVTab
, sqlite3_vtab_cursor
**ppCsr
){
220 Fts3auxCursor
*pCsr
; /* Pointer to cursor object to return */
222 UNUSED_PARAMETER(pVTab
);
224 pCsr
= (Fts3auxCursor
*)sqlite3_malloc(sizeof(Fts3auxCursor
));
225 if( !pCsr
) return SQLITE_NOMEM
;
226 memset(pCsr
, 0, sizeof(Fts3auxCursor
));
228 *ppCsr
= (sqlite3_vtab_cursor
*)pCsr
;
233 ** xClose - Close a cursor.
235 static int fts3auxCloseMethod(sqlite3_vtab_cursor
*pCursor
){
236 Fts3Table
*pFts3
= ((Fts3auxTable
*)pCursor
->pVtab
)->pFts3Tab
;
237 Fts3auxCursor
*pCsr
= (Fts3auxCursor
*)pCursor
;
239 sqlite3Fts3SegmentsClose(pFts3
);
240 sqlite3Fts3SegReaderFinish(&pCsr
->csr
);
241 sqlite3_free((void *)pCsr
->filter
.zTerm
);
242 sqlite3_free(pCsr
->zStop
);
243 sqlite3_free(pCsr
->aStat
);
248 static int fts3auxGrowStatArray(Fts3auxCursor
*pCsr
, int nSize
){
249 if( nSize
>pCsr
->nStat
){
250 struct Fts3auxColstats
*aNew
;
251 aNew
= (struct Fts3auxColstats
*)sqlite3_realloc64(pCsr
->aStat
,
252 sizeof(struct Fts3auxColstats
) * nSize
254 if( aNew
==0 ) return SQLITE_NOMEM
;
255 memset(&aNew
[pCsr
->nStat
], 0,
256 sizeof(struct Fts3auxColstats
) * (nSize
- pCsr
->nStat
)
265 ** xNext - Advance the cursor to the next row, if any.
267 static int fts3auxNextMethod(sqlite3_vtab_cursor
*pCursor
){
268 Fts3auxCursor
*pCsr
= (Fts3auxCursor
*)pCursor
;
269 Fts3Table
*pFts3
= ((Fts3auxTable
*)pCursor
->pVtab
)->pFts3Tab
;
272 /* Increment our pretend rowid value. */
275 for(pCsr
->iCol
++; pCsr
->iCol
<pCsr
->nStat
; pCsr
->iCol
++){
276 if( pCsr
->aStat
[pCsr
->iCol
].nDoc
>0 ) return SQLITE_OK
;
279 rc
= sqlite3Fts3SegReaderStep(pFts3
, &pCsr
->csr
);
280 if( rc
==SQLITE_ROW
){
282 int nDoclist
= pCsr
->csr
.nDoclist
;
283 char *aDoclist
= pCsr
->csr
.aDoclist
;
289 int n
= (pCsr
->nStop
<pCsr
->csr
.nTerm
) ? pCsr
->nStop
: pCsr
->csr
.nTerm
;
290 int mc
= memcmp(pCsr
->zStop
, pCsr
->csr
.zTerm
, n
);
291 if( mc
<0 || (mc
==0 && pCsr
->csr
.nTerm
>pCsr
->nStop
) ){
297 if( fts3auxGrowStatArray(pCsr
, 2) ) return SQLITE_NOMEM
;
298 memset(pCsr
->aStat
, 0, sizeof(struct Fts3auxColstats
) * pCsr
->nStat
);
305 i
+= sqlite3Fts3GetVarint(&aDoclist
[i
], &v
);
307 /* State 0. In this state the integer just read was a docid. */
309 pCsr
->aStat
[0].nDoc
++;
314 /* State 1. In this state we are expecting either a 1, indicating
315 ** that the following integer will be a column number, or the
316 ** start of a position list for column 0.
318 ** The only difference between state 1 and state 2 is that if the
319 ** integer encountered in state 1 is not 0 or 1, then we need to
320 ** increment the column 0 "nDoc" count for this term.
325 pCsr
->aStat
[1].nDoc
++;
331 if( v
==0 ){ /* 0x00. Next integer will be a docid. */
333 }else if( v
==1 ){ /* 0x01. Next integer will be a column number. */
335 }else{ /* 2 or greater. A position. */
336 pCsr
->aStat
[iCol
+1].nOcc
++;
337 pCsr
->aStat
[0].nOcc
++;
341 /* State 3. The integer just read is a column number. */
342 default: assert( eState
==3 );
345 rc
= SQLITE_CORRUPT_VTAB
;
348 if( fts3auxGrowStatArray(pCsr
, iCol
+2) ) return SQLITE_NOMEM
;
349 pCsr
->aStat
[iCol
+1].nDoc
++;
363 ** xFilter - Initialize a cursor to point at the start of its data.
365 static int fts3auxFilterMethod(
366 sqlite3_vtab_cursor
*pCursor
, /* The cursor used for this query */
367 int idxNum
, /* Strategy index */
368 const char *idxStr
, /* Unused */
369 int nVal
, /* Number of elements in apVal */
370 sqlite3_value
**apVal
/* Arguments for the indexing scheme */
372 Fts3auxCursor
*pCsr
= (Fts3auxCursor
*)pCursor
;
373 Fts3Table
*pFts3
= ((Fts3auxTable
*)pCursor
->pVtab
)->pFts3Tab
;
376 int iLangVal
= 0; /* Language id to query */
378 int iEq
= -1; /* Index of term=? value in apVal */
379 int iGe
= -1; /* Index of term>=? value in apVal */
380 int iLe
= -1; /* Index of term<=? value in apVal */
381 int iLangid
= -1; /* Index of languageid=? value in apVal */
384 UNUSED_PARAMETER(nVal
);
385 UNUSED_PARAMETER(idxStr
);
388 assert( idxNum
==FTS4AUX_EQ_CONSTRAINT
|| idxNum
==0
389 || idxNum
==FTS4AUX_LE_CONSTRAINT
|| idxNum
==FTS4AUX_GE_CONSTRAINT
390 || idxNum
==(FTS4AUX_LE_CONSTRAINT
|FTS4AUX_GE_CONSTRAINT
)
393 if( idxNum
==FTS4AUX_EQ_CONSTRAINT
){
397 if( idxNum
& FTS4AUX_GE_CONSTRAINT
){
400 if( idxNum
& FTS4AUX_LE_CONSTRAINT
){
408 /* In case this cursor is being reused, close and zero it. */
409 testcase(pCsr
->filter
.zTerm
);
410 sqlite3Fts3SegReaderFinish(&pCsr
->csr
);
411 sqlite3_free((void *)pCsr
->filter
.zTerm
);
412 sqlite3_free(pCsr
->aStat
);
413 sqlite3_free(pCsr
->zStop
);
414 memset(&pCsr
->csr
, 0, ((u8
*)&pCsr
[1]) - (u8
*)&pCsr
->csr
);
416 pCsr
->filter
.flags
= FTS3_SEGMENT_REQUIRE_POS
|FTS3_SEGMENT_IGNORE_EMPTY
;
417 if( isScan
) pCsr
->filter
.flags
|= FTS3_SEGMENT_SCAN
;
419 if( iEq
>=0 || iGe
>=0 ){
420 const unsigned char *zStr
= sqlite3_value_text(apVal
[0]);
421 assert( (iEq
==0 && iGe
==-1) || (iEq
==-1 && iGe
==0) );
423 pCsr
->filter
.zTerm
= sqlite3_mprintf("%s", zStr
);
424 if( pCsr
->filter
.zTerm
==0 ) return SQLITE_NOMEM
;
425 pCsr
->filter
.nTerm
= (int)strlen(pCsr
->filter
.zTerm
);
430 pCsr
->zStop
= sqlite3_mprintf("%s", sqlite3_value_text(apVal
[iLe
]));
431 if( pCsr
->zStop
==0 ) return SQLITE_NOMEM
;
432 pCsr
->nStop
= (int)strlen(pCsr
->zStop
);
436 iLangVal
= sqlite3_value_int(apVal
[iLangid
]);
438 /* If the user specified a negative value for the languageid, use zero
439 ** instead. This works, as the "languageid=?" constraint will also
440 ** be tested by the VDBE layer. The test will always be false (since
441 ** this module will not return a row with a negative languageid), and
442 ** so the overall query will return zero rows. */
443 if( iLangVal
<0 ) iLangVal
= 0;
445 pCsr
->iLangid
= iLangVal
;
447 rc
= sqlite3Fts3SegReaderCursor(pFts3
, iLangVal
, 0, FTS3_SEGCURSOR_ALL
,
448 pCsr
->filter
.zTerm
, pCsr
->filter
.nTerm
, 0, isScan
, &pCsr
->csr
451 rc
= sqlite3Fts3SegReaderStart(pFts3
, &pCsr
->csr
, &pCsr
->filter
);
454 if( rc
==SQLITE_OK
) rc
= fts3auxNextMethod(pCursor
);
459 ** xEof - Return true if the cursor is at EOF, or false otherwise.
461 static int fts3auxEofMethod(sqlite3_vtab_cursor
*pCursor
){
462 Fts3auxCursor
*pCsr
= (Fts3auxCursor
*)pCursor
;
467 ** xColumn - Return a column value.
469 static int fts3auxColumnMethod(
470 sqlite3_vtab_cursor
*pCursor
, /* Cursor to retrieve value from */
471 sqlite3_context
*pCtx
, /* Context for sqlite3_result_xxx() calls */
472 int iCol
/* Index of column to read value from */
474 Fts3auxCursor
*p
= (Fts3auxCursor
*)pCursor
;
476 assert( p
->isEof
==0 );
479 sqlite3_result_text(pCtx
, p
->csr
.zTerm
, p
->csr
.nTerm
, SQLITE_TRANSIENT
);
484 sqlite3_result_int(pCtx
, p
->iCol
-1);
486 sqlite3_result_text(pCtx
, "*", -1, SQLITE_STATIC
);
490 case 2: /* documents */
491 sqlite3_result_int64(pCtx
, p
->aStat
[p
->iCol
].nDoc
);
494 case 3: /* occurrences */
495 sqlite3_result_int64(pCtx
, p
->aStat
[p
->iCol
].nOcc
);
498 default: /* languageid */
500 sqlite3_result_int(pCtx
, p
->iLangid
);
508 ** xRowid - Return the current rowid for the cursor.
510 static int fts3auxRowidMethod(
511 sqlite3_vtab_cursor
*pCursor
, /* Cursor to retrieve value from */
512 sqlite_int64
*pRowid
/* OUT: Rowid value */
514 Fts3auxCursor
*pCsr
= (Fts3auxCursor
*)pCursor
;
515 *pRowid
= pCsr
->iRowid
;
520 ** Register the fts3aux module with database connection db. Return SQLITE_OK
521 ** if successful or an error code if sqlite3_create_module() fails.
523 int sqlite3Fts3InitAux(sqlite3
*db
){
524 static const sqlite3_module fts3aux_module
= {
526 fts3auxConnectMethod
, /* xCreate */
527 fts3auxConnectMethod
, /* xConnect */
528 fts3auxBestIndexMethod
, /* xBestIndex */
529 fts3auxDisconnectMethod
, /* xDisconnect */
530 fts3auxDisconnectMethod
, /* xDestroy */
531 fts3auxOpenMethod
, /* xOpen */
532 fts3auxCloseMethod
, /* xClose */
533 fts3auxFilterMethod
, /* xFilter */
534 fts3auxNextMethod
, /* xNext */
535 fts3auxEofMethod
, /* xEof */
536 fts3auxColumnMethod
, /* xColumn */
537 fts3auxRowidMethod
, /* xRowid */
543 0, /* xFindFunction */
551 int rc
; /* Return code */
553 rc
= sqlite3_create_module(db
, "fts4aux", &fts3aux_module
, 0);
557 #endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3) */