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 |