OLD | NEW |
| (Empty) |
1 /* | |
2 ** 2014 Jun 09 | |
3 ** | |
4 ** The author disclaims copyright to this source code. In place of | |
5 ** a legal notice, here is a blessing: | |
6 ** | |
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. | |
10 ** | |
11 ****************************************************************************** | |
12 ** | |
13 ** This is an SQLite module implementing full-text search. | |
14 */ | |
15 | |
16 | |
17 #include "fts5Int.h" | |
18 | |
19 /* | |
20 ** This variable is set to false when running tests for which the on disk | |
21 ** structures should not be corrupt. Otherwise, true. If it is false, extra | |
22 ** assert() conditions in the fts5 code are activated - conditions that are | |
23 ** only true if it is guaranteed that the fts5 database is not corrupt. | |
24 */ | |
25 int sqlite3_fts5_may_be_corrupt = 1; | |
26 | |
27 | |
28 typedef struct Fts5Auxdata Fts5Auxdata; | |
29 typedef struct Fts5Auxiliary Fts5Auxiliary; | |
30 typedef struct Fts5Cursor Fts5Cursor; | |
31 typedef struct Fts5Sorter Fts5Sorter; | |
32 typedef struct Fts5Table Fts5Table; | |
33 typedef struct Fts5TokenizerModule Fts5TokenizerModule; | |
34 | |
35 /* | |
36 ** NOTES ON TRANSACTIONS: | |
37 ** | |
38 ** SQLite invokes the following virtual table methods as transactions are | |
39 ** opened and closed by the user: | |
40 ** | |
41 ** xBegin(): Start of a new transaction. | |
42 ** xSync(): Initial part of two-phase commit. | |
43 ** xCommit(): Final part of two-phase commit. | |
44 ** xRollback(): Rollback the transaction. | |
45 ** | |
46 ** Anything that is required as part of a commit that may fail is performed | |
47 ** in the xSync() callback. Current versions of SQLite ignore any errors | |
48 ** returned by xCommit(). | |
49 ** | |
50 ** And as sub-transactions are opened/closed: | |
51 ** | |
52 ** xSavepoint(int S): Open savepoint S. | |
53 ** xRelease(int S): Commit and close savepoint S. | |
54 ** xRollbackTo(int S): Rollback to start of savepoint S. | |
55 ** | |
56 ** During a write-transaction the fts5_index.c module may cache some data | |
57 ** in-memory. It is flushed to disk whenever xSync(), xRelease() or | |
58 ** xSavepoint() is called. And discarded whenever xRollback() or xRollbackTo() | |
59 ** is called. | |
60 ** | |
61 ** Additionally, if SQLITE_DEBUG is defined, an instance of the following | |
62 ** structure is used to record the current transaction state. This information | |
63 ** is not required, but it is used in the assert() statements executed by | |
64 ** function fts5CheckTransactionState() (see below). | |
65 */ | |
66 struct Fts5TransactionState { | |
67 int eState; /* 0==closed, 1==open, 2==synced */ | |
68 int iSavepoint; /* Number of open savepoints (0 -> none) */ | |
69 }; | |
70 | |
71 /* | |
72 ** A single object of this type is allocated when the FTS5 module is | |
73 ** registered with a database handle. It is used to store pointers to | |
74 ** all registered FTS5 extensions - tokenizers and auxiliary functions. | |
75 */ | |
76 struct Fts5Global { | |
77 fts5_api api; /* User visible part of object (see fts5.h) */ | |
78 sqlite3 *db; /* Associated database connection */ | |
79 i64 iNextId; /* Used to allocate unique cursor ids */ | |
80 Fts5Auxiliary *pAux; /* First in list of all aux. functions */ | |
81 Fts5TokenizerModule *pTok; /* First in list of all tokenizer modules */ | |
82 Fts5TokenizerModule *pDfltTok; /* Default tokenizer module */ | |
83 Fts5Cursor *pCsr; /* First in list of all open cursors */ | |
84 }; | |
85 | |
86 /* | |
87 ** Each auxiliary function registered with the FTS5 module is represented | |
88 ** by an object of the following type. All such objects are stored as part | |
89 ** of the Fts5Global.pAux list. | |
90 */ | |
91 struct Fts5Auxiliary { | |
92 Fts5Global *pGlobal; /* Global context for this function */ | |
93 char *zFunc; /* Function name (nul-terminated) */ | |
94 void *pUserData; /* User-data pointer */ | |
95 fts5_extension_function xFunc; /* Callback function */ | |
96 void (*xDestroy)(void*); /* Destructor function */ | |
97 Fts5Auxiliary *pNext; /* Next registered auxiliary function */ | |
98 }; | |
99 | |
100 /* | |
101 ** Each tokenizer module registered with the FTS5 module is represented | |
102 ** by an object of the following type. All such objects are stored as part | |
103 ** of the Fts5Global.pTok list. | |
104 */ | |
105 struct Fts5TokenizerModule { | |
106 char *zName; /* Name of tokenizer */ | |
107 void *pUserData; /* User pointer passed to xCreate() */ | |
108 fts5_tokenizer x; /* Tokenizer functions */ | |
109 void (*xDestroy)(void*); /* Destructor function */ | |
110 Fts5TokenizerModule *pNext; /* Next registered tokenizer module */ | |
111 }; | |
112 | |
113 /* | |
114 ** Virtual-table object. | |
115 */ | |
116 struct Fts5Table { | |
117 sqlite3_vtab base; /* Base class used by SQLite core */ | |
118 Fts5Config *pConfig; /* Virtual table configuration */ | |
119 Fts5Index *pIndex; /* Full-text index */ | |
120 Fts5Storage *pStorage; /* Document store */ | |
121 Fts5Global *pGlobal; /* Global (connection wide) data */ | |
122 Fts5Cursor *pSortCsr; /* Sort data from this cursor */ | |
123 #ifdef SQLITE_DEBUG | |
124 struct Fts5TransactionState ts; | |
125 #endif | |
126 }; | |
127 | |
128 struct Fts5MatchPhrase { | |
129 Fts5Buffer *pPoslist; /* Pointer to current poslist */ | |
130 int nTerm; /* Size of phrase in terms */ | |
131 }; | |
132 | |
133 /* | |
134 ** pStmt: | |
135 ** SELECT rowid, <fts> FROM <fts> ORDER BY +rank; | |
136 ** | |
137 ** aIdx[]: | |
138 ** There is one entry in the aIdx[] array for each phrase in the query, | |
139 ** the value of which is the offset within aPoslist[] following the last | |
140 ** byte of the position list for the corresponding phrase. | |
141 */ | |
142 struct Fts5Sorter { | |
143 sqlite3_stmt *pStmt; | |
144 i64 iRowid; /* Current rowid */ | |
145 const u8 *aPoslist; /* Position lists for current row */ | |
146 int nIdx; /* Number of entries in aIdx[] */ | |
147 int aIdx[1]; /* Offsets into aPoslist for current row */ | |
148 }; | |
149 | |
150 | |
151 /* | |
152 ** Virtual-table cursor object. | |
153 ** | |
154 ** iSpecial: | |
155 ** If this is a 'special' query (refer to function fts5SpecialMatch()), | |
156 ** then this variable contains the result of the query. | |
157 ** | |
158 ** iFirstRowid, iLastRowid: | |
159 ** These variables are only used for FTS5_PLAN_MATCH cursors. Assuming the | |
160 ** cursor iterates in ascending order of rowids, iFirstRowid is the lower | |
161 ** limit of rowids to return, and iLastRowid the upper. In other words, the | |
162 ** WHERE clause in the user's query might have been: | |
163 ** | |
164 ** <tbl> MATCH <expr> AND rowid BETWEEN $iFirstRowid AND $iLastRowid | |
165 ** | |
166 ** If the cursor iterates in descending order of rowid, iFirstRowid | |
167 ** is the upper limit (i.e. the "first" rowid visited) and iLastRowid | |
168 ** the lower. | |
169 */ | |
170 struct Fts5Cursor { | |
171 sqlite3_vtab_cursor base; /* Base class used by SQLite core */ | |
172 Fts5Cursor *pNext; /* Next cursor in Fts5Cursor.pCsr list */ | |
173 int *aColumnSize; /* Values for xColumnSize() */ | |
174 i64 iCsrId; /* Cursor id */ | |
175 | |
176 /* Zero from this point onwards on cursor reset */ | |
177 int ePlan; /* FTS5_PLAN_XXX value */ | |
178 int bDesc; /* True for "ORDER BY rowid DESC" queries */ | |
179 i64 iFirstRowid; /* Return no rowids earlier than this */ | |
180 i64 iLastRowid; /* Return no rowids later than this */ | |
181 sqlite3_stmt *pStmt; /* Statement used to read %_content */ | |
182 Fts5Expr *pExpr; /* Expression for MATCH queries */ | |
183 Fts5Sorter *pSorter; /* Sorter for "ORDER BY rank" queries */ | |
184 int csrflags; /* Mask of cursor flags (see below) */ | |
185 i64 iSpecial; /* Result of special query */ | |
186 | |
187 /* "rank" function. Populated on demand from vtab.xColumn(). */ | |
188 char *zRank; /* Custom rank function */ | |
189 char *zRankArgs; /* Custom rank function args */ | |
190 Fts5Auxiliary *pRank; /* Rank callback (or NULL) */ | |
191 int nRankArg; /* Number of trailing arguments for rank() */ | |
192 sqlite3_value **apRankArg; /* Array of trailing arguments */ | |
193 sqlite3_stmt *pRankArgStmt; /* Origin of objects in apRankArg[] */ | |
194 | |
195 /* Auxiliary data storage */ | |
196 Fts5Auxiliary *pAux; /* Currently executing extension function */ | |
197 Fts5Auxdata *pAuxdata; /* First in linked list of saved aux-data */ | |
198 | |
199 /* Cache used by auxiliary functions xInst() and xInstCount() */ | |
200 Fts5PoslistReader *aInstIter; /* One for each phrase */ | |
201 int nInstAlloc; /* Size of aInst[] array (entries / 3) */ | |
202 int nInstCount; /* Number of phrase instances */ | |
203 int *aInst; /* 3 integers per phrase instance */ | |
204 }; | |
205 | |
206 /* | |
207 ** Bits that make up the "idxNum" parameter passed indirectly by | |
208 ** xBestIndex() to xFilter(). | |
209 */ | |
210 #define FTS5_BI_MATCH 0x0001 /* <tbl> MATCH ? */ | |
211 #define FTS5_BI_RANK 0x0002 /* rank MATCH ? */ | |
212 #define FTS5_BI_ROWID_EQ 0x0004 /* rowid == ? */ | |
213 #define FTS5_BI_ROWID_LE 0x0008 /* rowid <= ? */ | |
214 #define FTS5_BI_ROWID_GE 0x0010 /* rowid >= ? */ | |
215 | |
216 #define FTS5_BI_ORDER_RANK 0x0020 | |
217 #define FTS5_BI_ORDER_ROWID 0x0040 | |
218 #define FTS5_BI_ORDER_DESC 0x0080 | |
219 | |
220 /* | |
221 ** Values for Fts5Cursor.csrflags | |
222 */ | |
223 #define FTS5CSR_REQUIRE_CONTENT 0x01 | |
224 #define FTS5CSR_REQUIRE_DOCSIZE 0x02 | |
225 #define FTS5CSR_REQUIRE_INST 0x04 | |
226 #define FTS5CSR_EOF 0x08 | |
227 #define FTS5CSR_FREE_ZRANK 0x10 | |
228 #define FTS5CSR_REQUIRE_RESEEK 0x20 | |
229 | |
230 #define BitFlagAllTest(x,y) (((x) & (y))==(y)) | |
231 #define BitFlagTest(x,y) (((x) & (y))!=0) | |
232 | |
233 | |
234 /* | |
235 ** Macros to Set(), Clear() and Test() cursor flags. | |
236 */ | |
237 #define CsrFlagSet(pCsr, flag) ((pCsr)->csrflags |= (flag)) | |
238 #define CsrFlagClear(pCsr, flag) ((pCsr)->csrflags &= ~(flag)) | |
239 #define CsrFlagTest(pCsr, flag) ((pCsr)->csrflags & (flag)) | |
240 | |
241 struct Fts5Auxdata { | |
242 Fts5Auxiliary *pAux; /* Extension to which this belongs */ | |
243 void *pPtr; /* Pointer value */ | |
244 void(*xDelete)(void*); /* Destructor */ | |
245 Fts5Auxdata *pNext; /* Next object in linked list */ | |
246 }; | |
247 | |
248 #ifdef SQLITE_DEBUG | |
249 #define FTS5_BEGIN 1 | |
250 #define FTS5_SYNC 2 | |
251 #define FTS5_COMMIT 3 | |
252 #define FTS5_ROLLBACK 4 | |
253 #define FTS5_SAVEPOINT 5 | |
254 #define FTS5_RELEASE 6 | |
255 #define FTS5_ROLLBACKTO 7 | |
256 static void fts5CheckTransactionState(Fts5Table *p, int op, int iSavepoint){ | |
257 switch( op ){ | |
258 case FTS5_BEGIN: | |
259 assert( p->ts.eState==0 ); | |
260 p->ts.eState = 1; | |
261 p->ts.iSavepoint = -1; | |
262 break; | |
263 | |
264 case FTS5_SYNC: | |
265 assert( p->ts.eState==1 ); | |
266 p->ts.eState = 2; | |
267 break; | |
268 | |
269 case FTS5_COMMIT: | |
270 assert( p->ts.eState==2 ); | |
271 p->ts.eState = 0; | |
272 break; | |
273 | |
274 case FTS5_ROLLBACK: | |
275 assert( p->ts.eState==1 || p->ts.eState==2 || p->ts.eState==0 ); | |
276 p->ts.eState = 0; | |
277 break; | |
278 | |
279 case FTS5_SAVEPOINT: | |
280 assert( p->ts.eState==1 ); | |
281 assert( iSavepoint>=0 ); | |
282 assert( iSavepoint>p->ts.iSavepoint ); | |
283 p->ts.iSavepoint = iSavepoint; | |
284 break; | |
285 | |
286 case FTS5_RELEASE: | |
287 assert( p->ts.eState==1 ); | |
288 assert( iSavepoint>=0 ); | |
289 assert( iSavepoint<=p->ts.iSavepoint ); | |
290 p->ts.iSavepoint = iSavepoint-1; | |
291 break; | |
292 | |
293 case FTS5_ROLLBACKTO: | |
294 assert( p->ts.eState==1 ); | |
295 assert( iSavepoint>=0 ); | |
296 assert( iSavepoint<=p->ts.iSavepoint ); | |
297 p->ts.iSavepoint = iSavepoint; | |
298 break; | |
299 } | |
300 } | |
301 #else | |
302 # define fts5CheckTransactionState(x,y,z) | |
303 #endif | |
304 | |
305 /* | |
306 ** Return true if pTab is a contentless table. | |
307 */ | |
308 static int fts5IsContentless(Fts5Table *pTab){ | |
309 return pTab->pConfig->eContent==FTS5_CONTENT_NONE; | |
310 } | |
311 | |
312 /* | |
313 ** Delete a virtual table handle allocated by fts5InitVtab(). | |
314 */ | |
315 static void fts5FreeVtab(Fts5Table *pTab){ | |
316 if( pTab ){ | |
317 sqlite3Fts5IndexClose(pTab->pIndex); | |
318 sqlite3Fts5StorageClose(pTab->pStorage); | |
319 sqlite3Fts5ConfigFree(pTab->pConfig); | |
320 sqlite3_free(pTab); | |
321 } | |
322 } | |
323 | |
324 /* | |
325 ** The xDisconnect() virtual table method. | |
326 */ | |
327 static int fts5DisconnectMethod(sqlite3_vtab *pVtab){ | |
328 fts5FreeVtab((Fts5Table*)pVtab); | |
329 return SQLITE_OK; | |
330 } | |
331 | |
332 /* | |
333 ** The xDestroy() virtual table method. | |
334 */ | |
335 static int fts5DestroyMethod(sqlite3_vtab *pVtab){ | |
336 Fts5Table *pTab = (Fts5Table*)pVtab; | |
337 int rc = sqlite3Fts5DropAll(pTab->pConfig); | |
338 if( rc==SQLITE_OK ){ | |
339 fts5FreeVtab((Fts5Table*)pVtab); | |
340 } | |
341 return rc; | |
342 } | |
343 | |
344 /* | |
345 ** This function is the implementation of both the xConnect and xCreate | |
346 ** methods of the FTS3 virtual table. | |
347 ** | |
348 ** The argv[] array contains the following: | |
349 ** | |
350 ** argv[0] -> module name ("fts5") | |
351 ** argv[1] -> database name | |
352 ** argv[2] -> table name | |
353 ** argv[...] -> "column name" and other module argument fields. | |
354 */ | |
355 static int fts5InitVtab( | |
356 int bCreate, /* True for xCreate, false for xConnect */ | |
357 sqlite3 *db, /* The SQLite database connection */ | |
358 void *pAux, /* Hash table containing tokenizers */ | |
359 int argc, /* Number of elements in argv array */ | |
360 const char * const *argv, /* xCreate/xConnect argument array */ | |
361 sqlite3_vtab **ppVTab, /* Write the resulting vtab structure here */ | |
362 char **pzErr /* Write any error message here */ | |
363 ){ | |
364 Fts5Global *pGlobal = (Fts5Global*)pAux; | |
365 const char **azConfig = (const char**)argv; | |
366 int rc = SQLITE_OK; /* Return code */ | |
367 Fts5Config *pConfig = 0; /* Results of parsing argc/argv */ | |
368 Fts5Table *pTab = 0; /* New virtual table object */ | |
369 | |
370 /* Allocate the new vtab object and parse the configuration */ | |
371 pTab = (Fts5Table*)sqlite3Fts5MallocZero(&rc, sizeof(Fts5Table)); | |
372 if( rc==SQLITE_OK ){ | |
373 rc = sqlite3Fts5ConfigParse(pGlobal, db, argc, azConfig, &pConfig, pzErr); | |
374 assert( (rc==SQLITE_OK && *pzErr==0) || pConfig==0 ); | |
375 } | |
376 if( rc==SQLITE_OK ){ | |
377 pTab->pConfig = pConfig; | |
378 pTab->pGlobal = pGlobal; | |
379 } | |
380 | |
381 /* Open the index sub-system */ | |
382 if( rc==SQLITE_OK ){ | |
383 rc = sqlite3Fts5IndexOpen(pConfig, bCreate, &pTab->pIndex, pzErr); | |
384 } | |
385 | |
386 /* Open the storage sub-system */ | |
387 if( rc==SQLITE_OK ){ | |
388 rc = sqlite3Fts5StorageOpen( | |
389 pConfig, pTab->pIndex, bCreate, &pTab->pStorage, pzErr | |
390 ); | |
391 } | |
392 | |
393 /* Call sqlite3_declare_vtab() */ | |
394 if( rc==SQLITE_OK ){ | |
395 rc = sqlite3Fts5ConfigDeclareVtab(pConfig); | |
396 } | |
397 | |
398 /* Load the initial configuration */ | |
399 if( rc==SQLITE_OK ){ | |
400 assert( pConfig->pzErrmsg==0 ); | |
401 pConfig->pzErrmsg = pzErr; | |
402 rc = sqlite3Fts5IndexLoadConfig(pTab->pIndex); | |
403 sqlite3Fts5IndexRollback(pTab->pIndex); | |
404 pConfig->pzErrmsg = 0; | |
405 } | |
406 | |
407 if( rc!=SQLITE_OK ){ | |
408 fts5FreeVtab(pTab); | |
409 pTab = 0; | |
410 }else if( bCreate ){ | |
411 fts5CheckTransactionState(pTab, FTS5_BEGIN, 0); | |
412 } | |
413 *ppVTab = (sqlite3_vtab*)pTab; | |
414 return rc; | |
415 } | |
416 | |
417 /* | |
418 ** The xConnect() and xCreate() methods for the virtual table. All the | |
419 ** work is done in function fts5InitVtab(). | |
420 */ | |
421 static int fts5ConnectMethod( | |
422 sqlite3 *db, /* Database connection */ | |
423 void *pAux, /* Pointer to tokenizer hash table */ | |
424 int argc, /* Number of elements in argv array */ | |
425 const char * const *argv, /* xCreate/xConnect argument array */ | |
426 sqlite3_vtab **ppVtab, /* OUT: New sqlite3_vtab object */ | |
427 char **pzErr /* OUT: sqlite3_malloc'd error message */ | |
428 ){ | |
429 return fts5InitVtab(0, db, pAux, argc, argv, ppVtab, pzErr); | |
430 } | |
431 static int fts5CreateMethod( | |
432 sqlite3 *db, /* Database connection */ | |
433 void *pAux, /* Pointer to tokenizer hash table */ | |
434 int argc, /* Number of elements in argv array */ | |
435 const char * const *argv, /* xCreate/xConnect argument array */ | |
436 sqlite3_vtab **ppVtab, /* OUT: New sqlite3_vtab object */ | |
437 char **pzErr /* OUT: sqlite3_malloc'd error message */ | |
438 ){ | |
439 return fts5InitVtab(1, db, pAux, argc, argv, ppVtab, pzErr); | |
440 } | |
441 | |
442 /* | |
443 ** The different query plans. | |
444 */ | |
445 #define FTS5_PLAN_MATCH 1 /* (<tbl> MATCH ?) */ | |
446 #define FTS5_PLAN_SOURCE 2 /* A source cursor for SORTED_MATCH */ | |
447 #define FTS5_PLAN_SPECIAL 3 /* An internal query */ | |
448 #define FTS5_PLAN_SORTED_MATCH 4 /* (<tbl> MATCH ? ORDER BY rank) */ | |
449 #define FTS5_PLAN_SCAN 5 /* No usable constraint */ | |
450 #define FTS5_PLAN_ROWID 6 /* (rowid = ?) */ | |
451 | |
452 /* | |
453 ** Set the SQLITE_INDEX_SCAN_UNIQUE flag in pIdxInfo->flags. Unless this | |
454 ** extension is currently being used by a version of SQLite too old to | |
455 ** support index-info flags. In that case this function is a no-op. | |
456 */ | |
457 static void fts5SetUniqueFlag(sqlite3_index_info *pIdxInfo){ | |
458 #if SQLITE_VERSION_NUMBER>=3008012 | |
459 #ifndef SQLITE_CORE | |
460 if( sqlite3_libversion_number()>=3008012 ) | |
461 #endif | |
462 { | |
463 pIdxInfo->idxFlags |= SQLITE_INDEX_SCAN_UNIQUE; | |
464 } | |
465 #endif | |
466 } | |
467 | |
468 /* | |
469 ** Implementation of the xBestIndex method for FTS5 tables. Within the | |
470 ** WHERE constraint, it searches for the following: | |
471 ** | |
472 ** 1. A MATCH constraint against the special column. | |
473 ** 2. A MATCH constraint against the "rank" column. | |
474 ** 3. An == constraint against the rowid column. | |
475 ** 4. A < or <= constraint against the rowid column. | |
476 ** 5. A > or >= constraint against the rowid column. | |
477 ** | |
478 ** Within the ORDER BY, either: | |
479 ** | |
480 ** 5. ORDER BY rank [ASC|DESC] | |
481 ** 6. ORDER BY rowid [ASC|DESC] | |
482 ** | |
483 ** Costs are assigned as follows: | |
484 ** | |
485 ** a) If an unusable MATCH operator is present in the WHERE clause, the | |
486 ** cost is unconditionally set to 1e50 (a really big number). | |
487 ** | |
488 ** a) If a MATCH operator is present, the cost depends on the other | |
489 ** constraints also present. As follows: | |
490 ** | |
491 ** * No other constraints: cost=1000.0 | |
492 ** * One rowid range constraint: cost=750.0 | |
493 ** * Both rowid range constraints: cost=500.0 | |
494 ** * An == rowid constraint: cost=100.0 | |
495 ** | |
496 ** b) Otherwise, if there is no MATCH: | |
497 ** | |
498 ** * No other constraints: cost=1000000.0 | |
499 ** * One rowid range constraint: cost=750000.0 | |
500 ** * Both rowid range constraints: cost=250000.0 | |
501 ** * An == rowid constraint: cost=10.0 | |
502 ** | |
503 ** Costs are not modified by the ORDER BY clause. | |
504 */ | |
505 static int fts5BestIndexMethod(sqlite3_vtab *pVTab, sqlite3_index_info *pInfo){ | |
506 Fts5Table *pTab = (Fts5Table*)pVTab; | |
507 Fts5Config *pConfig = pTab->pConfig; | |
508 int idxFlags = 0; /* Parameter passed through to xFilter() */ | |
509 int bHasMatch; | |
510 int iNext; | |
511 int i; | |
512 | |
513 struct Constraint { | |
514 int op; /* Mask against sqlite3_index_constraint.op */ | |
515 int fts5op; /* FTS5 mask for idxFlags */ | |
516 int iCol; /* 0==rowid, 1==tbl, 2==rank */ | |
517 int omit; /* True to omit this if found */ | |
518 int iConsIndex; /* Index in pInfo->aConstraint[] */ | |
519 } aConstraint[] = { | |
520 {SQLITE_INDEX_CONSTRAINT_MATCH|SQLITE_INDEX_CONSTRAINT_EQ, | |
521 FTS5_BI_MATCH, 1, 1, -1}, | |
522 {SQLITE_INDEX_CONSTRAINT_MATCH|SQLITE_INDEX_CONSTRAINT_EQ, | |
523 FTS5_BI_RANK, 2, 1, -1}, | |
524 {SQLITE_INDEX_CONSTRAINT_EQ, FTS5_BI_ROWID_EQ, 0, 0, -1}, | |
525 {SQLITE_INDEX_CONSTRAINT_LT|SQLITE_INDEX_CONSTRAINT_LE, | |
526 FTS5_BI_ROWID_LE, 0, 0, -1}, | |
527 {SQLITE_INDEX_CONSTRAINT_GT|SQLITE_INDEX_CONSTRAINT_GE, | |
528 FTS5_BI_ROWID_GE, 0, 0, -1}, | |
529 }; | |
530 | |
531 int aColMap[3]; | |
532 aColMap[0] = -1; | |
533 aColMap[1] = pConfig->nCol; | |
534 aColMap[2] = pConfig->nCol+1; | |
535 | |
536 /* Set idxFlags flags for all WHERE clause terms that will be used. */ | |
537 for(i=0; i<pInfo->nConstraint; i++){ | |
538 struct sqlite3_index_constraint *p = &pInfo->aConstraint[i]; | |
539 int j; | |
540 for(j=0; j<(int)ArraySize(aConstraint); j++){ | |
541 struct Constraint *pC = &aConstraint[j]; | |
542 if( p->iColumn==aColMap[pC->iCol] && p->op & pC->op ){ | |
543 if( p->usable ){ | |
544 pC->iConsIndex = i; | |
545 idxFlags |= pC->fts5op; | |
546 }else if( j==0 ){ | |
547 /* As there exists an unusable MATCH constraint this is an | |
548 ** unusable plan. Set a prohibitively high cost. */ | |
549 pInfo->estimatedCost = 1e50; | |
550 return SQLITE_OK; | |
551 } | |
552 } | |
553 } | |
554 } | |
555 | |
556 /* Set idxFlags flags for the ORDER BY clause */ | |
557 if( pInfo->nOrderBy==1 ){ | |
558 int iSort = pInfo->aOrderBy[0].iColumn; | |
559 if( iSort==(pConfig->nCol+1) && BitFlagTest(idxFlags, FTS5_BI_MATCH) ){ | |
560 idxFlags |= FTS5_BI_ORDER_RANK; | |
561 }else if( iSort==-1 ){ | |
562 idxFlags |= FTS5_BI_ORDER_ROWID; | |
563 } | |
564 if( BitFlagTest(idxFlags, FTS5_BI_ORDER_RANK|FTS5_BI_ORDER_ROWID) ){ | |
565 pInfo->orderByConsumed = 1; | |
566 if( pInfo->aOrderBy[0].desc ){ | |
567 idxFlags |= FTS5_BI_ORDER_DESC; | |
568 } | |
569 } | |
570 } | |
571 | |
572 /* Calculate the estimated cost based on the flags set in idxFlags. */ | |
573 bHasMatch = BitFlagTest(idxFlags, FTS5_BI_MATCH); | |
574 if( BitFlagTest(idxFlags, FTS5_BI_ROWID_EQ) ){ | |
575 pInfo->estimatedCost = bHasMatch ? 100.0 : 10.0; | |
576 if( bHasMatch==0 ) fts5SetUniqueFlag(pInfo); | |
577 }else if( BitFlagAllTest(idxFlags, FTS5_BI_ROWID_LE|FTS5_BI_ROWID_GE) ){ | |
578 pInfo->estimatedCost = bHasMatch ? 500.0 : 250000.0; | |
579 }else if( BitFlagTest(idxFlags, FTS5_BI_ROWID_LE|FTS5_BI_ROWID_GE) ){ | |
580 pInfo->estimatedCost = bHasMatch ? 750.0 : 750000.0; | |
581 }else{ | |
582 pInfo->estimatedCost = bHasMatch ? 1000.0 : 1000000.0; | |
583 } | |
584 | |
585 /* Assign argvIndex values to each constraint in use. */ | |
586 iNext = 1; | |
587 for(i=0; i<(int)ArraySize(aConstraint); i++){ | |
588 struct Constraint *pC = &aConstraint[i]; | |
589 if( pC->iConsIndex>=0 ){ | |
590 pInfo->aConstraintUsage[pC->iConsIndex].argvIndex = iNext++; | |
591 pInfo->aConstraintUsage[pC->iConsIndex].omit = (unsigned char)pC->omit; | |
592 } | |
593 } | |
594 | |
595 pInfo->idxNum = idxFlags; | |
596 return SQLITE_OK; | |
597 } | |
598 | |
599 /* | |
600 ** Implementation of xOpen method. | |
601 */ | |
602 static int fts5OpenMethod(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCsr){ | |
603 Fts5Table *pTab = (Fts5Table*)pVTab; | |
604 Fts5Config *pConfig = pTab->pConfig; | |
605 Fts5Cursor *pCsr; /* New cursor object */ | |
606 int nByte; /* Bytes of space to allocate */ | |
607 int rc = SQLITE_OK; /* Return code */ | |
608 | |
609 nByte = sizeof(Fts5Cursor) + pConfig->nCol * sizeof(int); | |
610 pCsr = (Fts5Cursor*)sqlite3_malloc(nByte); | |
611 if( pCsr ){ | |
612 Fts5Global *pGlobal = pTab->pGlobal; | |
613 memset(pCsr, 0, nByte); | |
614 pCsr->aColumnSize = (int*)&pCsr[1]; | |
615 pCsr->pNext = pGlobal->pCsr; | |
616 pGlobal->pCsr = pCsr; | |
617 pCsr->iCsrId = ++pGlobal->iNextId; | |
618 }else{ | |
619 rc = SQLITE_NOMEM; | |
620 } | |
621 *ppCsr = (sqlite3_vtab_cursor*)pCsr; | |
622 return rc; | |
623 } | |
624 | |
625 static int fts5StmtType(Fts5Cursor *pCsr){ | |
626 if( pCsr->ePlan==FTS5_PLAN_SCAN ){ | |
627 return (pCsr->bDesc) ? FTS5_STMT_SCAN_DESC : FTS5_STMT_SCAN_ASC; | |
628 } | |
629 return FTS5_STMT_LOOKUP; | |
630 } | |
631 | |
632 /* | |
633 ** This function is called after the cursor passed as the only argument | |
634 ** is moved to point at a different row. It clears all cached data | |
635 ** specific to the previous row stored by the cursor object. | |
636 */ | |
637 static void fts5CsrNewrow(Fts5Cursor *pCsr){ | |
638 CsrFlagSet(pCsr, | |
639 FTS5CSR_REQUIRE_CONTENT | |
640 | FTS5CSR_REQUIRE_DOCSIZE | |
641 | FTS5CSR_REQUIRE_INST | |
642 ); | |
643 } | |
644 | |
645 static void fts5FreeCursorComponents(Fts5Cursor *pCsr){ | |
646 Fts5Table *pTab = (Fts5Table*)(pCsr->base.pVtab); | |
647 Fts5Auxdata *pData; | |
648 Fts5Auxdata *pNext; | |
649 | |
650 sqlite3_free(pCsr->aInstIter); | |
651 sqlite3_free(pCsr->aInst); | |
652 if( pCsr->pStmt ){ | |
653 int eStmt = fts5StmtType(pCsr); | |
654 sqlite3Fts5StorageStmtRelease(pTab->pStorage, eStmt, pCsr->pStmt); | |
655 } | |
656 if( pCsr->pSorter ){ | |
657 Fts5Sorter *pSorter = pCsr->pSorter; | |
658 sqlite3_finalize(pSorter->pStmt); | |
659 sqlite3_free(pSorter); | |
660 } | |
661 | |
662 if( pCsr->ePlan!=FTS5_PLAN_SOURCE ){ | |
663 sqlite3Fts5ExprFree(pCsr->pExpr); | |
664 } | |
665 | |
666 for(pData=pCsr->pAuxdata; pData; pData=pNext){ | |
667 pNext = pData->pNext; | |
668 if( pData->xDelete ) pData->xDelete(pData->pPtr); | |
669 sqlite3_free(pData); | |
670 } | |
671 | |
672 sqlite3_finalize(pCsr->pRankArgStmt); | |
673 sqlite3_free(pCsr->apRankArg); | |
674 | |
675 if( CsrFlagTest(pCsr, FTS5CSR_FREE_ZRANK) ){ | |
676 sqlite3_free(pCsr->zRank); | |
677 sqlite3_free(pCsr->zRankArgs); | |
678 } | |
679 | |
680 memset(&pCsr->ePlan, 0, sizeof(Fts5Cursor) - ((u8*)&pCsr->ePlan - (u8*)pCsr)); | |
681 } | |
682 | |
683 | |
684 /* | |
685 ** Close the cursor. For additional information see the documentation | |
686 ** on the xClose method of the virtual table interface. | |
687 */ | |
688 static int fts5CloseMethod(sqlite3_vtab_cursor *pCursor){ | |
689 if( pCursor ){ | |
690 Fts5Table *pTab = (Fts5Table*)(pCursor->pVtab); | |
691 Fts5Cursor *pCsr = (Fts5Cursor*)pCursor; | |
692 Fts5Cursor **pp; | |
693 | |
694 fts5FreeCursorComponents(pCsr); | |
695 /* Remove the cursor from the Fts5Global.pCsr list */ | |
696 for(pp=&pTab->pGlobal->pCsr; (*pp)!=pCsr; pp=&(*pp)->pNext); | |
697 *pp = pCsr->pNext; | |
698 | |
699 sqlite3_free(pCsr); | |
700 } | |
701 return SQLITE_OK; | |
702 } | |
703 | |
704 static int fts5SorterNext(Fts5Cursor *pCsr){ | |
705 Fts5Sorter *pSorter = pCsr->pSorter; | |
706 int rc; | |
707 | |
708 rc = sqlite3_step(pSorter->pStmt); | |
709 if( rc==SQLITE_DONE ){ | |
710 rc = SQLITE_OK; | |
711 CsrFlagSet(pCsr, FTS5CSR_EOF); | |
712 }else if( rc==SQLITE_ROW ){ | |
713 const u8 *a; | |
714 const u8 *aBlob; | |
715 int nBlob; | |
716 int i; | |
717 int iOff = 0; | |
718 rc = SQLITE_OK; | |
719 | |
720 pSorter->iRowid = sqlite3_column_int64(pSorter->pStmt, 0); | |
721 nBlob = sqlite3_column_bytes(pSorter->pStmt, 1); | |
722 aBlob = a = sqlite3_column_blob(pSorter->pStmt, 1); | |
723 | |
724 for(i=0; i<(pSorter->nIdx-1); i++){ | |
725 int iVal; | |
726 a += fts5GetVarint32(a, iVal); | |
727 iOff += iVal; | |
728 pSorter->aIdx[i] = iOff; | |
729 } | |
730 pSorter->aIdx[i] = &aBlob[nBlob] - a; | |
731 | |
732 pSorter->aPoslist = a; | |
733 fts5CsrNewrow(pCsr); | |
734 } | |
735 | |
736 return rc; | |
737 } | |
738 | |
739 | |
740 /* | |
741 ** Set the FTS5CSR_REQUIRE_RESEEK flag on all FTS5_PLAN_MATCH cursors | |
742 ** open on table pTab. | |
743 */ | |
744 static void fts5TripCursors(Fts5Table *pTab){ | |
745 Fts5Cursor *pCsr; | |
746 for(pCsr=pTab->pGlobal->pCsr; pCsr; pCsr=pCsr->pNext){ | |
747 if( pCsr->ePlan==FTS5_PLAN_MATCH | |
748 && pCsr->base.pVtab==(sqlite3_vtab*)pTab | |
749 ){ | |
750 CsrFlagSet(pCsr, FTS5CSR_REQUIRE_RESEEK); | |
751 } | |
752 } | |
753 } | |
754 | |
755 /* | |
756 ** If the REQUIRE_RESEEK flag is set on the cursor passed as the first | |
757 ** argument, close and reopen all Fts5IndexIter iterators that the cursor | |
758 ** is using. Then attempt to move the cursor to a rowid equal to or laster | |
759 ** (in the cursors sort order - ASC or DESC) than the current rowid. | |
760 ** | |
761 ** If the new rowid is not equal to the old, set output parameter *pbSkip | |
762 ** to 1 before returning. Otherwise, leave it unchanged. | |
763 ** | |
764 ** Return SQLITE_OK if successful or if no reseek was required, or an | |
765 ** error code if an error occurred. | |
766 */ | |
767 static int fts5CursorReseek(Fts5Cursor *pCsr, int *pbSkip){ | |
768 int rc = SQLITE_OK; | |
769 assert( *pbSkip==0 ); | |
770 if( CsrFlagTest(pCsr, FTS5CSR_REQUIRE_RESEEK) ){ | |
771 Fts5Table *pTab = (Fts5Table*)(pCsr->base.pVtab); | |
772 int bDesc = pCsr->bDesc; | |
773 i64 iRowid = sqlite3Fts5ExprRowid(pCsr->pExpr); | |
774 | |
775 rc = sqlite3Fts5ExprFirst(pCsr->pExpr, pTab->pIndex, iRowid, bDesc); | |
776 if( rc==SQLITE_OK && iRowid!=sqlite3Fts5ExprRowid(pCsr->pExpr) ){ | |
777 *pbSkip = 1; | |
778 } | |
779 | |
780 CsrFlagClear(pCsr, FTS5CSR_REQUIRE_RESEEK); | |
781 fts5CsrNewrow(pCsr); | |
782 if( sqlite3Fts5ExprEof(pCsr->pExpr) ){ | |
783 CsrFlagSet(pCsr, FTS5CSR_EOF); | |
784 } | |
785 } | |
786 return rc; | |
787 } | |
788 | |
789 | |
790 /* | |
791 ** Advance the cursor to the next row in the table that matches the | |
792 ** search criteria. | |
793 ** | |
794 ** Return SQLITE_OK if nothing goes wrong. SQLITE_OK is returned | |
795 ** even if we reach end-of-file. The fts5EofMethod() will be called | |
796 ** subsequently to determine whether or not an EOF was hit. | |
797 */ | |
798 static int fts5NextMethod(sqlite3_vtab_cursor *pCursor){ | |
799 Fts5Cursor *pCsr = (Fts5Cursor*)pCursor; | |
800 int rc = SQLITE_OK; | |
801 | |
802 assert( (pCsr->ePlan<3)== | |
803 (pCsr->ePlan==FTS5_PLAN_MATCH || pCsr->ePlan==FTS5_PLAN_SOURCE) | |
804 ); | |
805 | |
806 if( pCsr->ePlan<3 ){ | |
807 int bSkip = 0; | |
808 if( (rc = fts5CursorReseek(pCsr, &bSkip)) || bSkip ) return rc; | |
809 rc = sqlite3Fts5ExprNext(pCsr->pExpr, pCsr->iLastRowid); | |
810 if( sqlite3Fts5ExprEof(pCsr->pExpr) ){ | |
811 CsrFlagSet(pCsr, FTS5CSR_EOF); | |
812 } | |
813 fts5CsrNewrow(pCsr); | |
814 }else{ | |
815 switch( pCsr->ePlan ){ | |
816 case FTS5_PLAN_SPECIAL: { | |
817 CsrFlagSet(pCsr, FTS5CSR_EOF); | |
818 break; | |
819 } | |
820 | |
821 case FTS5_PLAN_SORTED_MATCH: { | |
822 rc = fts5SorterNext(pCsr); | |
823 break; | |
824 } | |
825 | |
826 default: | |
827 rc = sqlite3_step(pCsr->pStmt); | |
828 if( rc!=SQLITE_ROW ){ | |
829 CsrFlagSet(pCsr, FTS5CSR_EOF); | |
830 rc = sqlite3_reset(pCsr->pStmt); | |
831 }else{ | |
832 rc = SQLITE_OK; | |
833 } | |
834 break; | |
835 } | |
836 } | |
837 | |
838 return rc; | |
839 } | |
840 | |
841 | |
842 static sqlite3_stmt *fts5PrepareStatement( | |
843 int *pRc, | |
844 Fts5Config *pConfig, | |
845 const char *zFmt, | |
846 ... | |
847 ){ | |
848 sqlite3_stmt *pRet = 0; | |
849 va_list ap; | |
850 va_start(ap, zFmt); | |
851 | |
852 if( *pRc==SQLITE_OK ){ | |
853 int rc; | |
854 char *zSql = sqlite3_vmprintf(zFmt, ap); | |
855 if( zSql==0 ){ | |
856 rc = SQLITE_NOMEM; | |
857 }else{ | |
858 rc = sqlite3_prepare_v2(pConfig->db, zSql, -1, &pRet, 0); | |
859 if( rc!=SQLITE_OK ){ | |
860 *pConfig->pzErrmsg = sqlite3_mprintf("%s", sqlite3_errmsg(pConfig->db)); | |
861 } | |
862 sqlite3_free(zSql); | |
863 } | |
864 *pRc = rc; | |
865 } | |
866 | |
867 va_end(ap); | |
868 return pRet; | |
869 } | |
870 | |
871 static int fts5CursorFirstSorted(Fts5Table *pTab, Fts5Cursor *pCsr, int bDesc){ | |
872 Fts5Config *pConfig = pTab->pConfig; | |
873 Fts5Sorter *pSorter; | |
874 int nPhrase; | |
875 int nByte; | |
876 int rc = SQLITE_OK; | |
877 const char *zRank = pCsr->zRank; | |
878 const char *zRankArgs = pCsr->zRankArgs; | |
879 | |
880 nPhrase = sqlite3Fts5ExprPhraseCount(pCsr->pExpr); | |
881 nByte = sizeof(Fts5Sorter) + sizeof(int) * (nPhrase-1); | |
882 pSorter = (Fts5Sorter*)sqlite3_malloc(nByte); | |
883 if( pSorter==0 ) return SQLITE_NOMEM; | |
884 memset(pSorter, 0, nByte); | |
885 pSorter->nIdx = nPhrase; | |
886 | |
887 /* TODO: It would be better to have some system for reusing statement | |
888 ** handles here, rather than preparing a new one for each query. But that | |
889 ** is not possible as SQLite reference counts the virtual table objects. | |
890 ** And since the statement required here reads from this very virtual | |
891 ** table, saving it creates a circular reference. | |
892 ** | |
893 ** If SQLite a built-in statement cache, this wouldn't be a problem. */ | |
894 pSorter->pStmt = fts5PrepareStatement(&rc, pConfig, | |
895 "SELECT rowid, rank FROM %Q.%Q ORDER BY %s(%s%s%s) %s", | |
896 pConfig->zDb, pConfig->zName, zRank, pConfig->zName, | |
897 (zRankArgs ? ", " : ""), | |
898 (zRankArgs ? zRankArgs : ""), | |
899 bDesc ? "DESC" : "ASC" | |
900 ); | |
901 | |
902 pCsr->pSorter = pSorter; | |
903 if( rc==SQLITE_OK ){ | |
904 assert( pTab->pSortCsr==0 ); | |
905 pTab->pSortCsr = pCsr; | |
906 rc = fts5SorterNext(pCsr); | |
907 pTab->pSortCsr = 0; | |
908 } | |
909 | |
910 if( rc!=SQLITE_OK ){ | |
911 sqlite3_finalize(pSorter->pStmt); | |
912 sqlite3_free(pSorter); | |
913 pCsr->pSorter = 0; | |
914 } | |
915 | |
916 return rc; | |
917 } | |
918 | |
919 static int fts5CursorFirst(Fts5Table *pTab, Fts5Cursor *pCsr, int bDesc){ | |
920 int rc; | |
921 Fts5Expr *pExpr = pCsr->pExpr; | |
922 rc = sqlite3Fts5ExprFirst(pExpr, pTab->pIndex, pCsr->iFirstRowid, bDesc); | |
923 if( sqlite3Fts5ExprEof(pExpr) ){ | |
924 CsrFlagSet(pCsr, FTS5CSR_EOF); | |
925 } | |
926 fts5CsrNewrow(pCsr); | |
927 return rc; | |
928 } | |
929 | |
930 /* | |
931 ** Process a "special" query. A special query is identified as one with a | |
932 ** MATCH expression that begins with a '*' character. The remainder of | |
933 ** the text passed to the MATCH operator are used as the special query | |
934 ** parameters. | |
935 */ | |
936 static int fts5SpecialMatch( | |
937 Fts5Table *pTab, | |
938 Fts5Cursor *pCsr, | |
939 const char *zQuery | |
940 ){ | |
941 int rc = SQLITE_OK; /* Return code */ | |
942 const char *z = zQuery; /* Special query text */ | |
943 int n; /* Number of bytes in text at z */ | |
944 | |
945 while( z[0]==' ' ) z++; | |
946 for(n=0; z[n] && z[n]!=' '; n++); | |
947 | |
948 assert( pTab->base.zErrMsg==0 ); | |
949 pCsr->ePlan = FTS5_PLAN_SPECIAL; | |
950 | |
951 if( 0==sqlite3_strnicmp("reads", z, n) ){ | |
952 pCsr->iSpecial = sqlite3Fts5IndexReads(pTab->pIndex); | |
953 } | |
954 else if( 0==sqlite3_strnicmp("id", z, n) ){ | |
955 pCsr->iSpecial = pCsr->iCsrId; | |
956 } | |
957 else{ | |
958 /* An unrecognized directive. Return an error message. */ | |
959 pTab->base.zErrMsg = sqlite3_mprintf("unknown special query: %.*s", n, z); | |
960 rc = SQLITE_ERROR; | |
961 } | |
962 | |
963 return rc; | |
964 } | |
965 | |
966 /* | |
967 ** Search for an auxiliary function named zName that can be used with table | |
968 ** pTab. If one is found, return a pointer to the corresponding Fts5Auxiliary | |
969 ** structure. Otherwise, if no such function exists, return NULL. | |
970 */ | |
971 static Fts5Auxiliary *fts5FindAuxiliary(Fts5Table *pTab, const char *zName){ | |
972 Fts5Auxiliary *pAux; | |
973 | |
974 for(pAux=pTab->pGlobal->pAux; pAux; pAux=pAux->pNext){ | |
975 if( sqlite3_stricmp(zName, pAux->zFunc)==0 ) return pAux; | |
976 } | |
977 | |
978 /* No function of the specified name was found. Return 0. */ | |
979 return 0; | |
980 } | |
981 | |
982 | |
983 static int fts5FindRankFunction(Fts5Cursor *pCsr){ | |
984 Fts5Table *pTab = (Fts5Table*)(pCsr->base.pVtab); | |
985 Fts5Config *pConfig = pTab->pConfig; | |
986 int rc = SQLITE_OK; | |
987 Fts5Auxiliary *pAux = 0; | |
988 const char *zRank = pCsr->zRank; | |
989 const char *zRankArgs = pCsr->zRankArgs; | |
990 | |
991 if( zRankArgs ){ | |
992 char *zSql = sqlite3Fts5Mprintf(&rc, "SELECT %s", zRankArgs); | |
993 if( zSql ){ | |
994 sqlite3_stmt *pStmt = 0; | |
995 rc = sqlite3_prepare_v2(pConfig->db, zSql, -1, &pStmt, 0); | |
996 sqlite3_free(zSql); | |
997 assert( rc==SQLITE_OK || pCsr->pRankArgStmt==0 ); | |
998 if( rc==SQLITE_OK ){ | |
999 if( SQLITE_ROW==sqlite3_step(pStmt) ){ | |
1000 int nByte; | |
1001 pCsr->nRankArg = sqlite3_column_count(pStmt); | |
1002 nByte = sizeof(sqlite3_value*)*pCsr->nRankArg; | |
1003 pCsr->apRankArg = (sqlite3_value**)sqlite3Fts5MallocZero(&rc, nByte); | |
1004 if( rc==SQLITE_OK ){ | |
1005 int i; | |
1006 for(i=0; i<pCsr->nRankArg; i++){ | |
1007 pCsr->apRankArg[i] = sqlite3_column_value(pStmt, i); | |
1008 } | |
1009 } | |
1010 pCsr->pRankArgStmt = pStmt; | |
1011 }else{ | |
1012 rc = sqlite3_finalize(pStmt); | |
1013 assert( rc!=SQLITE_OK ); | |
1014 } | |
1015 } | |
1016 } | |
1017 } | |
1018 | |
1019 if( rc==SQLITE_OK ){ | |
1020 pAux = fts5FindAuxiliary(pTab, zRank); | |
1021 if( pAux==0 ){ | |
1022 assert( pTab->base.zErrMsg==0 ); | |
1023 pTab->base.zErrMsg = sqlite3_mprintf("no such function: %s", zRank); | |
1024 rc = SQLITE_ERROR; | |
1025 } | |
1026 } | |
1027 | |
1028 pCsr->pRank = pAux; | |
1029 return rc; | |
1030 } | |
1031 | |
1032 | |
1033 static int fts5CursorParseRank( | |
1034 Fts5Config *pConfig, | |
1035 Fts5Cursor *pCsr, | |
1036 sqlite3_value *pRank | |
1037 ){ | |
1038 int rc = SQLITE_OK; | |
1039 if( pRank ){ | |
1040 const char *z = (const char*)sqlite3_value_text(pRank); | |
1041 char *zRank = 0; | |
1042 char *zRankArgs = 0; | |
1043 | |
1044 if( z==0 ){ | |
1045 if( sqlite3_value_type(pRank)==SQLITE_NULL ) rc = SQLITE_ERROR; | |
1046 }else{ | |
1047 rc = sqlite3Fts5ConfigParseRank(z, &zRank, &zRankArgs); | |
1048 } | |
1049 if( rc==SQLITE_OK ){ | |
1050 pCsr->zRank = zRank; | |
1051 pCsr->zRankArgs = zRankArgs; | |
1052 CsrFlagSet(pCsr, FTS5CSR_FREE_ZRANK); | |
1053 }else if( rc==SQLITE_ERROR ){ | |
1054 pCsr->base.pVtab->zErrMsg = sqlite3_mprintf( | |
1055 "parse error in rank function: %s", z | |
1056 ); | |
1057 } | |
1058 }else{ | |
1059 if( pConfig->zRank ){ | |
1060 pCsr->zRank = (char*)pConfig->zRank; | |
1061 pCsr->zRankArgs = (char*)pConfig->zRankArgs; | |
1062 }else{ | |
1063 pCsr->zRank = (char*)FTS5_DEFAULT_RANK; | |
1064 pCsr->zRankArgs = 0; | |
1065 } | |
1066 } | |
1067 return rc; | |
1068 } | |
1069 | |
1070 static i64 fts5GetRowidLimit(sqlite3_value *pVal, i64 iDefault){ | |
1071 if( pVal ){ | |
1072 int eType = sqlite3_value_numeric_type(pVal); | |
1073 if( eType==SQLITE_INTEGER ){ | |
1074 return sqlite3_value_int64(pVal); | |
1075 } | |
1076 } | |
1077 return iDefault; | |
1078 } | |
1079 | |
1080 /* | |
1081 ** This is the xFilter interface for the virtual table. See | |
1082 ** the virtual table xFilter method documentation for additional | |
1083 ** information. | |
1084 ** | |
1085 ** There are three possible query strategies: | |
1086 ** | |
1087 ** 1. Full-text search using a MATCH operator. | |
1088 ** 2. A by-rowid lookup. | |
1089 ** 3. A full-table scan. | |
1090 */ | |
1091 static int fts5FilterMethod( | |
1092 sqlite3_vtab_cursor *pCursor, /* The cursor used for this query */ | |
1093 int idxNum, /* Strategy index */ | |
1094 const char *idxStr, /* Unused */ | |
1095 int nVal, /* Number of elements in apVal */ | |
1096 sqlite3_value **apVal /* Arguments for the indexing scheme */ | |
1097 ){ | |
1098 Fts5Table *pTab = (Fts5Table*)(pCursor->pVtab); | |
1099 Fts5Config *pConfig = pTab->pConfig; | |
1100 Fts5Cursor *pCsr = (Fts5Cursor*)pCursor; | |
1101 int rc = SQLITE_OK; /* Error code */ | |
1102 int iVal = 0; /* Counter for apVal[] */ | |
1103 int bDesc; /* True if ORDER BY [rank|rowid] DESC */ | |
1104 int bOrderByRank; /* True if ORDER BY rank */ | |
1105 sqlite3_value *pMatch = 0; /* <tbl> MATCH ? expression (or NULL) */ | |
1106 sqlite3_value *pRank = 0; /* rank MATCH ? expression (or NULL) */ | |
1107 sqlite3_value *pRowidEq = 0; /* rowid = ? expression (or NULL) */ | |
1108 sqlite3_value *pRowidLe = 0; /* rowid <= ? expression (or NULL) */ | |
1109 sqlite3_value *pRowidGe = 0; /* rowid >= ? expression (or NULL) */ | |
1110 char **pzErrmsg = pConfig->pzErrmsg; | |
1111 | |
1112 if( pCsr->ePlan ){ | |
1113 fts5FreeCursorComponents(pCsr); | |
1114 memset(&pCsr->ePlan, 0, sizeof(Fts5Cursor) - ((u8*)&pCsr->ePlan-(u8*)pCsr)); | |
1115 } | |
1116 | |
1117 assert( pCsr->pStmt==0 ); | |
1118 assert( pCsr->pExpr==0 ); | |
1119 assert( pCsr->csrflags==0 ); | |
1120 assert( pCsr->pRank==0 ); | |
1121 assert( pCsr->zRank==0 ); | |
1122 assert( pCsr->zRankArgs==0 ); | |
1123 | |
1124 assert( pzErrmsg==0 || pzErrmsg==&pTab->base.zErrMsg ); | |
1125 pConfig->pzErrmsg = &pTab->base.zErrMsg; | |
1126 | |
1127 /* Decode the arguments passed through to this function. | |
1128 ** | |
1129 ** Note: The following set of if(...) statements must be in the same | |
1130 ** order as the corresponding entries in the struct at the top of | |
1131 ** fts5BestIndexMethod(). */ | |
1132 if( BitFlagTest(idxNum, FTS5_BI_MATCH) ) pMatch = apVal[iVal++]; | |
1133 if( BitFlagTest(idxNum, FTS5_BI_RANK) ) pRank = apVal[iVal++]; | |
1134 if( BitFlagTest(idxNum, FTS5_BI_ROWID_EQ) ) pRowidEq = apVal[iVal++]; | |
1135 if( BitFlagTest(idxNum, FTS5_BI_ROWID_LE) ) pRowidLe = apVal[iVal++]; | |
1136 if( BitFlagTest(idxNum, FTS5_BI_ROWID_GE) ) pRowidGe = apVal[iVal++]; | |
1137 assert( iVal==nVal ); | |
1138 bOrderByRank = ((idxNum & FTS5_BI_ORDER_RANK) ? 1 : 0); | |
1139 pCsr->bDesc = bDesc = ((idxNum & FTS5_BI_ORDER_DESC) ? 1 : 0); | |
1140 | |
1141 /* Set the cursor upper and lower rowid limits. Only some strategies | |
1142 ** actually use them. This is ok, as the xBestIndex() method leaves the | |
1143 ** sqlite3_index_constraint.omit flag clear for range constraints | |
1144 ** on the rowid field. */ | |
1145 if( pRowidEq ){ | |
1146 pRowidLe = pRowidGe = pRowidEq; | |
1147 } | |
1148 if( bDesc ){ | |
1149 pCsr->iFirstRowid = fts5GetRowidLimit(pRowidLe, LARGEST_INT64); | |
1150 pCsr->iLastRowid = fts5GetRowidLimit(pRowidGe, SMALLEST_INT64); | |
1151 }else{ | |
1152 pCsr->iLastRowid = fts5GetRowidLimit(pRowidLe, LARGEST_INT64); | |
1153 pCsr->iFirstRowid = fts5GetRowidLimit(pRowidGe, SMALLEST_INT64); | |
1154 } | |
1155 | |
1156 if( pTab->pSortCsr ){ | |
1157 /* If pSortCsr is non-NULL, then this call is being made as part of | |
1158 ** processing for a "... MATCH <expr> ORDER BY rank" query (ePlan is | |
1159 ** set to FTS5_PLAN_SORTED_MATCH). pSortCsr is the cursor that will | |
1160 ** return results to the user for this query. The current cursor | |
1161 ** (pCursor) is used to execute the query issued by function | |
1162 ** fts5CursorFirstSorted() above. */ | |
1163 assert( pRowidEq==0 && pRowidLe==0 && pRowidGe==0 && pRank==0 ); | |
1164 assert( nVal==0 && pMatch==0 && bOrderByRank==0 && bDesc==0 ); | |
1165 assert( pCsr->iLastRowid==LARGEST_INT64 ); | |
1166 assert( pCsr->iFirstRowid==SMALLEST_INT64 ); | |
1167 pCsr->ePlan = FTS5_PLAN_SOURCE; | |
1168 pCsr->pExpr = pTab->pSortCsr->pExpr; | |
1169 rc = fts5CursorFirst(pTab, pCsr, bDesc); | |
1170 }else if( pMatch ){ | |
1171 const char *zExpr = (const char*)sqlite3_value_text(apVal[0]); | |
1172 if( zExpr==0 ) zExpr = ""; | |
1173 | |
1174 rc = fts5CursorParseRank(pConfig, pCsr, pRank); | |
1175 if( rc==SQLITE_OK ){ | |
1176 if( zExpr[0]=='*' ){ | |
1177 /* The user has issued a query of the form "MATCH '*...'". This | |
1178 ** indicates that the MATCH expression is not a full text query, | |
1179 ** but a request for an internal parameter. */ | |
1180 rc = fts5SpecialMatch(pTab, pCsr, &zExpr[1]); | |
1181 }else{ | |
1182 char **pzErr = &pTab->base.zErrMsg; | |
1183 rc = sqlite3Fts5ExprNew(pConfig, zExpr, &pCsr->pExpr, pzErr); | |
1184 if( rc==SQLITE_OK ){ | |
1185 if( bOrderByRank ){ | |
1186 pCsr->ePlan = FTS5_PLAN_SORTED_MATCH; | |
1187 rc = fts5CursorFirstSorted(pTab, pCsr, bDesc); | |
1188 }else{ | |
1189 pCsr->ePlan = FTS5_PLAN_MATCH; | |
1190 rc = fts5CursorFirst(pTab, pCsr, bDesc); | |
1191 } | |
1192 } | |
1193 } | |
1194 } | |
1195 }else if( pConfig->zContent==0 ){ | |
1196 *pConfig->pzErrmsg = sqlite3_mprintf( | |
1197 "%s: table does not support scanning", pConfig->zName | |
1198 ); | |
1199 rc = SQLITE_ERROR; | |
1200 }else{ | |
1201 /* This is either a full-table scan (ePlan==FTS5_PLAN_SCAN) or a lookup | |
1202 ** by rowid (ePlan==FTS5_PLAN_ROWID). */ | |
1203 pCsr->ePlan = (pRowidEq ? FTS5_PLAN_ROWID : FTS5_PLAN_SCAN); | |
1204 rc = sqlite3Fts5StorageStmt( | |
1205 pTab->pStorage, fts5StmtType(pCsr), &pCsr->pStmt, &pTab->base.zErrMsg | |
1206 ); | |
1207 if( rc==SQLITE_OK ){ | |
1208 if( pCsr->ePlan==FTS5_PLAN_ROWID ){ | |
1209 sqlite3_bind_value(pCsr->pStmt, 1, apVal[0]); | |
1210 }else{ | |
1211 sqlite3_bind_int64(pCsr->pStmt, 1, pCsr->iFirstRowid); | |
1212 sqlite3_bind_int64(pCsr->pStmt, 2, pCsr->iLastRowid); | |
1213 } | |
1214 rc = fts5NextMethod(pCursor); | |
1215 } | |
1216 } | |
1217 | |
1218 pConfig->pzErrmsg = pzErrmsg; | |
1219 return rc; | |
1220 } | |
1221 | |
1222 /* | |
1223 ** This is the xEof method of the virtual table. SQLite calls this | |
1224 ** routine to find out if it has reached the end of a result set. | |
1225 */ | |
1226 static int fts5EofMethod(sqlite3_vtab_cursor *pCursor){ | |
1227 Fts5Cursor *pCsr = (Fts5Cursor*)pCursor; | |
1228 return (CsrFlagTest(pCsr, FTS5CSR_EOF) ? 1 : 0); | |
1229 } | |
1230 | |
1231 /* | |
1232 ** Return the rowid that the cursor currently points to. | |
1233 */ | |
1234 static i64 fts5CursorRowid(Fts5Cursor *pCsr){ | |
1235 assert( pCsr->ePlan==FTS5_PLAN_MATCH | |
1236 || pCsr->ePlan==FTS5_PLAN_SORTED_MATCH | |
1237 || pCsr->ePlan==FTS5_PLAN_SOURCE | |
1238 ); | |
1239 if( pCsr->pSorter ){ | |
1240 return pCsr->pSorter->iRowid; | |
1241 }else{ | |
1242 return sqlite3Fts5ExprRowid(pCsr->pExpr); | |
1243 } | |
1244 } | |
1245 | |
1246 /* | |
1247 ** This is the xRowid method. The SQLite core calls this routine to | |
1248 ** retrieve the rowid for the current row of the result set. fts5 | |
1249 ** exposes %_content.rowid as the rowid for the virtual table. The | |
1250 ** rowid should be written to *pRowid. | |
1251 */ | |
1252 static int fts5RowidMethod(sqlite3_vtab_cursor *pCursor, sqlite_int64 *pRowid){ | |
1253 Fts5Cursor *pCsr = (Fts5Cursor*)pCursor; | |
1254 int ePlan = pCsr->ePlan; | |
1255 | |
1256 assert( CsrFlagTest(pCsr, FTS5CSR_EOF)==0 ); | |
1257 switch( ePlan ){ | |
1258 case FTS5_PLAN_SPECIAL: | |
1259 *pRowid = 0; | |
1260 break; | |
1261 | |
1262 case FTS5_PLAN_SOURCE: | |
1263 case FTS5_PLAN_MATCH: | |
1264 case FTS5_PLAN_SORTED_MATCH: | |
1265 *pRowid = fts5CursorRowid(pCsr); | |
1266 break; | |
1267 | |
1268 default: | |
1269 *pRowid = sqlite3_column_int64(pCsr->pStmt, 0); | |
1270 break; | |
1271 } | |
1272 | |
1273 return SQLITE_OK; | |
1274 } | |
1275 | |
1276 /* | |
1277 ** If the cursor requires seeking (bSeekRequired flag is set), seek it. | |
1278 ** Return SQLITE_OK if no error occurs, or an SQLite error code otherwise. | |
1279 ** | |
1280 ** If argument bErrormsg is true and an error occurs, an error message may | |
1281 ** be left in sqlite3_vtab.zErrMsg. | |
1282 */ | |
1283 static int fts5SeekCursor(Fts5Cursor *pCsr, int bErrormsg){ | |
1284 int rc = SQLITE_OK; | |
1285 | |
1286 /* If the cursor does not yet have a statement handle, obtain one now. */ | |
1287 if( pCsr->pStmt==0 ){ | |
1288 Fts5Table *pTab = (Fts5Table*)(pCsr->base.pVtab); | |
1289 int eStmt = fts5StmtType(pCsr); | |
1290 rc = sqlite3Fts5StorageStmt( | |
1291 pTab->pStorage, eStmt, &pCsr->pStmt, (bErrormsg?&pTab->base.zErrMsg:0) | |
1292 ); | |
1293 assert( rc!=SQLITE_OK || pTab->base.zErrMsg==0 ); | |
1294 assert( CsrFlagTest(pCsr, FTS5CSR_REQUIRE_CONTENT) ); | |
1295 } | |
1296 | |
1297 if( rc==SQLITE_OK && CsrFlagTest(pCsr, FTS5CSR_REQUIRE_CONTENT) ){ | |
1298 assert( pCsr->pExpr ); | |
1299 sqlite3_reset(pCsr->pStmt); | |
1300 sqlite3_bind_int64(pCsr->pStmt, 1, fts5CursorRowid(pCsr)); | |
1301 rc = sqlite3_step(pCsr->pStmt); | |
1302 if( rc==SQLITE_ROW ){ | |
1303 rc = SQLITE_OK; | |
1304 CsrFlagClear(pCsr, FTS5CSR_REQUIRE_CONTENT); | |
1305 }else{ | |
1306 rc = sqlite3_reset(pCsr->pStmt); | |
1307 if( rc==SQLITE_OK ){ | |
1308 rc = FTS5_CORRUPT; | |
1309 } | |
1310 } | |
1311 } | |
1312 return rc; | |
1313 } | |
1314 | |
1315 static void fts5SetVtabError(Fts5Table *p, const char *zFormat, ...){ | |
1316 va_list ap; /* ... printf arguments */ | |
1317 va_start(ap, zFormat); | |
1318 assert( p->base.zErrMsg==0 ); | |
1319 p->base.zErrMsg = sqlite3_vmprintf(zFormat, ap); | |
1320 va_end(ap); | |
1321 } | |
1322 | |
1323 /* | |
1324 ** This function is called to handle an FTS INSERT command. In other words, | |
1325 ** an INSERT statement of the form: | |
1326 ** | |
1327 ** INSERT INTO fts(fts) VALUES($pCmd) | |
1328 ** INSERT INTO fts(fts, rank) VALUES($pCmd, $pVal) | |
1329 ** | |
1330 ** Argument pVal is the value assigned to column "fts" by the INSERT | |
1331 ** statement. This function returns SQLITE_OK if successful, or an SQLite | |
1332 ** error code if an error occurs. | |
1333 ** | |
1334 ** The commands implemented by this function are documented in the "Special | |
1335 ** INSERT Directives" section of the documentation. It should be updated if | |
1336 ** more commands are added to this function. | |
1337 */ | |
1338 static int fts5SpecialInsert( | |
1339 Fts5Table *pTab, /* Fts5 table object */ | |
1340 const char *zCmd, /* Text inserted into table-name column */ | |
1341 sqlite3_value *pVal /* Value inserted into rank column */ | |
1342 ){ | |
1343 Fts5Config *pConfig = pTab->pConfig; | |
1344 int rc = SQLITE_OK; | |
1345 int bError = 0; | |
1346 | |
1347 if( 0==sqlite3_stricmp("delete-all", zCmd) ){ | |
1348 if( pConfig->eContent==FTS5_CONTENT_NORMAL ){ | |
1349 fts5SetVtabError(pTab, | |
1350 "'delete-all' may only be used with a " | |
1351 "contentless or external content fts5 table" | |
1352 ); | |
1353 rc = SQLITE_ERROR; | |
1354 }else{ | |
1355 rc = sqlite3Fts5StorageDeleteAll(pTab->pStorage); | |
1356 } | |
1357 }else if( 0==sqlite3_stricmp("rebuild", zCmd) ){ | |
1358 if( pConfig->eContent==FTS5_CONTENT_NONE ){ | |
1359 fts5SetVtabError(pTab, | |
1360 "'rebuild' may not be used with a contentless fts5 table" | |
1361 ); | |
1362 rc = SQLITE_ERROR; | |
1363 }else{ | |
1364 rc = sqlite3Fts5StorageRebuild(pTab->pStorage); | |
1365 } | |
1366 }else if( 0==sqlite3_stricmp("optimize", zCmd) ){ | |
1367 rc = sqlite3Fts5StorageOptimize(pTab->pStorage); | |
1368 }else if( 0==sqlite3_stricmp("merge", zCmd) ){ | |
1369 int nMerge = sqlite3_value_int(pVal); | |
1370 rc = sqlite3Fts5StorageMerge(pTab->pStorage, nMerge); | |
1371 }else if( 0==sqlite3_stricmp("integrity-check", zCmd) ){ | |
1372 rc = sqlite3Fts5StorageIntegrity(pTab->pStorage); | |
1373 #ifdef SQLITE_DEBUG | |
1374 }else if( 0==sqlite3_stricmp("prefix-index", zCmd) ){ | |
1375 pConfig->bPrefixIndex = sqlite3_value_int(pVal); | |
1376 #endif | |
1377 }else{ | |
1378 rc = sqlite3Fts5IndexLoadConfig(pTab->pIndex); | |
1379 if( rc==SQLITE_OK ){ | |
1380 rc = sqlite3Fts5ConfigSetValue(pTab->pConfig, zCmd, pVal, &bError); | |
1381 } | |
1382 if( rc==SQLITE_OK ){ | |
1383 if( bError ){ | |
1384 rc = SQLITE_ERROR; | |
1385 }else{ | |
1386 rc = sqlite3Fts5StorageConfigValue(pTab->pStorage, zCmd, pVal, 0); | |
1387 } | |
1388 } | |
1389 } | |
1390 return rc; | |
1391 } | |
1392 | |
1393 static int fts5SpecialDelete( | |
1394 Fts5Table *pTab, | |
1395 sqlite3_value **apVal, | |
1396 sqlite3_int64 *piRowid | |
1397 ){ | |
1398 int rc = SQLITE_OK; | |
1399 int eType1 = sqlite3_value_type(apVal[1]); | |
1400 if( eType1==SQLITE_INTEGER ){ | |
1401 sqlite3_int64 iDel = sqlite3_value_int64(apVal[1]); | |
1402 rc = sqlite3Fts5StorageSpecialDelete(pTab->pStorage, iDel, &apVal[2]); | |
1403 } | |
1404 return rc; | |
1405 } | |
1406 | |
1407 static void fts5StorageInsert( | |
1408 int *pRc, | |
1409 Fts5Table *pTab, | |
1410 sqlite3_value **apVal, | |
1411 i64 *piRowid | |
1412 ){ | |
1413 int rc = *pRc; | |
1414 if( rc==SQLITE_OK ){ | |
1415 rc = sqlite3Fts5StorageContentInsert(pTab->pStorage, apVal, piRowid); | |
1416 } | |
1417 if( rc==SQLITE_OK ){ | |
1418 rc = sqlite3Fts5StorageIndexInsert(pTab->pStorage, apVal, *piRowid); | |
1419 } | |
1420 *pRc = rc; | |
1421 } | |
1422 | |
1423 /* | |
1424 ** This function is the implementation of the xUpdate callback used by | |
1425 ** FTS3 virtual tables. It is invoked by SQLite each time a row is to be | |
1426 ** inserted, updated or deleted. | |
1427 ** | |
1428 ** A delete specifies a single argument - the rowid of the row to remove. | |
1429 ** | |
1430 ** Update and insert operations pass: | |
1431 ** | |
1432 ** 1. The "old" rowid, or NULL. | |
1433 ** 2. The "new" rowid. | |
1434 ** 3. Values for each of the nCol matchable columns. | |
1435 ** 4. Values for the two hidden columns (<tablename> and "rank"). | |
1436 */ | |
1437 static int fts5UpdateMethod( | |
1438 sqlite3_vtab *pVtab, /* Virtual table handle */ | |
1439 int nArg, /* Size of argument array */ | |
1440 sqlite3_value **apVal, /* Array of arguments */ | |
1441 sqlite_int64 *pRowid /* OUT: The affected (or effected) rowid */ | |
1442 ){ | |
1443 Fts5Table *pTab = (Fts5Table*)pVtab; | |
1444 Fts5Config *pConfig = pTab->pConfig; | |
1445 int eType0; /* value_type() of apVal[0] */ | |
1446 int rc = SQLITE_OK; /* Return code */ | |
1447 | |
1448 /* A transaction must be open when this is called. */ | |
1449 assert( pTab->ts.eState==1 ); | |
1450 | |
1451 assert( pVtab->zErrMsg==0 ); | |
1452 assert( nArg==1 || nArg==(2+pConfig->nCol+2) ); | |
1453 assert( nArg==1 | |
1454 || sqlite3_value_type(apVal[1])==SQLITE_INTEGER | |
1455 || sqlite3_value_type(apVal[1])==SQLITE_NULL | |
1456 ); | |
1457 assert( pTab->pConfig->pzErrmsg==0 ); | |
1458 pTab->pConfig->pzErrmsg = &pTab->base.zErrMsg; | |
1459 | |
1460 /* Put any active cursors into REQUIRE_SEEK state. */ | |
1461 fts5TripCursors(pTab); | |
1462 | |
1463 eType0 = sqlite3_value_type(apVal[0]); | |
1464 if( eType0==SQLITE_NULL | |
1465 && sqlite3_value_type(apVal[2+pConfig->nCol])!=SQLITE_NULL | |
1466 ){ | |
1467 /* A "special" INSERT op. These are handled separately. */ | |
1468 const char *z = (const char*)sqlite3_value_text(apVal[2+pConfig->nCol]); | |
1469 if( pConfig->eContent!=FTS5_CONTENT_NORMAL | |
1470 && 0==sqlite3_stricmp("delete", z) | |
1471 ){ | |
1472 rc = fts5SpecialDelete(pTab, apVal, pRowid); | |
1473 }else{ | |
1474 rc = fts5SpecialInsert(pTab, z, apVal[2 + pConfig->nCol + 1]); | |
1475 } | |
1476 }else{ | |
1477 /* A regular INSERT, UPDATE or DELETE statement. The trick here is that | |
1478 ** any conflict on the rowid value must be detected before any | |
1479 ** modifications are made to the database file. There are 4 cases: | |
1480 ** | |
1481 ** 1) DELETE | |
1482 ** 2) UPDATE (rowid not modified) | |
1483 ** 3) UPDATE (rowid modified) | |
1484 ** 4) INSERT | |
1485 ** | |
1486 ** Cases 3 and 4 may violate the rowid constraint. | |
1487 */ | |
1488 int eConflict = SQLITE_ABORT; | |
1489 if( pConfig->eContent==FTS5_CONTENT_NORMAL ){ | |
1490 eConflict = sqlite3_vtab_on_conflict(pConfig->db); | |
1491 } | |
1492 | |
1493 assert( eType0==SQLITE_INTEGER || eType0==SQLITE_NULL ); | |
1494 assert( nArg!=1 || eType0==SQLITE_INTEGER ); | |
1495 | |
1496 /* Filter out attempts to run UPDATE or DELETE on contentless tables. | |
1497 ** This is not suported. */ | |
1498 if( eType0==SQLITE_INTEGER && fts5IsContentless(pTab) ){ | |
1499 pTab->base.zErrMsg = sqlite3_mprintf( | |
1500 "cannot %s contentless fts5 table: %s", | |
1501 (nArg>1 ? "UPDATE" : "DELETE from"), pConfig->zName | |
1502 ); | |
1503 rc = SQLITE_ERROR; | |
1504 } | |
1505 | |
1506 /* Case 1: DELETE */ | |
1507 else if( nArg==1 ){ | |
1508 i64 iDel = sqlite3_value_int64(apVal[0]); /* Rowid to delete */ | |
1509 rc = sqlite3Fts5StorageDelete(pTab->pStorage, iDel); | |
1510 } | |
1511 | |
1512 /* Case 2: INSERT */ | |
1513 else if( eType0!=SQLITE_INTEGER ){ | |
1514 /* If this is a REPLACE, first remove the current entry (if any) */ | |
1515 if( eConflict==SQLITE_REPLACE | |
1516 && sqlite3_value_type(apVal[1])==SQLITE_INTEGER | |
1517 ){ | |
1518 i64 iNew = sqlite3_value_int64(apVal[1]); /* Rowid to delete */ | |
1519 rc = sqlite3Fts5StorageDelete(pTab->pStorage, iNew); | |
1520 } | |
1521 fts5StorageInsert(&rc, pTab, apVal, pRowid); | |
1522 } | |
1523 | |
1524 /* Case 2: UPDATE */ | |
1525 else{ | |
1526 i64 iOld = sqlite3_value_int64(apVal[0]); /* Old rowid */ | |
1527 i64 iNew = sqlite3_value_int64(apVal[1]); /* New rowid */ | |
1528 if( iOld!=iNew ){ | |
1529 if( eConflict==SQLITE_REPLACE ){ | |
1530 rc = sqlite3Fts5StorageDelete(pTab->pStorage, iOld); | |
1531 if( rc==SQLITE_OK ){ | |
1532 rc = sqlite3Fts5StorageDelete(pTab->pStorage, iNew); | |
1533 } | |
1534 fts5StorageInsert(&rc, pTab, apVal, pRowid); | |
1535 }else{ | |
1536 rc = sqlite3Fts5StorageContentInsert(pTab->pStorage, apVal, pRowid); | |
1537 if( rc==SQLITE_OK ){ | |
1538 rc = sqlite3Fts5StorageDelete(pTab->pStorage, iOld); | |
1539 } | |
1540 if( rc==SQLITE_OK ){ | |
1541 rc = sqlite3Fts5StorageIndexInsert(pTab->pStorage, apVal, *pRowid); | |
1542 } | |
1543 } | |
1544 }else{ | |
1545 rc = sqlite3Fts5StorageDelete(pTab->pStorage, iOld); | |
1546 fts5StorageInsert(&rc, pTab, apVal, pRowid); | |
1547 } | |
1548 } | |
1549 } | |
1550 | |
1551 pTab->pConfig->pzErrmsg = 0; | |
1552 return rc; | |
1553 } | |
1554 | |
1555 /* | |
1556 ** Implementation of xSync() method. | |
1557 */ | |
1558 static int fts5SyncMethod(sqlite3_vtab *pVtab){ | |
1559 int rc; | |
1560 Fts5Table *pTab = (Fts5Table*)pVtab; | |
1561 fts5CheckTransactionState(pTab, FTS5_SYNC, 0); | |
1562 pTab->pConfig->pzErrmsg = &pTab->base.zErrMsg; | |
1563 fts5TripCursors(pTab); | |
1564 rc = sqlite3Fts5StorageSync(pTab->pStorage, 1); | |
1565 pTab->pConfig->pzErrmsg = 0; | |
1566 return rc; | |
1567 } | |
1568 | |
1569 /* | |
1570 ** Implementation of xBegin() method. | |
1571 */ | |
1572 static int fts5BeginMethod(sqlite3_vtab *pVtab){ | |
1573 fts5CheckTransactionState((Fts5Table*)pVtab, FTS5_BEGIN, 0); | |
1574 return SQLITE_OK; | |
1575 } | |
1576 | |
1577 /* | |
1578 ** Implementation of xCommit() method. This is a no-op. The contents of | |
1579 ** the pending-terms hash-table have already been flushed into the database | |
1580 ** by fts5SyncMethod(). | |
1581 */ | |
1582 static int fts5CommitMethod(sqlite3_vtab *pVtab){ | |
1583 fts5CheckTransactionState((Fts5Table*)pVtab, FTS5_COMMIT, 0); | |
1584 return SQLITE_OK; | |
1585 } | |
1586 | |
1587 /* | |
1588 ** Implementation of xRollback(). Discard the contents of the pending-terms | |
1589 ** hash-table. Any changes made to the database are reverted by SQLite. | |
1590 */ | |
1591 static int fts5RollbackMethod(sqlite3_vtab *pVtab){ | |
1592 int rc; | |
1593 Fts5Table *pTab = (Fts5Table*)pVtab; | |
1594 fts5CheckTransactionState(pTab, FTS5_ROLLBACK, 0); | |
1595 rc = sqlite3Fts5StorageRollback(pTab->pStorage); | |
1596 return rc; | |
1597 } | |
1598 | |
1599 static void *fts5ApiUserData(Fts5Context *pCtx){ | |
1600 Fts5Cursor *pCsr = (Fts5Cursor*)pCtx; | |
1601 return pCsr->pAux->pUserData; | |
1602 } | |
1603 | |
1604 static int fts5ApiColumnCount(Fts5Context *pCtx){ | |
1605 Fts5Cursor *pCsr = (Fts5Cursor*)pCtx; | |
1606 return ((Fts5Table*)(pCsr->base.pVtab))->pConfig->nCol; | |
1607 } | |
1608 | |
1609 static int fts5ApiColumnTotalSize( | |
1610 Fts5Context *pCtx, | |
1611 int iCol, | |
1612 sqlite3_int64 *pnToken | |
1613 ){ | |
1614 Fts5Cursor *pCsr = (Fts5Cursor*)pCtx; | |
1615 Fts5Table *pTab = (Fts5Table*)(pCsr->base.pVtab); | |
1616 return sqlite3Fts5StorageSize(pTab->pStorage, iCol, pnToken); | |
1617 } | |
1618 | |
1619 static int fts5ApiRowCount(Fts5Context *pCtx, i64 *pnRow){ | |
1620 Fts5Cursor *pCsr = (Fts5Cursor*)pCtx; | |
1621 Fts5Table *pTab = (Fts5Table*)(pCsr->base.pVtab); | |
1622 return sqlite3Fts5StorageRowCount(pTab->pStorage, pnRow); | |
1623 } | |
1624 | |
1625 static int fts5ApiTokenize( | |
1626 Fts5Context *pCtx, | |
1627 const char *pText, int nText, | |
1628 void *pUserData, | |
1629 int (*xToken)(void*, int, const char*, int, int, int) | |
1630 ){ | |
1631 Fts5Cursor *pCsr = (Fts5Cursor*)pCtx; | |
1632 Fts5Table *pTab = (Fts5Table*)(pCsr->base.pVtab); | |
1633 return sqlite3Fts5Tokenize( | |
1634 pTab->pConfig, FTS5_TOKENIZE_AUX, pText, nText, pUserData, xToken | |
1635 ); | |
1636 } | |
1637 | |
1638 static int fts5ApiPhraseCount(Fts5Context *pCtx){ | |
1639 Fts5Cursor *pCsr = (Fts5Cursor*)pCtx; | |
1640 return sqlite3Fts5ExprPhraseCount(pCsr->pExpr); | |
1641 } | |
1642 | |
1643 static int fts5ApiPhraseSize(Fts5Context *pCtx, int iPhrase){ | |
1644 Fts5Cursor *pCsr = (Fts5Cursor*)pCtx; | |
1645 return sqlite3Fts5ExprPhraseSize(pCsr->pExpr, iPhrase); | |
1646 } | |
1647 | |
1648 static int fts5CsrPoslist(Fts5Cursor *pCsr, int iPhrase, const u8 **pa){ | |
1649 int n; | |
1650 if( pCsr->pSorter ){ | |
1651 Fts5Sorter *pSorter = pCsr->pSorter; | |
1652 int i1 = (iPhrase==0 ? 0 : pSorter->aIdx[iPhrase-1]); | |
1653 n = pSorter->aIdx[iPhrase] - i1; | |
1654 *pa = &pSorter->aPoslist[i1]; | |
1655 }else{ | |
1656 n = sqlite3Fts5ExprPoslist(pCsr->pExpr, iPhrase, pa); | |
1657 } | |
1658 return n; | |
1659 } | |
1660 | |
1661 /* | |
1662 ** Ensure that the Fts5Cursor.nInstCount and aInst[] variables are populated | |
1663 ** correctly for the current view. Return SQLITE_OK if successful, or an | |
1664 ** SQLite error code otherwise. | |
1665 */ | |
1666 static int fts5CacheInstArray(Fts5Cursor *pCsr){ | |
1667 int rc = SQLITE_OK; | |
1668 Fts5PoslistReader *aIter; /* One iterator for each phrase */ | |
1669 int nIter; /* Number of iterators/phrases */ | |
1670 | |
1671 nIter = sqlite3Fts5ExprPhraseCount(pCsr->pExpr); | |
1672 if( pCsr->aInstIter==0 ){ | |
1673 int nByte = sizeof(Fts5PoslistReader) * nIter; | |
1674 pCsr->aInstIter = (Fts5PoslistReader*)sqlite3Fts5MallocZero(&rc, nByte); | |
1675 } | |
1676 aIter = pCsr->aInstIter; | |
1677 | |
1678 if( aIter ){ | |
1679 int nInst = 0; /* Number instances seen so far */ | |
1680 int i; | |
1681 | |
1682 /* Initialize all iterators */ | |
1683 for(i=0; i<nIter; i++){ | |
1684 const u8 *a; | |
1685 int n = fts5CsrPoslist(pCsr, i, &a); | |
1686 sqlite3Fts5PoslistReaderInit(a, n, &aIter[i]); | |
1687 } | |
1688 | |
1689 while( 1 ){ | |
1690 int *aInst; | |
1691 int iBest = -1; | |
1692 for(i=0; i<nIter; i++){ | |
1693 if( (aIter[i].bEof==0) | |
1694 && (iBest<0 || aIter[i].iPos<aIter[iBest].iPos) | |
1695 ){ | |
1696 iBest = i; | |
1697 } | |
1698 } | |
1699 if( iBest<0 ) break; | |
1700 | |
1701 nInst++; | |
1702 if( nInst>=pCsr->nInstAlloc ){ | |
1703 pCsr->nInstAlloc = pCsr->nInstAlloc ? pCsr->nInstAlloc*2 : 32; | |
1704 aInst = (int*)sqlite3_realloc( | |
1705 pCsr->aInst, pCsr->nInstAlloc*sizeof(int)*3 | |
1706 ); | |
1707 if( aInst ){ | |
1708 pCsr->aInst = aInst; | |
1709 }else{ | |
1710 rc = SQLITE_NOMEM; | |
1711 break; | |
1712 } | |
1713 } | |
1714 | |
1715 aInst = &pCsr->aInst[3 * (nInst-1)]; | |
1716 aInst[0] = iBest; | |
1717 aInst[1] = FTS5_POS2COLUMN(aIter[iBest].iPos); | |
1718 aInst[2] = FTS5_POS2OFFSET(aIter[iBest].iPos); | |
1719 sqlite3Fts5PoslistReaderNext(&aIter[iBest]); | |
1720 } | |
1721 | |
1722 pCsr->nInstCount = nInst; | |
1723 CsrFlagClear(pCsr, FTS5CSR_REQUIRE_INST); | |
1724 } | |
1725 return rc; | |
1726 } | |
1727 | |
1728 static int fts5ApiInstCount(Fts5Context *pCtx, int *pnInst){ | |
1729 Fts5Cursor *pCsr = (Fts5Cursor*)pCtx; | |
1730 int rc = SQLITE_OK; | |
1731 if( CsrFlagTest(pCsr, FTS5CSR_REQUIRE_INST)==0 | |
1732 || SQLITE_OK==(rc = fts5CacheInstArray(pCsr)) ){ | |
1733 *pnInst = pCsr->nInstCount; | |
1734 } | |
1735 return rc; | |
1736 } | |
1737 | |
1738 static int fts5ApiInst( | |
1739 Fts5Context *pCtx, | |
1740 int iIdx, | |
1741 int *piPhrase, | |
1742 int *piCol, | |
1743 int *piOff | |
1744 ){ | |
1745 Fts5Cursor *pCsr = (Fts5Cursor*)pCtx; | |
1746 int rc = SQLITE_OK; | |
1747 if( CsrFlagTest(pCsr, FTS5CSR_REQUIRE_INST)==0 | |
1748 || SQLITE_OK==(rc = fts5CacheInstArray(pCsr)) | |
1749 ){ | |
1750 if( iIdx<0 || iIdx>=pCsr->nInstCount ){ | |
1751 rc = SQLITE_RANGE; | |
1752 }else{ | |
1753 *piPhrase = pCsr->aInst[iIdx*3]; | |
1754 *piCol = pCsr->aInst[iIdx*3 + 1]; | |
1755 *piOff = pCsr->aInst[iIdx*3 + 2]; | |
1756 } | |
1757 } | |
1758 return rc; | |
1759 } | |
1760 | |
1761 static sqlite3_int64 fts5ApiRowid(Fts5Context *pCtx){ | |
1762 return fts5CursorRowid((Fts5Cursor*)pCtx); | |
1763 } | |
1764 | |
1765 static int fts5ApiColumnText( | |
1766 Fts5Context *pCtx, | |
1767 int iCol, | |
1768 const char **pz, | |
1769 int *pn | |
1770 ){ | |
1771 int rc = SQLITE_OK; | |
1772 Fts5Cursor *pCsr = (Fts5Cursor*)pCtx; | |
1773 if( fts5IsContentless((Fts5Table*)(pCsr->base.pVtab)) ){ | |
1774 *pz = 0; | |
1775 *pn = 0; | |
1776 }else{ | |
1777 rc = fts5SeekCursor(pCsr, 0); | |
1778 if( rc==SQLITE_OK ){ | |
1779 *pz = (const char*)sqlite3_column_text(pCsr->pStmt, iCol+1); | |
1780 *pn = sqlite3_column_bytes(pCsr->pStmt, iCol+1); | |
1781 } | |
1782 } | |
1783 return rc; | |
1784 } | |
1785 | |
1786 static int fts5ColumnSizeCb( | |
1787 void *pContext, /* Pointer to int */ | |
1788 int tflags, | |
1789 const char *pToken, /* Buffer containing token */ | |
1790 int nToken, /* Size of token in bytes */ | |
1791 int iStart, /* Start offset of token */ | |
1792 int iEnd /* End offset of token */ | |
1793 ){ | |
1794 int *pCnt = (int*)pContext; | |
1795 if( (tflags & FTS5_TOKEN_COLOCATED)==0 ){ | |
1796 (*pCnt)++; | |
1797 } | |
1798 return SQLITE_OK; | |
1799 } | |
1800 | |
1801 static int fts5ApiColumnSize(Fts5Context *pCtx, int iCol, int *pnToken){ | |
1802 Fts5Cursor *pCsr = (Fts5Cursor*)pCtx; | |
1803 Fts5Table *pTab = (Fts5Table*)(pCsr->base.pVtab); | |
1804 Fts5Config *pConfig = pTab->pConfig; | |
1805 int rc = SQLITE_OK; | |
1806 | |
1807 if( CsrFlagTest(pCsr, FTS5CSR_REQUIRE_DOCSIZE) ){ | |
1808 if( pConfig->bColumnsize ){ | |
1809 i64 iRowid = fts5CursorRowid(pCsr); | |
1810 rc = sqlite3Fts5StorageDocsize(pTab->pStorage, iRowid, pCsr->aColumnSize); | |
1811 }else if( pConfig->zContent==0 ){ | |
1812 int i; | |
1813 for(i=0; i<pConfig->nCol; i++){ | |
1814 if( pConfig->abUnindexed[i]==0 ){ | |
1815 pCsr->aColumnSize[i] = -1; | |
1816 } | |
1817 } | |
1818 }else{ | |
1819 int i; | |
1820 for(i=0; rc==SQLITE_OK && i<pConfig->nCol; i++){ | |
1821 if( pConfig->abUnindexed[i]==0 ){ | |
1822 const char *z; int n; | |
1823 void *p = (void*)(&pCsr->aColumnSize[i]); | |
1824 pCsr->aColumnSize[i] = 0; | |
1825 rc = fts5ApiColumnText(pCtx, i, &z, &n); | |
1826 if( rc==SQLITE_OK ){ | |
1827 rc = sqlite3Fts5Tokenize( | |
1828 pConfig, FTS5_TOKENIZE_AUX, z, n, p, fts5ColumnSizeCb | |
1829 ); | |
1830 } | |
1831 } | |
1832 } | |
1833 } | |
1834 CsrFlagClear(pCsr, FTS5CSR_REQUIRE_DOCSIZE); | |
1835 } | |
1836 if( iCol<0 ){ | |
1837 int i; | |
1838 *pnToken = 0; | |
1839 for(i=0; i<pConfig->nCol; i++){ | |
1840 *pnToken += pCsr->aColumnSize[i]; | |
1841 } | |
1842 }else if( iCol<pConfig->nCol ){ | |
1843 *pnToken = pCsr->aColumnSize[iCol]; | |
1844 }else{ | |
1845 *pnToken = 0; | |
1846 rc = SQLITE_RANGE; | |
1847 } | |
1848 return rc; | |
1849 } | |
1850 | |
1851 /* | |
1852 ** Implementation of the xSetAuxdata() method. | |
1853 */ | |
1854 static int fts5ApiSetAuxdata( | |
1855 Fts5Context *pCtx, /* Fts5 context */ | |
1856 void *pPtr, /* Pointer to save as auxdata */ | |
1857 void(*xDelete)(void*) /* Destructor for pPtr (or NULL) */ | |
1858 ){ | |
1859 Fts5Cursor *pCsr = (Fts5Cursor*)pCtx; | |
1860 Fts5Auxdata *pData; | |
1861 | |
1862 /* Search through the cursors list of Fts5Auxdata objects for one that | |
1863 ** corresponds to the currently executing auxiliary function. */ | |
1864 for(pData=pCsr->pAuxdata; pData; pData=pData->pNext){ | |
1865 if( pData->pAux==pCsr->pAux ) break; | |
1866 } | |
1867 | |
1868 if( pData ){ | |
1869 if( pData->xDelete ){ | |
1870 pData->xDelete(pData->pPtr); | |
1871 } | |
1872 }else{ | |
1873 int rc = SQLITE_OK; | |
1874 pData = (Fts5Auxdata*)sqlite3Fts5MallocZero(&rc, sizeof(Fts5Auxdata)); | |
1875 if( pData==0 ){ | |
1876 if( xDelete ) xDelete(pPtr); | |
1877 return rc; | |
1878 } | |
1879 pData->pAux = pCsr->pAux; | |
1880 pData->pNext = pCsr->pAuxdata; | |
1881 pCsr->pAuxdata = pData; | |
1882 } | |
1883 | |
1884 pData->xDelete = xDelete; | |
1885 pData->pPtr = pPtr; | |
1886 return SQLITE_OK; | |
1887 } | |
1888 | |
1889 static void *fts5ApiGetAuxdata(Fts5Context *pCtx, int bClear){ | |
1890 Fts5Cursor *pCsr = (Fts5Cursor*)pCtx; | |
1891 Fts5Auxdata *pData; | |
1892 void *pRet = 0; | |
1893 | |
1894 for(pData=pCsr->pAuxdata; pData; pData=pData->pNext){ | |
1895 if( pData->pAux==pCsr->pAux ) break; | |
1896 } | |
1897 | |
1898 if( pData ){ | |
1899 pRet = pData->pPtr; | |
1900 if( bClear ){ | |
1901 pData->pPtr = 0; | |
1902 pData->xDelete = 0; | |
1903 } | |
1904 } | |
1905 | |
1906 return pRet; | |
1907 } | |
1908 | |
1909 static void fts5ApiPhraseNext( | |
1910 Fts5Context *pCtx, | |
1911 Fts5PhraseIter *pIter, | |
1912 int *piCol, int *piOff | |
1913 ){ | |
1914 if( pIter->a>=pIter->b ){ | |
1915 *piCol = -1; | |
1916 *piOff = -1; | |
1917 }else{ | |
1918 int iVal; | |
1919 pIter->a += fts5GetVarint32(pIter->a, iVal); | |
1920 if( iVal==1 ){ | |
1921 pIter->a += fts5GetVarint32(pIter->a, iVal); | |
1922 *piCol = iVal; | |
1923 *piOff = 0; | |
1924 pIter->a += fts5GetVarint32(pIter->a, iVal); | |
1925 } | |
1926 *piOff += (iVal-2); | |
1927 } | |
1928 } | |
1929 | |
1930 static void fts5ApiPhraseFirst( | |
1931 Fts5Context *pCtx, | |
1932 int iPhrase, | |
1933 Fts5PhraseIter *pIter, | |
1934 int *piCol, int *piOff | |
1935 ){ | |
1936 Fts5Cursor *pCsr = (Fts5Cursor*)pCtx; | |
1937 int n = fts5CsrPoslist(pCsr, iPhrase, &pIter->a); | |
1938 pIter->b = &pIter->a[n]; | |
1939 *piCol = 0; | |
1940 *piOff = 0; | |
1941 fts5ApiPhraseNext(pCtx, pIter, piCol, piOff); | |
1942 } | |
1943 | |
1944 static int fts5ApiQueryPhrase(Fts5Context*, int, void*, | |
1945 int(*)(const Fts5ExtensionApi*, Fts5Context*, void*) | |
1946 ); | |
1947 | |
1948 static const Fts5ExtensionApi sFts5Api = { | |
1949 2, /* iVersion */ | |
1950 fts5ApiUserData, | |
1951 fts5ApiColumnCount, | |
1952 fts5ApiRowCount, | |
1953 fts5ApiColumnTotalSize, | |
1954 fts5ApiTokenize, | |
1955 fts5ApiPhraseCount, | |
1956 fts5ApiPhraseSize, | |
1957 fts5ApiInstCount, | |
1958 fts5ApiInst, | |
1959 fts5ApiRowid, | |
1960 fts5ApiColumnText, | |
1961 fts5ApiColumnSize, | |
1962 fts5ApiQueryPhrase, | |
1963 fts5ApiSetAuxdata, | |
1964 fts5ApiGetAuxdata, | |
1965 fts5ApiPhraseFirst, | |
1966 fts5ApiPhraseNext, | |
1967 }; | |
1968 | |
1969 | |
1970 /* | |
1971 ** Implementation of API function xQueryPhrase(). | |
1972 */ | |
1973 static int fts5ApiQueryPhrase( | |
1974 Fts5Context *pCtx, | |
1975 int iPhrase, | |
1976 void *pUserData, | |
1977 int(*xCallback)(const Fts5ExtensionApi*, Fts5Context*, void*) | |
1978 ){ | |
1979 Fts5Cursor *pCsr = (Fts5Cursor*)pCtx; | |
1980 Fts5Table *pTab = (Fts5Table*)(pCsr->base.pVtab); | |
1981 int rc; | |
1982 Fts5Cursor *pNew = 0; | |
1983 | |
1984 rc = fts5OpenMethod(pCsr->base.pVtab, (sqlite3_vtab_cursor**)&pNew); | |
1985 if( rc==SQLITE_OK ){ | |
1986 Fts5Config *pConf = pTab->pConfig; | |
1987 pNew->ePlan = FTS5_PLAN_MATCH; | |
1988 pNew->iFirstRowid = SMALLEST_INT64; | |
1989 pNew->iLastRowid = LARGEST_INT64; | |
1990 pNew->base.pVtab = (sqlite3_vtab*)pTab; | |
1991 rc = sqlite3Fts5ExprClonePhrase(pConf, pCsr->pExpr, iPhrase, &pNew->pExpr); | |
1992 } | |
1993 | |
1994 if( rc==SQLITE_OK ){ | |
1995 for(rc = fts5CursorFirst(pTab, pNew, 0); | |
1996 rc==SQLITE_OK && CsrFlagTest(pNew, FTS5CSR_EOF)==0; | |
1997 rc = fts5NextMethod((sqlite3_vtab_cursor*)pNew) | |
1998 ){ | |
1999 rc = xCallback(&sFts5Api, (Fts5Context*)pNew, pUserData); | |
2000 if( rc!=SQLITE_OK ){ | |
2001 if( rc==SQLITE_DONE ) rc = SQLITE_OK; | |
2002 break; | |
2003 } | |
2004 } | |
2005 } | |
2006 | |
2007 fts5CloseMethod((sqlite3_vtab_cursor*)pNew); | |
2008 return rc; | |
2009 } | |
2010 | |
2011 static void fts5ApiInvoke( | |
2012 Fts5Auxiliary *pAux, | |
2013 Fts5Cursor *pCsr, | |
2014 sqlite3_context *context, | |
2015 int argc, | |
2016 sqlite3_value **argv | |
2017 ){ | |
2018 assert( pCsr->pAux==0 ); | |
2019 pCsr->pAux = pAux; | |
2020 pAux->xFunc(&sFts5Api, (Fts5Context*)pCsr, context, argc, argv); | |
2021 pCsr->pAux = 0; | |
2022 } | |
2023 | |
2024 static Fts5Cursor *fts5CursorFromCsrid(Fts5Global *pGlobal, i64 iCsrId){ | |
2025 Fts5Cursor *pCsr; | |
2026 for(pCsr=pGlobal->pCsr; pCsr; pCsr=pCsr->pNext){ | |
2027 if( pCsr->iCsrId==iCsrId ) break; | |
2028 } | |
2029 return pCsr; | |
2030 } | |
2031 | |
2032 static void fts5ApiCallback( | |
2033 sqlite3_context *context, | |
2034 int argc, | |
2035 sqlite3_value **argv | |
2036 ){ | |
2037 | |
2038 Fts5Auxiliary *pAux; | |
2039 Fts5Cursor *pCsr; | |
2040 i64 iCsrId; | |
2041 | |
2042 assert( argc>=1 ); | |
2043 pAux = (Fts5Auxiliary*)sqlite3_user_data(context); | |
2044 iCsrId = sqlite3_value_int64(argv[0]); | |
2045 | |
2046 pCsr = fts5CursorFromCsrid(pAux->pGlobal, iCsrId); | |
2047 if( pCsr==0 ){ | |
2048 char *zErr = sqlite3_mprintf("no such cursor: %lld", iCsrId); | |
2049 sqlite3_result_error(context, zErr, -1); | |
2050 sqlite3_free(zErr); | |
2051 }else{ | |
2052 fts5ApiInvoke(pAux, pCsr, context, argc-1, &argv[1]); | |
2053 } | |
2054 } | |
2055 | |
2056 | |
2057 /* | |
2058 ** Given cursor id iId, return a pointer to the corresponding Fts5Index | |
2059 ** object. Or NULL If the cursor id does not exist. | |
2060 ** | |
2061 ** If successful, set *ppConfig to point to the associated config object | |
2062 ** before returning. | |
2063 */ | |
2064 Fts5Index *sqlite3Fts5IndexFromCsrid( | |
2065 Fts5Global *pGlobal, /* FTS5 global context for db handle */ | |
2066 i64 iCsrId, /* Id of cursor to find */ | |
2067 Fts5Config **ppConfig /* OUT: Configuration object */ | |
2068 ){ | |
2069 Fts5Cursor *pCsr; | |
2070 Fts5Table *pTab; | |
2071 | |
2072 pCsr = fts5CursorFromCsrid(pGlobal, iCsrId); | |
2073 pTab = (Fts5Table*)pCsr->base.pVtab; | |
2074 *ppConfig = pTab->pConfig; | |
2075 | |
2076 return pTab->pIndex; | |
2077 } | |
2078 | |
2079 /* | |
2080 ** Return a "position-list blob" corresponding to the current position of | |
2081 ** cursor pCsr via sqlite3_result_blob(). A position-list blob contains | |
2082 ** the current position-list for each phrase in the query associated with | |
2083 ** cursor pCsr. | |
2084 ** | |
2085 ** A position-list blob begins with (nPhrase-1) varints, where nPhrase is | |
2086 ** the number of phrases in the query. Following the varints are the | |
2087 ** concatenated position lists for each phrase, in order. | |
2088 ** | |
2089 ** The first varint (if it exists) contains the size of the position list | |
2090 ** for phrase 0. The second (same disclaimer) contains the size of position | |
2091 ** list 1. And so on. There is no size field for the final position list, | |
2092 ** as it can be derived from the total size of the blob. | |
2093 */ | |
2094 static int fts5PoslistBlob(sqlite3_context *pCtx, Fts5Cursor *pCsr){ | |
2095 int i; | |
2096 int rc = SQLITE_OK; | |
2097 int nPhrase = sqlite3Fts5ExprPhraseCount(pCsr->pExpr); | |
2098 Fts5Buffer val; | |
2099 | |
2100 memset(&val, 0, sizeof(Fts5Buffer)); | |
2101 | |
2102 /* Append the varints */ | |
2103 for(i=0; i<(nPhrase-1); i++){ | |
2104 const u8 *dummy; | |
2105 int nByte = sqlite3Fts5ExprPoslist(pCsr->pExpr, i, &dummy); | |
2106 sqlite3Fts5BufferAppendVarint(&rc, &val, nByte); | |
2107 } | |
2108 | |
2109 /* Append the position lists */ | |
2110 for(i=0; i<nPhrase; i++){ | |
2111 const u8 *pPoslist; | |
2112 int nPoslist; | |
2113 nPoslist = sqlite3Fts5ExprPoslist(pCsr->pExpr, i, &pPoslist); | |
2114 sqlite3Fts5BufferAppendBlob(&rc, &val, nPoslist, pPoslist); | |
2115 } | |
2116 | |
2117 sqlite3_result_blob(pCtx, val.p, val.n, sqlite3_free); | |
2118 return rc; | |
2119 } | |
2120 | |
2121 /* | |
2122 ** This is the xColumn method, called by SQLite to request a value from | |
2123 ** the row that the supplied cursor currently points to. | |
2124 */ | |
2125 static int fts5ColumnMethod( | |
2126 sqlite3_vtab_cursor *pCursor, /* Cursor to retrieve value from */ | |
2127 sqlite3_context *pCtx, /* Context for sqlite3_result_xxx() calls */ | |
2128 int iCol /* Index of column to read value from */ | |
2129 ){ | |
2130 Fts5Table *pTab = (Fts5Table*)(pCursor->pVtab); | |
2131 Fts5Config *pConfig = pTab->pConfig; | |
2132 Fts5Cursor *pCsr = (Fts5Cursor*)pCursor; | |
2133 int rc = SQLITE_OK; | |
2134 | |
2135 assert( CsrFlagTest(pCsr, FTS5CSR_EOF)==0 ); | |
2136 | |
2137 if( pCsr->ePlan==FTS5_PLAN_SPECIAL ){ | |
2138 if( iCol==pConfig->nCol ){ | |
2139 sqlite3_result_int64(pCtx, pCsr->iSpecial); | |
2140 } | |
2141 }else | |
2142 | |
2143 if( iCol==pConfig->nCol ){ | |
2144 /* User is requesting the value of the special column with the same name | |
2145 ** as the table. Return the cursor integer id number. This value is only | |
2146 ** useful in that it may be passed as the first argument to an FTS5 | |
2147 ** auxiliary function. */ | |
2148 sqlite3_result_int64(pCtx, pCsr->iCsrId); | |
2149 }else if( iCol==pConfig->nCol+1 ){ | |
2150 | |
2151 /* The value of the "rank" column. */ | |
2152 if( pCsr->ePlan==FTS5_PLAN_SOURCE ){ | |
2153 fts5PoslistBlob(pCtx, pCsr); | |
2154 }else if( | |
2155 pCsr->ePlan==FTS5_PLAN_MATCH | |
2156 || pCsr->ePlan==FTS5_PLAN_SORTED_MATCH | |
2157 ){ | |
2158 if( pCsr->pRank || SQLITE_OK==(rc = fts5FindRankFunction(pCsr)) ){ | |
2159 fts5ApiInvoke(pCsr->pRank, pCsr, pCtx, pCsr->nRankArg, pCsr->apRankArg); | |
2160 } | |
2161 } | |
2162 }else if( !fts5IsContentless(pTab) ){ | |
2163 rc = fts5SeekCursor(pCsr, 1); | |
2164 if( rc==SQLITE_OK ){ | |
2165 sqlite3_result_value(pCtx, sqlite3_column_value(pCsr->pStmt, iCol+1)); | |
2166 } | |
2167 } | |
2168 return rc; | |
2169 } | |
2170 | |
2171 | |
2172 /* | |
2173 ** This routine implements the xFindFunction method for the FTS3 | |
2174 ** virtual table. | |
2175 */ | |
2176 static int fts5FindFunctionMethod( | |
2177 sqlite3_vtab *pVtab, /* Virtual table handle */ | |
2178 int nArg, /* Number of SQL function arguments */ | |
2179 const char *zName, /* Name of SQL function */ | |
2180 void (**pxFunc)(sqlite3_context*,int,sqlite3_value**), /* OUT: Result */ | |
2181 void **ppArg /* OUT: User data for *pxFunc */ | |
2182 ){ | |
2183 Fts5Table *pTab = (Fts5Table*)pVtab; | |
2184 Fts5Auxiliary *pAux; | |
2185 | |
2186 pAux = fts5FindAuxiliary(pTab, zName); | |
2187 if( pAux ){ | |
2188 *pxFunc = fts5ApiCallback; | |
2189 *ppArg = (void*)pAux; | |
2190 return 1; | |
2191 } | |
2192 | |
2193 /* No function of the specified name was found. Return 0. */ | |
2194 return 0; | |
2195 } | |
2196 | |
2197 /* | |
2198 ** Implementation of FTS5 xRename method. Rename an fts5 table. | |
2199 */ | |
2200 static int fts5RenameMethod( | |
2201 sqlite3_vtab *pVtab, /* Virtual table handle */ | |
2202 const char *zName /* New name of table */ | |
2203 ){ | |
2204 Fts5Table *pTab = (Fts5Table*)pVtab; | |
2205 return sqlite3Fts5StorageRename(pTab->pStorage, zName); | |
2206 } | |
2207 | |
2208 /* | |
2209 ** The xSavepoint() method. | |
2210 ** | |
2211 ** Flush the contents of the pending-terms table to disk. | |
2212 */ | |
2213 static int fts5SavepointMethod(sqlite3_vtab *pVtab, int iSavepoint){ | |
2214 Fts5Table *pTab = (Fts5Table*)pVtab; | |
2215 fts5CheckTransactionState(pTab, FTS5_SAVEPOINT, iSavepoint); | |
2216 fts5TripCursors(pTab); | |
2217 return sqlite3Fts5StorageSync(pTab->pStorage, 0); | |
2218 } | |
2219 | |
2220 /* | |
2221 ** The xRelease() method. | |
2222 ** | |
2223 ** This is a no-op. | |
2224 */ | |
2225 static int fts5ReleaseMethod(sqlite3_vtab *pVtab, int iSavepoint){ | |
2226 Fts5Table *pTab = (Fts5Table*)pVtab; | |
2227 fts5CheckTransactionState(pTab, FTS5_RELEASE, iSavepoint); | |
2228 fts5TripCursors(pTab); | |
2229 return sqlite3Fts5StorageSync(pTab->pStorage, 0); | |
2230 } | |
2231 | |
2232 /* | |
2233 ** The xRollbackTo() method. | |
2234 ** | |
2235 ** Discard the contents of the pending terms table. | |
2236 */ | |
2237 static int fts5RollbackToMethod(sqlite3_vtab *pVtab, int iSavepoint){ | |
2238 Fts5Table *pTab = (Fts5Table*)pVtab; | |
2239 fts5CheckTransactionState(pTab, FTS5_ROLLBACKTO, iSavepoint); | |
2240 fts5TripCursors(pTab); | |
2241 return sqlite3Fts5StorageRollback(pTab->pStorage); | |
2242 } | |
2243 | |
2244 /* | |
2245 ** Register a new auxiliary function with global context pGlobal. | |
2246 */ | |
2247 static int fts5CreateAux( | |
2248 fts5_api *pApi, /* Global context (one per db handle) */ | |
2249 const char *zName, /* Name of new function */ | |
2250 void *pUserData, /* User data for aux. function */ | |
2251 fts5_extension_function xFunc, /* Aux. function implementation */ | |
2252 void(*xDestroy)(void*) /* Destructor for pUserData */ | |
2253 ){ | |
2254 Fts5Global *pGlobal = (Fts5Global*)pApi; | |
2255 int rc = sqlite3_overload_function(pGlobal->db, zName, -1); | |
2256 if( rc==SQLITE_OK ){ | |
2257 Fts5Auxiliary *pAux; | |
2258 int nName; /* Size of zName in bytes, including \0 */ | |
2259 int nByte; /* Bytes of space to allocate */ | |
2260 | |
2261 nName = (int)strlen(zName) + 1; | |
2262 nByte = sizeof(Fts5Auxiliary) + nName; | |
2263 pAux = (Fts5Auxiliary*)sqlite3_malloc(nByte); | |
2264 if( pAux ){ | |
2265 memset(pAux, 0, nByte); | |
2266 pAux->zFunc = (char*)&pAux[1]; | |
2267 memcpy(pAux->zFunc, zName, nName); | |
2268 pAux->pGlobal = pGlobal; | |
2269 pAux->pUserData = pUserData; | |
2270 pAux->xFunc = xFunc; | |
2271 pAux->xDestroy = xDestroy; | |
2272 pAux->pNext = pGlobal->pAux; | |
2273 pGlobal->pAux = pAux; | |
2274 }else{ | |
2275 rc = SQLITE_NOMEM; | |
2276 } | |
2277 } | |
2278 | |
2279 return rc; | |
2280 } | |
2281 | |
2282 /* | |
2283 ** Register a new tokenizer. This is the implementation of the | |
2284 ** fts5_api.xCreateTokenizer() method. | |
2285 */ | |
2286 static int fts5CreateTokenizer( | |
2287 fts5_api *pApi, /* Global context (one per db handle) */ | |
2288 const char *zName, /* Name of new function */ | |
2289 void *pUserData, /* User data for aux. function */ | |
2290 fts5_tokenizer *pTokenizer, /* Tokenizer implementation */ | |
2291 void(*xDestroy)(void*) /* Destructor for pUserData */ | |
2292 ){ | |
2293 Fts5Global *pGlobal = (Fts5Global*)pApi; | |
2294 Fts5TokenizerModule *pNew; | |
2295 int nName; /* Size of zName and its \0 terminator */ | |
2296 int nByte; /* Bytes of space to allocate */ | |
2297 int rc = SQLITE_OK; | |
2298 | |
2299 nName = (int)strlen(zName) + 1; | |
2300 nByte = sizeof(Fts5TokenizerModule) + nName; | |
2301 pNew = (Fts5TokenizerModule*)sqlite3_malloc(nByte); | |
2302 if( pNew ){ | |
2303 memset(pNew, 0, nByte); | |
2304 pNew->zName = (char*)&pNew[1]; | |
2305 memcpy(pNew->zName, zName, nName); | |
2306 pNew->pUserData = pUserData; | |
2307 pNew->x = *pTokenizer; | |
2308 pNew->xDestroy = xDestroy; | |
2309 pNew->pNext = pGlobal->pTok; | |
2310 pGlobal->pTok = pNew; | |
2311 if( pNew->pNext==0 ){ | |
2312 pGlobal->pDfltTok = pNew; | |
2313 } | |
2314 }else{ | |
2315 rc = SQLITE_NOMEM; | |
2316 } | |
2317 | |
2318 return rc; | |
2319 } | |
2320 | |
2321 static Fts5TokenizerModule *fts5LocateTokenizer( | |
2322 Fts5Global *pGlobal, | |
2323 const char *zName | |
2324 ){ | |
2325 Fts5TokenizerModule *pMod = 0; | |
2326 | |
2327 if( zName==0 ){ | |
2328 pMod = pGlobal->pDfltTok; | |
2329 }else{ | |
2330 for(pMod=pGlobal->pTok; pMod; pMod=pMod->pNext){ | |
2331 if( sqlite3_stricmp(zName, pMod->zName)==0 ) break; | |
2332 } | |
2333 } | |
2334 | |
2335 return pMod; | |
2336 } | |
2337 | |
2338 /* | |
2339 ** Find a tokenizer. This is the implementation of the | |
2340 ** fts5_api.xFindTokenizer() method. | |
2341 */ | |
2342 static int fts5FindTokenizer( | |
2343 fts5_api *pApi, /* Global context (one per db handle) */ | |
2344 const char *zName, /* Name of new function */ | |
2345 void **ppUserData, | |
2346 fts5_tokenizer *pTokenizer /* Populate this object */ | |
2347 ){ | |
2348 int rc = SQLITE_OK; | |
2349 Fts5TokenizerModule *pMod; | |
2350 | |
2351 pMod = fts5LocateTokenizer((Fts5Global*)pApi, zName); | |
2352 if( pMod ){ | |
2353 *pTokenizer = pMod->x; | |
2354 *ppUserData = pMod->pUserData; | |
2355 }else{ | |
2356 memset(pTokenizer, 0, sizeof(fts5_tokenizer)); | |
2357 rc = SQLITE_ERROR; | |
2358 } | |
2359 | |
2360 return rc; | |
2361 } | |
2362 | |
2363 int sqlite3Fts5GetTokenizer( | |
2364 Fts5Global *pGlobal, | |
2365 const char **azArg, | |
2366 int nArg, | |
2367 Fts5Tokenizer **ppTok, | |
2368 fts5_tokenizer **ppTokApi, | |
2369 char **pzErr | |
2370 ){ | |
2371 Fts5TokenizerModule *pMod; | |
2372 int rc = SQLITE_OK; | |
2373 | |
2374 pMod = fts5LocateTokenizer(pGlobal, nArg==0 ? 0 : azArg[0]); | |
2375 if( pMod==0 ){ | |
2376 assert( nArg>0 ); | |
2377 rc = SQLITE_ERROR; | |
2378 *pzErr = sqlite3_mprintf("no such tokenizer: %s", azArg[0]); | |
2379 }else{ | |
2380 rc = pMod->x.xCreate(pMod->pUserData, &azArg[1], (nArg?nArg-1:0), ppTok); | |
2381 *ppTokApi = &pMod->x; | |
2382 if( rc!=SQLITE_OK && pzErr ){ | |
2383 *pzErr = sqlite3_mprintf("error in tokenizer constructor"); | |
2384 } | |
2385 } | |
2386 | |
2387 if( rc!=SQLITE_OK ){ | |
2388 *ppTokApi = 0; | |
2389 *ppTok = 0; | |
2390 } | |
2391 | |
2392 return rc; | |
2393 } | |
2394 | |
2395 static void fts5ModuleDestroy(void *pCtx){ | |
2396 Fts5TokenizerModule *pTok, *pNextTok; | |
2397 Fts5Auxiliary *pAux, *pNextAux; | |
2398 Fts5Global *pGlobal = (Fts5Global*)pCtx; | |
2399 | |
2400 for(pAux=pGlobal->pAux; pAux; pAux=pNextAux){ | |
2401 pNextAux = pAux->pNext; | |
2402 if( pAux->xDestroy ) pAux->xDestroy(pAux->pUserData); | |
2403 sqlite3_free(pAux); | |
2404 } | |
2405 | |
2406 for(pTok=pGlobal->pTok; pTok; pTok=pNextTok){ | |
2407 pNextTok = pTok->pNext; | |
2408 if( pTok->xDestroy ) pTok->xDestroy(pTok->pUserData); | |
2409 sqlite3_free(pTok); | |
2410 } | |
2411 | |
2412 sqlite3_free(pGlobal); | |
2413 } | |
2414 | |
2415 static void fts5Fts5Func( | |
2416 sqlite3_context *pCtx, /* Function call context */ | |
2417 int nArg, /* Number of args */ | |
2418 sqlite3_value **apVal /* Function arguments */ | |
2419 ){ | |
2420 Fts5Global *pGlobal = (Fts5Global*)sqlite3_user_data(pCtx); | |
2421 char buf[8]; | |
2422 assert( nArg==0 ); | |
2423 assert( sizeof(buf)>=sizeof(pGlobal) ); | |
2424 memcpy(buf, (void*)&pGlobal, sizeof(pGlobal)); | |
2425 sqlite3_result_blob(pCtx, buf, sizeof(pGlobal), SQLITE_TRANSIENT); | |
2426 } | |
2427 | |
2428 /* | |
2429 ** Implementation of fts5_source_id() function. | |
2430 */ | |
2431 static void fts5SourceIdFunc( | |
2432 sqlite3_context *pCtx, /* Function call context */ | |
2433 int nArg, /* Number of args */ | |
2434 sqlite3_value **apVal /* Function arguments */ | |
2435 ){ | |
2436 assert( nArg==0 ); | |
2437 sqlite3_result_text(pCtx, "--FTS5-SOURCE-ID--", -1, SQLITE_TRANSIENT); | |
2438 } | |
2439 | |
2440 static int fts5Init(sqlite3 *db){ | |
2441 static const sqlite3_module fts5Mod = { | |
2442 /* iVersion */ 2, | |
2443 /* xCreate */ fts5CreateMethod, | |
2444 /* xConnect */ fts5ConnectMethod, | |
2445 /* xBestIndex */ fts5BestIndexMethod, | |
2446 /* xDisconnect */ fts5DisconnectMethod, | |
2447 /* xDestroy */ fts5DestroyMethod, | |
2448 /* xOpen */ fts5OpenMethod, | |
2449 /* xClose */ fts5CloseMethod, | |
2450 /* xFilter */ fts5FilterMethod, | |
2451 /* xNext */ fts5NextMethod, | |
2452 /* xEof */ fts5EofMethod, | |
2453 /* xColumn */ fts5ColumnMethod, | |
2454 /* xRowid */ fts5RowidMethod, | |
2455 /* xUpdate */ fts5UpdateMethod, | |
2456 /* xBegin */ fts5BeginMethod, | |
2457 /* xSync */ fts5SyncMethod, | |
2458 /* xCommit */ fts5CommitMethod, | |
2459 /* xRollback */ fts5RollbackMethod, | |
2460 /* xFindFunction */ fts5FindFunctionMethod, | |
2461 /* xRename */ fts5RenameMethod, | |
2462 /* xSavepoint */ fts5SavepointMethod, | |
2463 /* xRelease */ fts5ReleaseMethod, | |
2464 /* xRollbackTo */ fts5RollbackToMethod, | |
2465 }; | |
2466 | |
2467 int rc; | |
2468 Fts5Global *pGlobal = 0; | |
2469 | |
2470 pGlobal = (Fts5Global*)sqlite3_malloc(sizeof(Fts5Global)); | |
2471 if( pGlobal==0 ){ | |
2472 rc = SQLITE_NOMEM; | |
2473 }else{ | |
2474 void *p = (void*)pGlobal; | |
2475 memset(pGlobal, 0, sizeof(Fts5Global)); | |
2476 pGlobal->db = db; | |
2477 pGlobal->api.iVersion = 2; | |
2478 pGlobal->api.xCreateFunction = fts5CreateAux; | |
2479 pGlobal->api.xCreateTokenizer = fts5CreateTokenizer; | |
2480 pGlobal->api.xFindTokenizer = fts5FindTokenizer; | |
2481 rc = sqlite3_create_module_v2(db, "fts5", &fts5Mod, p, fts5ModuleDestroy); | |
2482 if( rc==SQLITE_OK ) rc = sqlite3Fts5IndexInit(db); | |
2483 if( rc==SQLITE_OK ) rc = sqlite3Fts5ExprInit(pGlobal, db); | |
2484 if( rc==SQLITE_OK ) rc = sqlite3Fts5AuxInit(&pGlobal->api); | |
2485 if( rc==SQLITE_OK ) rc = sqlite3Fts5TokenizerInit(&pGlobal->api); | |
2486 if( rc==SQLITE_OK ) rc = sqlite3Fts5VocabInit(pGlobal, db); | |
2487 if( rc==SQLITE_OK ){ | |
2488 rc = sqlite3_create_function( | |
2489 db, "fts5", 0, SQLITE_UTF8, p, fts5Fts5Func, 0, 0 | |
2490 ); | |
2491 } | |
2492 if( rc==SQLITE_OK ){ | |
2493 rc = sqlite3_create_function( | |
2494 db, "fts5_source_id", 0, SQLITE_UTF8, p, fts5SourceIdFunc, 0, 0 | |
2495 ); | |
2496 } | |
2497 } | |
2498 return rc; | |
2499 } | |
2500 | |
2501 /* | |
2502 ** The following functions are used to register the module with SQLite. If | |
2503 ** this module is being built as part of the SQLite core (SQLITE_CORE is | |
2504 ** defined), then sqlite3_open() will call sqlite3Fts5Init() directly. | |
2505 ** | |
2506 ** Or, if this module is being built as a loadable extension, | |
2507 ** sqlite3Fts5Init() is omitted and the two standard entry points | |
2508 ** sqlite3_fts_init() and sqlite3_fts5_init() defined instead. | |
2509 */ | |
2510 #ifndef SQLITE_CORE | |
2511 #ifdef _WIN32 | |
2512 __declspec(dllexport) | |
2513 #endif | |
2514 int sqlite3_fts_init( | |
2515 sqlite3 *db, | |
2516 char **pzErrMsg, | |
2517 const sqlite3_api_routines *pApi | |
2518 ){ | |
2519 SQLITE_EXTENSION_INIT2(pApi); | |
2520 (void)pzErrMsg; /* Unused parameter */ | |
2521 return fts5Init(db); | |
2522 } | |
2523 | |
2524 #ifdef _WIN32 | |
2525 __declspec(dllexport) | |
2526 #endif | |
2527 int sqlite3_fts5_init( | |
2528 sqlite3 *db, | |
2529 char **pzErrMsg, | |
2530 const sqlite3_api_routines *pApi | |
2531 ){ | |
2532 SQLITE_EXTENSION_INIT2(pApi); | |
2533 (void)pzErrMsg; /* Unused parameter */ | |
2534 return fts5Init(db); | |
2535 } | |
2536 #else | |
2537 int sqlite3Fts5Init(sqlite3 *db){ | |
2538 return fts5Init(db); | |
2539 } | |
2540 #endif | |
OLD | NEW |