Fossil SCM
Update the built-in SQLite to the latest 3.11.0 alpha version.
Commit
c9fad621f72c44cc33c8bb6996a30c6562cd144e
Parent
2e1ccc6a36d4de8…
2 files changed
+2057
-759
+73
-9
+2057
-759
| --- src/sqlite3.c | ||
| +++ src/sqlite3.c | ||
| @@ -1,8 +1,8 @@ | ||
| 1 | 1 | /****************************************************************************** |
| 2 | 2 | ** This file is an amalgamation of many separate C source files from SQLite |
| 3 | -** version 3.10.0. By combining all the individual C code files into this | |
| 3 | +** version 3.11.0. By combining all the individual C code files into this | |
| 4 | 4 | ** single large file, the entire code can be compiled as a single translation |
| 5 | 5 | ** unit. This allows many compilers to do optimizations that would not be |
| 6 | 6 | ** possible if the files were compiled separately. Performance improvements |
| 7 | 7 | ** of 5% or more are commonly seen when SQLite is compiled as a single |
| 8 | 8 | ** translation unit. |
| @@ -119,10 +119,12 @@ | ||
| 119 | 119 | #define SQLITE_ENABLE_LOCKING_STYLE 0 |
| 120 | 120 | #define HAVE_UTIME 1 |
| 121 | 121 | #else |
| 122 | 122 | /* This is not VxWorks. */ |
| 123 | 123 | #define OS_VXWORKS 0 |
| 124 | +#define HAVE_FCHOWN 1 | |
| 125 | +#define HAVE_READLINK 1 | |
| 124 | 126 | #endif /* defined(_WRS_KERNEL) */ |
| 125 | 127 | |
| 126 | 128 | /************** End of vxworks.h *********************************************/ |
| 127 | 129 | /************** Continuing where we left off in sqliteInt.h ******************/ |
| 128 | 130 | |
| @@ -323,13 +325,13 @@ | ||
| 323 | 325 | ** |
| 324 | 326 | ** See also: [sqlite3_libversion()], |
| 325 | 327 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 326 | 328 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 327 | 329 | */ |
| 328 | -#define SQLITE_VERSION "3.10.0" | |
| 329 | -#define SQLITE_VERSION_NUMBER 3010000 | |
| 330 | -#define SQLITE_SOURCE_ID "2016-01-06 11:01:07 fd0a50f0797d154fefff724624f00548b5320566" | |
| 330 | +#define SQLITE_VERSION "3.11.0" | |
| 331 | +#define SQLITE_VERSION_NUMBER 3011000 | |
| 332 | +#define SQLITE_SOURCE_ID "2016-01-14 14:19:50 d17bc2c92f4d086280e49a3cc72993be7fee2da7" | |
| 331 | 333 | |
| 332 | 334 | /* |
| 333 | 335 | ** CAPI3REF: Run-Time Library Version Numbers |
| 334 | 336 | ** KEYWORDS: sqlite3_version, sqlite3_sourceid |
| 335 | 337 | ** |
| @@ -1006,12 +1008,17 @@ | ||
| 1006 | 1008 | ** improve performance on some systems. |
| 1007 | 1009 | ** |
| 1008 | 1010 | ** <li>[[SQLITE_FCNTL_FILE_POINTER]] |
| 1009 | 1011 | ** The [SQLITE_FCNTL_FILE_POINTER] opcode is used to obtain a pointer |
| 1010 | 1012 | ** to the [sqlite3_file] object associated with a particular database |
| 1011 | -** connection. See the [sqlite3_file_control()] documentation for | |
| 1012 | -** additional information. | |
| 1013 | +** connection. See also [SQLITE_FCNTL_JOURNAL_POINTER]. | |
| 1014 | +** | |
| 1015 | +** <li>[[SQLITE_FCNTL_JOURNAL_POINTER]] | |
| 1016 | +** The [SQLITE_FCNTL_JOURNAL_POINTER] opcode is used to obtain a pointer | |
| 1017 | +** to the [sqlite3_file] object associated with the journal file (either | |
| 1018 | +** the [rollback journal] or the [write-ahead log]) for a particular database | |
| 1019 | +** connection. See also [SQLITE_FCNTL_FILE_POINTER]. | |
| 1013 | 1020 | ** |
| 1014 | 1021 | ** <li>[[SQLITE_FCNTL_SYNC_OMITTED]] |
| 1015 | 1022 | ** No longer in use. |
| 1016 | 1023 | ** |
| 1017 | 1024 | ** <li>[[SQLITE_FCNTL_SYNC]] |
| @@ -1222,10 +1229,11 @@ | ||
| 1222 | 1229 | #define SQLITE_FCNTL_WIN32_SET_HANDLE 23 |
| 1223 | 1230 | #define SQLITE_FCNTL_WAL_BLOCK 24 |
| 1224 | 1231 | #define SQLITE_FCNTL_ZIPVFS 25 |
| 1225 | 1232 | #define SQLITE_FCNTL_RBU 26 |
| 1226 | 1233 | #define SQLITE_FCNTL_VFS_POINTER 27 |
| 1234 | +#define SQLITE_FCNTL_JOURNAL_POINTER 28 | |
| 1227 | 1235 | |
| 1228 | 1236 | /* deprecated names */ |
| 1229 | 1237 | #define SQLITE_GET_LOCKPROXYFILE SQLITE_FCNTL_GET_LOCKPROXYFILE |
| 1230 | 1238 | #define SQLITE_SET_LOCKPROXYFILE SQLITE_FCNTL_SET_LOCKPROXYFILE |
| 1231 | 1239 | #define SQLITE_LAST_ERRNO SQLITE_FCNTL_LAST_ERRNO |
| @@ -8399,10 +8407,13 @@ | ||
| 8399 | 8407 | ** If parameter iCol is greater than or equal to the number of columns |
| 8400 | 8408 | ** in the table, SQLITE_RANGE is returned. Or, if an error occurs (e.g. |
| 8401 | 8409 | ** an OOM condition or IO error), an appropriate SQLite error code is |
| 8402 | 8410 | ** returned. |
| 8403 | 8411 | ** |
| 8412 | +** This function may be quite inefficient if used with an FTS5 table | |
| 8413 | +** created with the "columnsize=0" option. | |
| 8414 | +** | |
| 8404 | 8415 | ** xColumnText: |
| 8405 | 8416 | ** This function attempts to retrieve the text of column iCol of the |
| 8406 | 8417 | ** current document. If successful, (*pz) is set to point to a buffer |
| 8407 | 8418 | ** containing the text in utf-8 encoding, (*pn) is set to the size in bytes |
| 8408 | 8419 | ** (not characters) of the buffer and SQLITE_OK is returned. Otherwise, |
| @@ -8419,18 +8430,32 @@ | ||
| 8419 | 8430 | ** xInstCount: |
| 8420 | 8431 | ** Set *pnInst to the total number of occurrences of all phrases within |
| 8421 | 8432 | ** the query within the current row. Return SQLITE_OK if successful, or |
| 8422 | 8433 | ** an error code (i.e. SQLITE_NOMEM) if an error occurs. |
| 8423 | 8434 | ** |
| 8435 | +** This API can be quite slow if used with an FTS5 table created with the | |
| 8436 | +** "detail=none" or "detail=column" option. If the FTS5 table is created | |
| 8437 | +** with either "detail=none" or "detail=column" and "content=" option | |
| 8438 | +** (i.e. if it is a contentless table), then this API always returns 0. | |
| 8439 | +** | |
| 8424 | 8440 | ** xInst: |
| 8425 | 8441 | ** Query for the details of phrase match iIdx within the current row. |
| 8426 | 8442 | ** Phrase matches are numbered starting from zero, so the iIdx argument |
| 8427 | 8443 | ** should be greater than or equal to zero and smaller than the value |
| 8428 | 8444 | ** output by xInstCount(). |
| 8445 | +** | |
| 8446 | +** Usually, output parameter *piPhrase is set to the phrase number, *piCol | |
| 8447 | +** to the column in which it occurs and *piOff the token offset of the | |
| 8448 | +** first token of the phrase. The exception is if the table was created | |
| 8449 | +** with the offsets=0 option specified. In this case *piOff is always | |
| 8450 | +** set to -1. | |
| 8429 | 8451 | ** |
| 8430 | 8452 | ** Returns SQLITE_OK if successful, or an error code (i.e. SQLITE_NOMEM) |
| 8431 | 8453 | ** if an error occurs. |
| 8454 | +** | |
| 8455 | +** This API can be quite slow if used with an FTS5 table created with the | |
| 8456 | +** "detail=none" or "detail=column" option. | |
| 8432 | 8457 | ** |
| 8433 | 8458 | ** xRowid: |
| 8434 | 8459 | ** Returns the rowid of the current row. |
| 8435 | 8460 | ** |
| 8436 | 8461 | ** xTokenize: |
| @@ -8511,25 +8536,63 @@ | ||
| 8511 | 8536 | ** through instances of phrase iPhrase, use the following code: |
| 8512 | 8537 | ** |
| 8513 | 8538 | ** Fts5PhraseIter iter; |
| 8514 | 8539 | ** int iCol, iOff; |
| 8515 | 8540 | ** for(pApi->xPhraseFirst(pFts, iPhrase, &iter, &iCol, &iOff); |
| 8516 | -** iOff>=0; | |
| 8541 | +** iCol>=0; | |
| 8517 | 8542 | ** pApi->xPhraseNext(pFts, &iter, &iCol, &iOff) |
| 8518 | 8543 | ** ){ |
| 8519 | 8544 | ** // An instance of phrase iPhrase at offset iOff of column iCol |
| 8520 | 8545 | ** } |
| 8521 | 8546 | ** |
| 8522 | 8547 | ** The Fts5PhraseIter structure is defined above. Applications should not |
| 8523 | 8548 | ** modify this structure directly - it should only be used as shown above |
| 8524 | -** with the xPhraseFirst() and xPhraseNext() API methods. | |
| 8549 | +** with the xPhraseFirst() and xPhraseNext() API methods (and by | |
| 8550 | +** xPhraseFirstColumn() and xPhraseNextColumn() as illustrated below). | |
| 8551 | +** | |
| 8552 | +** This API can be quite slow if used with an FTS5 table created with the | |
| 8553 | +** "detail=none" or "detail=column" option. If the FTS5 table is created | |
| 8554 | +** with either "detail=none" or "detail=column" and "content=" option | |
| 8555 | +** (i.e. if it is a contentless table), then this API always iterates | |
| 8556 | +** through an empty set (all calls to xPhraseFirst() set iCol to -1). | |
| 8525 | 8557 | ** |
| 8526 | 8558 | ** xPhraseNext() |
| 8527 | 8559 | ** See xPhraseFirst above. |
| 8560 | +** | |
| 8561 | +** xPhraseFirstColumn() | |
| 8562 | +** This function and xPhraseNextColumn() are similar to the xPhraseFirst() | |
| 8563 | +** and xPhraseNext() APIs described above. The difference is that instead | |
| 8564 | +** of iterating through all instances of a phrase in the current row, these | |
| 8565 | +** APIs are used to iterate through the set of columns in the current row | |
| 8566 | +** that contain one or more instances of a specified phrase. For example: | |
| 8567 | +** | |
| 8568 | +** Fts5PhraseIter iter; | |
| 8569 | +** int iCol; | |
| 8570 | +** for(pApi->xPhraseFirstColumn(pFts, iPhrase, &iter, &iCol); | |
| 8571 | +** iCol>=0; | |
| 8572 | +** pApi->xPhraseNextColumn(pFts, &iter, &iCol) | |
| 8573 | +** ){ | |
| 8574 | +** // Column iCol contains at least one instance of phrase iPhrase | |
| 8575 | +** } | |
| 8576 | +** | |
| 8577 | +** This API can be quite slow if used with an FTS5 table created with the | |
| 8578 | +** "detail=none" option. If the FTS5 table is created with either | |
| 8579 | +** "detail=none" "content=" option (i.e. if it is a contentless table), | |
| 8580 | +** then this API always iterates through an empty set (all calls to | |
| 8581 | +** xPhraseFirstColumn() set iCol to -1). | |
| 8582 | +** | |
| 8583 | +** The information accessed using this API and its companion | |
| 8584 | +** xPhraseFirstColumn() may also be obtained using xPhraseFirst/xPhraseNext | |
| 8585 | +** (or xInst/xInstCount). The chief advantage of this API is that it is | |
| 8586 | +** significantly more efficient than those alternatives when used with | |
| 8587 | +** "detail=column" tables. | |
| 8588 | +** | |
| 8589 | +** xPhraseNextColumn() | |
| 8590 | +** See xPhraseFirstColumn above. | |
| 8528 | 8591 | */ |
| 8529 | 8592 | struct Fts5ExtensionApi { |
| 8530 | - int iVersion; /* Currently always set to 1 */ | |
| 8593 | + int iVersion; /* Currently always set to 3 */ | |
| 8531 | 8594 | |
| 8532 | 8595 | void *(*xUserData)(Fts5Context*); |
| 8533 | 8596 | |
| 8534 | 8597 | int (*xColumnCount)(Fts5Context*); |
| 8535 | 8598 | int (*xRowCount)(Fts5Context*, sqlite3_int64 *pnRow); |
| @@ -8555,12 +8618,15 @@ | ||
| 8555 | 8618 | int(*)(const Fts5ExtensionApi*,Fts5Context*,void*) |
| 8556 | 8619 | ); |
| 8557 | 8620 | int (*xSetAuxdata)(Fts5Context*, void *pAux, void(*xDelete)(void*)); |
| 8558 | 8621 | void *(*xGetAuxdata)(Fts5Context*, int bClear); |
| 8559 | 8622 | |
| 8560 | - void (*xPhraseFirst)(Fts5Context*, int iPhrase, Fts5PhraseIter*, int*, int*); | |
| 8623 | + int (*xPhraseFirst)(Fts5Context*, int iPhrase, Fts5PhraseIter*, int*, int*); | |
| 8561 | 8624 | void (*xPhraseNext)(Fts5Context*, Fts5PhraseIter*, int *piCol, int *piOff); |
| 8625 | + | |
| 8626 | + int (*xPhraseFirstColumn)(Fts5Context*, int iPhrase, Fts5PhraseIter*, int*); | |
| 8627 | + void (*xPhraseNextColumn)(Fts5Context*, Fts5PhraseIter*, int *piCol); | |
| 8562 | 8628 | }; |
| 8563 | 8629 | |
| 8564 | 8630 | /* |
| 8565 | 8631 | ** CUSTOM AUXILIARY FUNCTIONS |
| 8566 | 8632 | *************************************************************************/ |
| @@ -9976,14 +10042,10 @@ | ||
| 9976 | 10042 | /* |
| 9977 | 10043 | ** Default maximum size of memory used by memory-mapped I/O in the VFS |
| 9978 | 10044 | */ |
| 9979 | 10045 | #ifdef __APPLE__ |
| 9980 | 10046 | # include <TargetConditionals.h> |
| 9981 | -# if TARGET_OS_IPHONE | |
| 9982 | -# undef SQLITE_MAX_MMAP_SIZE | |
| 9983 | -# define SQLITE_MAX_MMAP_SIZE 0 | |
| 9984 | -# endif | |
| 9985 | 10047 | #endif |
| 9986 | 10048 | #ifndef SQLITE_MAX_MMAP_SIZE |
| 9987 | 10049 | # if defined(__linux__) \ |
| 9988 | 10050 | || defined(_WIN32) \ |
| 9989 | 10051 | || (defined(__APPLE__) && defined(__MACH__)) \ |
| @@ -10479,19 +10541,21 @@ | ||
| 10479 | 10541 | ** Enter and Leave procedures no-ops. |
| 10480 | 10542 | */ |
| 10481 | 10543 | #ifndef SQLITE_OMIT_SHARED_CACHE |
| 10482 | 10544 | SQLITE_PRIVATE void sqlite3BtreeEnter(Btree*); |
| 10483 | 10545 | SQLITE_PRIVATE void sqlite3BtreeEnterAll(sqlite3*); |
| 10546 | +SQLITE_PRIVATE int sqlite3BtreeSharable(Btree*); | |
| 10547 | +SQLITE_PRIVATE void sqlite3BtreeEnterCursor(BtCursor*); | |
| 10484 | 10548 | #else |
| 10485 | 10549 | # define sqlite3BtreeEnter(X) |
| 10486 | 10550 | # define sqlite3BtreeEnterAll(X) |
| 10551 | +# define sqlite3BtreeSharable(X) 0 | |
| 10552 | +# define sqlite3BtreeEnterCursor(X) | |
| 10487 | 10553 | #endif |
| 10488 | 10554 | |
| 10489 | 10555 | #if !defined(SQLITE_OMIT_SHARED_CACHE) && SQLITE_THREADSAFE |
| 10490 | -SQLITE_PRIVATE int sqlite3BtreeSharable(Btree*); | |
| 10491 | 10556 | SQLITE_PRIVATE void sqlite3BtreeLeave(Btree*); |
| 10492 | -SQLITE_PRIVATE void sqlite3BtreeEnterCursor(BtCursor*); | |
| 10493 | 10557 | SQLITE_PRIVATE void sqlite3BtreeLeaveCursor(BtCursor*); |
| 10494 | 10558 | SQLITE_PRIVATE void sqlite3BtreeLeaveAll(sqlite3*); |
| 10495 | 10559 | #ifndef NDEBUG |
| 10496 | 10560 | /* These routines are used inside assert() statements only. */ |
| 10497 | 10561 | SQLITE_PRIVATE int sqlite3BtreeHoldsMutex(Btree*); |
| @@ -10498,13 +10562,11 @@ | ||
| 10498 | 10562 | SQLITE_PRIVATE int sqlite3BtreeHoldsAllMutexes(sqlite3*); |
| 10499 | 10563 | SQLITE_PRIVATE int sqlite3SchemaMutexHeld(sqlite3*,int,Schema*); |
| 10500 | 10564 | #endif |
| 10501 | 10565 | #else |
| 10502 | 10566 | |
| 10503 | -# define sqlite3BtreeSharable(X) 0 | |
| 10504 | 10567 | # define sqlite3BtreeLeave(X) |
| 10505 | -# define sqlite3BtreeEnterCursor(X) | |
| 10506 | 10568 | # define sqlite3BtreeLeaveCursor(X) |
| 10507 | 10569 | # define sqlite3BtreeLeaveAll(X) |
| 10508 | 10570 | |
| 10509 | 10571 | # define sqlite3BtreeHoldsMutex(X) 1 |
| 10510 | 10572 | # define sqlite3BtreeHoldsAllMutexes(X) 1 |
| @@ -11215,10 +11277,11 @@ | ||
| 11215 | 11277 | #endif |
| 11216 | 11278 | SQLITE_PRIVATE int sqlite3PagerMemUsed(Pager*); |
| 11217 | 11279 | SQLITE_PRIVATE const char *sqlite3PagerFilename(Pager*, int); |
| 11218 | 11280 | SQLITE_PRIVATE sqlite3_vfs *sqlite3PagerVfs(Pager*); |
| 11219 | 11281 | SQLITE_PRIVATE sqlite3_file *sqlite3PagerFile(Pager*); |
| 11282 | +SQLITE_PRIVATE sqlite3_file *sqlite3PagerJrnlFile(Pager*); | |
| 11220 | 11283 | SQLITE_PRIVATE const char *sqlite3PagerJournalname(Pager*); |
| 11221 | 11284 | SQLITE_PRIVATE int sqlite3PagerNosync(Pager*); |
| 11222 | 11285 | SQLITE_PRIVATE void *sqlite3PagerTempSpace(Pager*); |
| 11223 | 11286 | SQLITE_PRIVATE int sqlite3PagerIsMemdb(Pager*); |
| 11224 | 11287 | SQLITE_PRIVATE void sqlite3PagerCacheStat(Pager *, int, int, int *); |
| @@ -11309,10 +11372,12 @@ | ||
| 11309 | 11372 | #define PGHDR_NEED_SYNC 0x008 /* Fsync the rollback journal before |
| 11310 | 11373 | ** writing this page to the database */ |
| 11311 | 11374 | #define PGHDR_NEED_READ 0x010 /* Content is unread */ |
| 11312 | 11375 | #define PGHDR_DONT_WRITE 0x020 /* Do not write content to disk */ |
| 11313 | 11376 | #define PGHDR_MMAP 0x040 /* This is an mmap page object */ |
| 11377 | + | |
| 11378 | +#define PGHDR_WAL_APPEND 0x080 /* Appended to wal file */ | |
| 11314 | 11379 | |
| 11315 | 11380 | /* Initialize and shutdown the page cache subsystem */ |
| 11316 | 11381 | SQLITE_PRIVATE int sqlite3PcacheInitialize(void); |
| 11317 | 11382 | SQLITE_PRIVATE void sqlite3PcacheShutdown(void); |
| 11318 | 11383 | |
| @@ -14135,11 +14200,10 @@ | ||
| 14135 | 14200 | SQLITE_PRIVATE int sqlite3InitCallback(void*, int, char**, char**); |
| 14136 | 14201 | SQLITE_PRIVATE void sqlite3Pragma(Parse*,Token*,Token*,Token*,int); |
| 14137 | 14202 | SQLITE_PRIVATE void sqlite3ResetAllSchemasOfConnection(sqlite3*); |
| 14138 | 14203 | SQLITE_PRIVATE void sqlite3ResetOneSchema(sqlite3*,int); |
| 14139 | 14204 | SQLITE_PRIVATE void sqlite3CollapseDatabaseArray(sqlite3*); |
| 14140 | -SQLITE_PRIVATE void sqlite3BeginParse(Parse*,int); | |
| 14141 | 14205 | SQLITE_PRIVATE void sqlite3CommitInternalChanges(sqlite3*); |
| 14142 | 14206 | SQLITE_PRIVATE void sqlite3DeleteColumnNames(sqlite3*,Table*); |
| 14143 | 14207 | SQLITE_PRIVATE int sqlite3ColumnsFromExprList(Parse*,ExprList*,i16*,Column**); |
| 14144 | 14208 | SQLITE_PRIVATE Table *sqlite3ResultSetOfSelect(Parse*,Select*); |
| 14145 | 14209 | SQLITE_PRIVATE void sqlite3OpenMasterTable(Parse *, int); |
| @@ -16066,15 +16130,19 @@ | ||
| 16066 | 16130 | SQLITE_PRIVATE int sqlite3VdbeSorterNext(sqlite3 *, const VdbeCursor *, int *); |
| 16067 | 16131 | SQLITE_PRIVATE int sqlite3VdbeSorterRewind(const VdbeCursor *, int *); |
| 16068 | 16132 | SQLITE_PRIVATE int sqlite3VdbeSorterWrite(const VdbeCursor *, Mem *); |
| 16069 | 16133 | SQLITE_PRIVATE int sqlite3VdbeSorterCompare(const VdbeCursor *, Mem *, int, int *); |
| 16070 | 16134 | |
| 16071 | -#if !defined(SQLITE_OMIT_SHARED_CACHE) && SQLITE_THREADSAFE>0 | |
| 16135 | +#if !defined(SQLITE_OMIT_SHARED_CACHE) | |
| 16072 | 16136 | SQLITE_PRIVATE void sqlite3VdbeEnter(Vdbe*); |
| 16073 | -SQLITE_PRIVATE void sqlite3VdbeLeave(Vdbe*); | |
| 16074 | 16137 | #else |
| 16075 | 16138 | # define sqlite3VdbeEnter(X) |
| 16139 | +#endif | |
| 16140 | + | |
| 16141 | +#if !defined(SQLITE_OMIT_SHARED_CACHE) && SQLITE_THREADSAFE>0 | |
| 16142 | +SQLITE_PRIVATE void sqlite3VdbeLeave(Vdbe*); | |
| 16143 | +#else | |
| 16076 | 16144 | # define sqlite3VdbeLeave(X) |
| 16077 | 16145 | #endif |
| 16078 | 16146 | |
| 16079 | 16147 | #ifdef SQLITE_DEBUG |
| 16080 | 16148 | SQLITE_PRIVATE void sqlite3VdbeMemAboutToChange(Vdbe*,Mem*); |
| @@ -19758,10 +19826,11 @@ | ||
| 19758 | 19826 | /* |
| 19759 | 19827 | ** Mutex to control access to the memory allocation subsystem. |
| 19760 | 19828 | */ |
| 19761 | 19829 | sqlite3_mutex *mutex; |
| 19762 | 19830 | |
| 19831 | +#if defined(SQLITE_DEBUG) || defined(SQLITE_TEST) | |
| 19763 | 19832 | /* |
| 19764 | 19833 | ** Performance statistics |
| 19765 | 19834 | */ |
| 19766 | 19835 | u64 nAlloc; /* Total number of calls to malloc */ |
| 19767 | 19836 | u64 totalAlloc; /* Total of all malloc calls - includes internal frag */ |
| @@ -19769,10 +19838,11 @@ | ||
| 19769 | 19838 | u32 currentOut; /* Current checkout, including internal fragmentation */ |
| 19770 | 19839 | u32 currentCount; /* Current number of distinct checkouts */ |
| 19771 | 19840 | u32 maxOut; /* Maximum instantaneous currentOut */ |
| 19772 | 19841 | u32 maxCount; /* Maximum instantaneous currentCount */ |
| 19773 | 19842 | u32 maxRequest; /* Largest allocation (exclusive of internal frag) */ |
| 19843 | +#endif | |
| 19774 | 19844 | |
| 19775 | 19845 | /* |
| 19776 | 19846 | ** Lists of free blocks. aiFreelist[0] is a list of free blocks of |
| 19777 | 19847 | ** size mem5.szAtom. aiFreelist[1] holds blocks of size szAtom*2. |
| 19778 | 19848 | ** aiFreelist[2] holds free blocks of size szAtom*4. And so forth. |
| @@ -19880,18 +19950,21 @@ | ||
| 19880 | 19950 | int iLogsize; /* Log2 of iFullSz/POW2_MIN */ |
| 19881 | 19951 | |
| 19882 | 19952 | /* nByte must be a positive */ |
| 19883 | 19953 | assert( nByte>0 ); |
| 19884 | 19954 | |
| 19955 | + /* No more than 1GiB per allocation */ | |
| 19956 | + if( nByte > 0x40000000 ) return 0; | |
| 19957 | + | |
| 19958 | +#if defined(SQLITE_DEBUG) || defined(SQLITE_TEST) | |
| 19885 | 19959 | /* Keep track of the maximum allocation request. Even unfulfilled |
| 19886 | 19960 | ** requests are counted */ |
| 19887 | 19961 | if( (u32)nByte>mem5.maxRequest ){ |
| 19888 | - /* Abort if the requested allocation size is larger than the largest | |
| 19889 | - ** power of two that we can represent using 32-bit signed integers. */ | |
| 19890 | - if( nByte > 0x40000000 ) return 0; | |
| 19891 | 19962 | mem5.maxRequest = nByte; |
| 19892 | 19963 | } |
| 19964 | +#endif | |
| 19965 | + | |
| 19893 | 19966 | |
| 19894 | 19967 | /* Round nByte up to the next valid power of two */ |
| 19895 | 19968 | for(iFullSz=mem5.szAtom,iLogsize=0; iFullSz<nByte; iFullSz*=2,iLogsize++){} |
| 19896 | 19969 | |
| 19897 | 19970 | /* Make sure mem5.aiFreelist[iLogsize] contains at least one free |
| @@ -19914,18 +19987,20 @@ | ||
| 19914 | 19987 | mem5.aCtrl[i+newSize] = CTRL_FREE | iBin; |
| 19915 | 19988 | memsys5Link(i+newSize, iBin); |
| 19916 | 19989 | } |
| 19917 | 19990 | mem5.aCtrl[i] = iLogsize; |
| 19918 | 19991 | |
| 19992 | +#if defined(SQLITE_DEBUG) || defined(SQLITE_TEST) | |
| 19919 | 19993 | /* Update allocator performance statistics. */ |
| 19920 | 19994 | mem5.nAlloc++; |
| 19921 | 19995 | mem5.totalAlloc += iFullSz; |
| 19922 | 19996 | mem5.totalExcess += iFullSz - nByte; |
| 19923 | 19997 | mem5.currentCount++; |
| 19924 | 19998 | mem5.currentOut += iFullSz; |
| 19925 | 19999 | if( mem5.maxCount<mem5.currentCount ) mem5.maxCount = mem5.currentCount; |
| 19926 | 20000 | if( mem5.maxOut<mem5.currentOut ) mem5.maxOut = mem5.currentOut; |
| 20001 | +#endif | |
| 19927 | 20002 | |
| 19928 | 20003 | #ifdef SQLITE_DEBUG |
| 19929 | 20004 | /* Make sure the allocated memory does not assume that it is set to zero |
| 19930 | 20005 | ** or retains a value from a previous allocation */ |
| 19931 | 20006 | memset(&mem5.zPool[i*mem5.szAtom], 0xAA, iFullSz); |
| @@ -19956,16 +20031,19 @@ | ||
| 19956 | 20031 | size = 1<<iLogsize; |
| 19957 | 20032 | assert( iBlock+size-1<(u32)mem5.nBlock ); |
| 19958 | 20033 | |
| 19959 | 20034 | mem5.aCtrl[iBlock] |= CTRL_FREE; |
| 19960 | 20035 | mem5.aCtrl[iBlock+size-1] |= CTRL_FREE; |
| 20036 | + | |
| 20037 | +#if defined(SQLITE_DEBUG) || defined(SQLITE_TEST) | |
| 19961 | 20038 | assert( mem5.currentCount>0 ); |
| 19962 | 20039 | assert( mem5.currentOut>=(size*mem5.szAtom) ); |
| 19963 | 20040 | mem5.currentCount--; |
| 19964 | 20041 | mem5.currentOut -= size*mem5.szAtom; |
| 19965 | 20042 | assert( mem5.currentOut>0 || mem5.currentCount==0 ); |
| 19966 | 20043 | assert( mem5.currentCount>0 || mem5.currentOut==0 ); |
| 20044 | +#endif | |
| 19967 | 20045 | |
| 19968 | 20046 | mem5.aCtrl[iBlock] = CTRL_FREE | iLogsize; |
| 19969 | 20047 | while( ALWAYS(iLogsize<LOGMAX) ){ |
| 19970 | 20048 | int iBuddy; |
| 19971 | 20049 | if( (iBlock>>iLogsize) & 1 ){ |
| @@ -27479,37 +27557,55 @@ | ||
| 27479 | 27557 | #define osMkdir ((int(*)(const char*,mode_t))aSyscall[18].pCurrent) |
| 27480 | 27558 | |
| 27481 | 27559 | { "rmdir", (sqlite3_syscall_ptr)rmdir, 0 }, |
| 27482 | 27560 | #define osRmdir ((int(*)(const char*))aSyscall[19].pCurrent) |
| 27483 | 27561 | |
| 27562 | +#if defined(HAVE_FCHOWN) | |
| 27484 | 27563 | { "fchown", (sqlite3_syscall_ptr)fchown, 0 }, |
| 27564 | +#else | |
| 27565 | + { "fchown", (sqlite3_syscall_ptr)0, 0 }, | |
| 27566 | +#endif | |
| 27485 | 27567 | #define osFchown ((int(*)(int,uid_t,gid_t))aSyscall[20].pCurrent) |
| 27486 | 27568 | |
| 27487 | 27569 | { "geteuid", (sqlite3_syscall_ptr)geteuid, 0 }, |
| 27488 | 27570 | #define osGeteuid ((uid_t(*)(void))aSyscall[21].pCurrent) |
| 27489 | 27571 | |
| 27490 | 27572 | #if !defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0 |
| 27491 | - { "mmap", (sqlite3_syscall_ptr)mmap, 0 }, | |
| 27573 | + { "mmap", (sqlite3_syscall_ptr)mmap, 0 }, | |
| 27574 | +#else | |
| 27575 | + { "mmap", (sqlite3_syscall_ptr)0, 0 }, | |
| 27576 | +#endif | |
| 27492 | 27577 | #define osMmap ((void*(*)(void*,size_t,int,int,int,off_t))aSyscall[22].pCurrent) |
| 27493 | 27578 | |
| 27579 | +#if !defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0 | |
| 27494 | 27580 | { "munmap", (sqlite3_syscall_ptr)munmap, 0 }, |
| 27581 | +#else | |
| 27582 | + { "munmap", (sqlite3_syscall_ptr)0, 0 }, | |
| 27583 | +#endif | |
| 27495 | 27584 | #define osMunmap ((void*(*)(void*,size_t))aSyscall[23].pCurrent) |
| 27496 | 27585 | |
| 27497 | -#if HAVE_MREMAP | |
| 27586 | +#if HAVE_MREMAP && (!defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0) | |
| 27498 | 27587 | { "mremap", (sqlite3_syscall_ptr)mremap, 0 }, |
| 27499 | 27588 | #else |
| 27500 | 27589 | { "mremap", (sqlite3_syscall_ptr)0, 0 }, |
| 27501 | 27590 | #endif |
| 27502 | 27591 | #define osMremap ((void*(*)(void*,size_t,size_t,int,...))aSyscall[24].pCurrent) |
| 27503 | 27592 | |
| 27593 | +#if !defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0 | |
| 27504 | 27594 | { "getpagesize", (sqlite3_syscall_ptr)unixGetpagesize, 0 }, |
| 27595 | +#else | |
| 27596 | + { "getpagesize", (sqlite3_syscall_ptr)0, 0 }, | |
| 27597 | +#endif | |
| 27505 | 27598 | #define osGetpagesize ((int(*)(void))aSyscall[25].pCurrent) |
| 27506 | 27599 | |
| 27600 | +#if defined(HAVE_READLINK) | |
| 27507 | 27601 | { "readlink", (sqlite3_syscall_ptr)readlink, 0 }, |
| 27602 | +#else | |
| 27603 | + { "readlink", (sqlite3_syscall_ptr)0, 0 }, | |
| 27604 | +#endif | |
| 27508 | 27605 | #define osReadlink ((ssize_t(*)(const char*,char*,size_t))aSyscall[26].pCurrent) |
| 27509 | 27606 | |
| 27510 | -#endif | |
| 27511 | 27607 | |
| 27512 | 27608 | }; /* End of the overrideable system calls */ |
| 27513 | 27609 | |
| 27514 | 27610 | |
| 27515 | 27611 | /* |
| @@ -27516,14 +27612,14 @@ | ||
| 27516 | 27612 | ** On some systems, calls to fchown() will trigger a message in a security |
| 27517 | 27613 | ** log if they come from non-root processes. So avoid calling fchown() if |
| 27518 | 27614 | ** we are not running as root. |
| 27519 | 27615 | */ |
| 27520 | 27616 | static int robustFchown(int fd, uid_t uid, gid_t gid){ |
| 27521 | -#if OS_VXWORKS | |
| 27522 | - return 0; | |
| 27523 | -#else | |
| 27617 | +#if defined(HAVE_FCHOWN) | |
| 27524 | 27618 | return osGeteuid() ? 0 : osFchown(fd,uid,gid); |
| 27619 | +#else | |
| 27620 | + return 0; | |
| 27525 | 27621 | #endif |
| 27526 | 27622 | } |
| 27527 | 27623 | |
| 27528 | 27624 | /* |
| 27529 | 27625 | ** This is the xSetSystemCall() method of sqlite3_vfs for all of the |
| @@ -32986,10 +33082,11 @@ | ||
| 32986 | 33082 | SimulateIOError( return SQLITE_ERROR ); |
| 32987 | 33083 | |
| 32988 | 33084 | assert( pVfs->mxPathname==MAX_PATHNAME ); |
| 32989 | 33085 | UNUSED_PARAMETER(pVfs); |
| 32990 | 33086 | |
| 33087 | +#if defined(HAVE_READLINK) | |
| 32991 | 33088 | /* Attempt to resolve the path as if it were a symbolic link. If it is |
| 32992 | 33089 | ** a symbolic link, the resolved path is stored in buffer zOut[]. Or, if |
| 32993 | 33090 | ** the identified file is not a symbolic link or does not exist, then |
| 32994 | 33091 | ** zPath is copied directly into zOut. Either way, nByte is left set to |
| 32995 | 33092 | ** the size of the string copied into zOut[] in bytes. */ |
| @@ -33001,10 +33098,11 @@ | ||
| 33001 | 33098 | sqlite3_snprintf(nOut, zOut, "%s", zPath); |
| 33002 | 33099 | nByte = sqlite3Strlen30(zOut); |
| 33003 | 33100 | }else{ |
| 33004 | 33101 | zOut[nByte] = '\0'; |
| 33005 | 33102 | } |
| 33103 | +#endif | |
| 33006 | 33104 | |
| 33007 | 33105 | /* If buffer zOut[] now contains an absolute path there is nothing more |
| 33008 | 33106 | ** to do. If it contains a relative path, do the following: |
| 33009 | 33107 | ** |
| 33010 | 33108 | ** * move the relative path string so that it is at the end of th |
| @@ -43327,10 +43425,11 @@ | ||
| 43327 | 43425 | # define sqlite3WalCallback(z) 0 |
| 43328 | 43426 | # define sqlite3WalExclusiveMode(y,z) 0 |
| 43329 | 43427 | # define sqlite3WalHeapMemory(z) 0 |
| 43330 | 43428 | # define sqlite3WalFramesize(z) 0 |
| 43331 | 43429 | # define sqlite3WalFindFrame(x,y,z) 0 |
| 43430 | +# define sqlite3WalFile(x) 0 | |
| 43332 | 43431 | #else |
| 43333 | 43432 | |
| 43334 | 43433 | #define WAL_SAVEPOINT_NDATA 4 |
| 43335 | 43434 | |
| 43336 | 43435 | /* Connection to a write-ahead log (WAL) file. |
| @@ -43421,10 +43520,13 @@ | ||
| 43421 | 43520 | ** stored in each frame (i.e. the db page-size when the WAL was created). |
| 43422 | 43521 | */ |
| 43423 | 43522 | SQLITE_PRIVATE int sqlite3WalFramesize(Wal *pWal); |
| 43424 | 43523 | #endif |
| 43425 | 43524 | |
| 43525 | +/* Return the sqlite3_file object for the WAL file */ | |
| 43526 | +SQLITE_PRIVATE sqlite3_file *sqlite3WalFile(Wal *pWal); | |
| 43527 | + | |
| 43426 | 43528 | #endif /* ifndef SQLITE_OMIT_WAL */ |
| 43427 | 43529 | #endif /* _WAL_H_ */ |
| 43428 | 43530 | |
| 43429 | 43531 | /************** End of wal.h *************************************************/ |
| 43430 | 43532 | /************** Continuing where we left off in pager.c **********************/ |
| @@ -49032,11 +49134,11 @@ | ||
| 49032 | 49134 | if( pPager->exclusiveMode && sqlite3WalExclusiveMode(pPager->pWal, -1) ){ |
| 49033 | 49135 | rc = pagerLockDb(pPager, EXCLUSIVE_LOCK); |
| 49034 | 49136 | if( rc!=SQLITE_OK ){ |
| 49035 | 49137 | return rc; |
| 49036 | 49138 | } |
| 49037 | - sqlite3WalExclusiveMode(pPager->pWal, 1); | |
| 49139 | + (void)sqlite3WalExclusiveMode(pPager->pWal, 1); | |
| 49038 | 49140 | } |
| 49039 | 49141 | |
| 49040 | 49142 | /* Grab the write lock on the log file. If successful, upgrade to |
| 49041 | 49143 | ** PAGER_RESERVED state. Otherwise, return an error code to the caller. |
| 49042 | 49144 | ** The busy-handler is not invoked if another connection already |
| @@ -50096,10 +50198,22 @@ | ||
| 50096 | 50198 | ** not yet been opened. |
| 50097 | 50199 | */ |
| 50098 | 50200 | SQLITE_PRIVATE sqlite3_file *sqlite3PagerFile(Pager *pPager){ |
| 50099 | 50201 | return pPager->fd; |
| 50100 | 50202 | } |
| 50203 | + | |
| 50204 | +/* | |
| 50205 | +** Return the file handle for the journal file (if it exists). | |
| 50206 | +** This will be either the rollback journal or the WAL file. | |
| 50207 | +*/ | |
| 50208 | +SQLITE_PRIVATE sqlite3_file *sqlite3PagerJrnlFile(Pager *pPager){ | |
| 50209 | +#if SQLITE_OMIT_WAL | |
| 50210 | + return pPager->jfd; | |
| 50211 | +#else | |
| 50212 | + return pPager->pWal ? sqlite3WalFile(pPager->pWal) : pPager->jfd; | |
| 50213 | +#endif | |
| 50214 | +} | |
| 50101 | 50215 | |
| 50102 | 50216 | /* |
| 50103 | 50217 | ** Return the full pathname of the journal file. |
| 50104 | 50218 | */ |
| 50105 | 50219 | SQLITE_PRIVATE const char *sqlite3PagerJournalname(Pager *pPager){ |
| @@ -51202,10 +51316,11 @@ | ||
| 51202 | 51316 | u8 truncateOnCommit; /* True to truncate WAL file on commit */ |
| 51203 | 51317 | u8 syncHeader; /* Fsync the WAL header if true */ |
| 51204 | 51318 | u8 padToSectorBoundary; /* Pad transactions out to the next sector */ |
| 51205 | 51319 | WalIndexHdr hdr; /* Wal-index header for current transaction */ |
| 51206 | 51320 | u32 minFrame; /* Ignore wal frames before this one */ |
| 51321 | + u32 iReCksum; /* On commit, recalculate checksums from here */ | |
| 51207 | 51322 | const char *zWalName; /* Name of WAL file */ |
| 51208 | 51323 | u32 nCkpt; /* Checkpoint sequence counter in the wal-header */ |
| 51209 | 51324 | #ifdef SQLITE_DEBUG |
| 51210 | 51325 | u8 lockError; /* True if a locking error has occurred */ |
| 51211 | 51326 | #endif |
| @@ -51455,18 +51570,22 @@ | ||
| 51455 | 51570 | int nativeCksum; /* True for native byte-order checksums */ |
| 51456 | 51571 | u32 *aCksum = pWal->hdr.aFrameCksum; |
| 51457 | 51572 | assert( WAL_FRAME_HDRSIZE==24 ); |
| 51458 | 51573 | sqlite3Put4byte(&aFrame[0], iPage); |
| 51459 | 51574 | sqlite3Put4byte(&aFrame[4], nTruncate); |
| 51460 | - memcpy(&aFrame[8], pWal->hdr.aSalt, 8); | |
| 51461 | - | |
| 51462 | - nativeCksum = (pWal->hdr.bigEndCksum==SQLITE_BIGENDIAN); | |
| 51463 | - walChecksumBytes(nativeCksum, aFrame, 8, aCksum, aCksum); | |
| 51464 | - walChecksumBytes(nativeCksum, aData, pWal->szPage, aCksum, aCksum); | |
| 51465 | - | |
| 51466 | - sqlite3Put4byte(&aFrame[16], aCksum[0]); | |
| 51467 | - sqlite3Put4byte(&aFrame[20], aCksum[1]); | |
| 51575 | + if( pWal->iReCksum==0 ){ | |
| 51576 | + memcpy(&aFrame[8], pWal->hdr.aSalt, 8); | |
| 51577 | + | |
| 51578 | + nativeCksum = (pWal->hdr.bigEndCksum==SQLITE_BIGENDIAN); | |
| 51579 | + walChecksumBytes(nativeCksum, aFrame, 8, aCksum, aCksum); | |
| 51580 | + walChecksumBytes(nativeCksum, aData, pWal->szPage, aCksum, aCksum); | |
| 51581 | + | |
| 51582 | + sqlite3Put4byte(&aFrame[16], aCksum[0]); | |
| 51583 | + sqlite3Put4byte(&aFrame[20], aCksum[1]); | |
| 51584 | + }else{ | |
| 51585 | + memset(&aFrame[8], 0, 16); | |
| 51586 | + } | |
| 51468 | 51587 | } |
| 51469 | 51588 | |
| 51470 | 51589 | /* |
| 51471 | 51590 | ** Check to see if the frame with header in aFrame[] and content |
| 51472 | 51591 | ** in aData[] is valid. If it is a valid frame, fill *piPage and |
| @@ -53389,10 +53508,11 @@ | ||
| 53389 | 53508 | int rc; |
| 53390 | 53509 | |
| 53391 | 53510 | /* Cannot start a write transaction without first holding a read |
| 53392 | 53511 | ** transaction. */ |
| 53393 | 53512 | assert( pWal->readLock>=0 ); |
| 53513 | + assert( pWal->writeLock==0 && pWal->iReCksum==0 ); | |
| 53394 | 53514 | |
| 53395 | 53515 | if( pWal->readOnly ){ |
| 53396 | 53516 | return SQLITE_READONLY; |
| 53397 | 53517 | } |
| 53398 | 53518 | |
| @@ -53424,10 +53544,11 @@ | ||
| 53424 | 53544 | */ |
| 53425 | 53545 | SQLITE_PRIVATE int sqlite3WalEndWriteTransaction(Wal *pWal){ |
| 53426 | 53546 | if( pWal->writeLock ){ |
| 53427 | 53547 | walUnlockExclusive(pWal, WAL_WRITE_LOCK, 1); |
| 53428 | 53548 | pWal->writeLock = 0; |
| 53549 | + pWal->iReCksum = 0; | |
| 53429 | 53550 | pWal->truncateOnCommit = 0; |
| 53430 | 53551 | } |
| 53431 | 53552 | return SQLITE_OK; |
| 53432 | 53553 | } |
| 53433 | 53554 | |
| @@ -53641,10 +53762,63 @@ | ||
| 53641 | 53762 | if( rc ) return rc; |
| 53642 | 53763 | /* Write the page data */ |
| 53643 | 53764 | rc = walWriteToLog(p, pData, p->szPage, iOffset+sizeof(aFrame)); |
| 53644 | 53765 | return rc; |
| 53645 | 53766 | } |
| 53767 | + | |
| 53768 | +/* | |
| 53769 | +** This function is called as part of committing a transaction within which | |
| 53770 | +** one or more frames have been overwritten. It updates the checksums for | |
| 53771 | +** all frames written to the wal file by the current transaction starting | |
| 53772 | +** with the earliest to have been overwritten. | |
| 53773 | +** | |
| 53774 | +** SQLITE_OK is returned if successful, or an SQLite error code otherwise. | |
| 53775 | +*/ | |
| 53776 | +static int walRewriteChecksums(Wal *pWal, u32 iLast){ | |
| 53777 | + const int szPage = pWal->szPage;/* Database page size */ | |
| 53778 | + int rc = SQLITE_OK; /* Return code */ | |
| 53779 | + u8 *aBuf; /* Buffer to load data from wal file into */ | |
| 53780 | + u8 aFrame[WAL_FRAME_HDRSIZE]; /* Buffer to assemble frame-headers in */ | |
| 53781 | + u32 iRead; /* Next frame to read from wal file */ | |
| 53782 | + i64 iCksumOff; | |
| 53783 | + | |
| 53784 | + aBuf = sqlite3_malloc(szPage + WAL_FRAME_HDRSIZE); | |
| 53785 | + if( aBuf==0 ) return SQLITE_NOMEM; | |
| 53786 | + | |
| 53787 | + /* Find the checksum values to use as input for the recalculating the | |
| 53788 | + ** first checksum. If the first frame is frame 1 (implying that the current | |
| 53789 | + ** transaction restarted the wal file), these values must be read from the | |
| 53790 | + ** wal-file header. Otherwise, read them from the frame header of the | |
| 53791 | + ** previous frame. */ | |
| 53792 | + assert( pWal->iReCksum>0 ); | |
| 53793 | + if( pWal->iReCksum==1 ){ | |
| 53794 | + iCksumOff = 24; | |
| 53795 | + }else{ | |
| 53796 | + iCksumOff = walFrameOffset(pWal->iReCksum-1, szPage) + 16; | |
| 53797 | + } | |
| 53798 | + rc = sqlite3OsRead(pWal->pWalFd, aBuf, sizeof(u32)*2, iCksumOff); | |
| 53799 | + pWal->hdr.aFrameCksum[0] = sqlite3Get4byte(aBuf); | |
| 53800 | + pWal->hdr.aFrameCksum[1] = sqlite3Get4byte(&aBuf[sizeof(u32)]); | |
| 53801 | + | |
| 53802 | + iRead = pWal->iReCksum; | |
| 53803 | + pWal->iReCksum = 0; | |
| 53804 | + for(; rc==SQLITE_OK && iRead<=iLast; iRead++){ | |
| 53805 | + i64 iOff = walFrameOffset(iRead, szPage); | |
| 53806 | + rc = sqlite3OsRead(pWal->pWalFd, aBuf, szPage+WAL_FRAME_HDRSIZE, iOff); | |
| 53807 | + if( rc==SQLITE_OK ){ | |
| 53808 | + u32 iPgno, nDbSize; | |
| 53809 | + iPgno = sqlite3Get4byte(aBuf); | |
| 53810 | + nDbSize = sqlite3Get4byte(&aBuf[4]); | |
| 53811 | + | |
| 53812 | + walEncodeFrame(pWal, iPgno, nDbSize, &aBuf[WAL_FRAME_HDRSIZE], aFrame); | |
| 53813 | + rc = sqlite3OsWrite(pWal->pWalFd, aFrame, sizeof(aFrame), iOff); | |
| 53814 | + } | |
| 53815 | + } | |
| 53816 | + | |
| 53817 | + sqlite3_free(aBuf); | |
| 53818 | + return rc; | |
| 53819 | +} | |
| 53646 | 53820 | |
| 53647 | 53821 | /* |
| 53648 | 53822 | ** Write a set of frames to the log. The caller must hold the write-lock |
| 53649 | 53823 | ** on the log file (obtained using sqlite3WalBeginWriteTransaction()). |
| 53650 | 53824 | */ |
| @@ -53662,10 +53836,12 @@ | ||
| 53662 | 53836 | PgHdr *pLast = 0; /* Last frame in list */ |
| 53663 | 53837 | int nExtra = 0; /* Number of extra copies of last page */ |
| 53664 | 53838 | int szFrame; /* The size of a single frame */ |
| 53665 | 53839 | i64 iOffset; /* Next byte to write in WAL file */ |
| 53666 | 53840 | WalWriter w; /* The writer */ |
| 53841 | + u32 iFirst = 0; /* First frame that may be overwritten */ | |
| 53842 | + WalIndexHdr *pLive; /* Pointer to shared header */ | |
| 53667 | 53843 | |
| 53668 | 53844 | assert( pList ); |
| 53669 | 53845 | assert( pWal->writeLock ); |
| 53670 | 53846 | |
| 53671 | 53847 | /* If this frame set completes a transaction, then nTruncate>0. If |
| @@ -53676,10 +53852,15 @@ | ||
| 53676 | 53852 | { int cnt; for(cnt=0, p=pList; p; p=p->pDirty, cnt++){} |
| 53677 | 53853 | WALTRACE(("WAL%p: frame write begin. %d frames. mxFrame=%d. %s\n", |
| 53678 | 53854 | pWal, cnt, pWal->hdr.mxFrame, isCommit ? "Commit" : "Spill")); |
| 53679 | 53855 | } |
| 53680 | 53856 | #endif |
| 53857 | + | |
| 53858 | + pLive = (WalIndexHdr*)walIndexHdr(pWal); | |
| 53859 | + if( memcmp(&pWal->hdr, (void *)pLive, sizeof(WalIndexHdr))!=0 ){ | |
| 53860 | + iFirst = pLive->mxFrame+1; | |
| 53861 | + } | |
| 53681 | 53862 | |
| 53682 | 53863 | /* See if it is possible to write these frames into the start of the |
| 53683 | 53864 | ** log file, instead of appending to it at pWal->hdr.mxFrame. |
| 53684 | 53865 | */ |
| 53685 | 53866 | if( SQLITE_OK!=(rc = walRestartLog(pWal)) ){ |
| @@ -53741,17 +53922,45 @@ | ||
| 53741 | 53922 | szFrame = szPage + WAL_FRAME_HDRSIZE; |
| 53742 | 53923 | |
| 53743 | 53924 | /* Write all frames into the log file exactly once */ |
| 53744 | 53925 | for(p=pList; p; p=p->pDirty){ |
| 53745 | 53926 | int nDbSize; /* 0 normally. Positive == commit flag */ |
| 53927 | + | |
| 53928 | + /* Check if this page has already been written into the wal file by | |
| 53929 | + ** the current transaction. If so, overwrite the existing frame and | |
| 53930 | + ** set Wal.writeLock to WAL_WRITELOCK_RECKSUM - indicating that | |
| 53931 | + ** checksums must be recomputed when the transaction is committed. */ | |
| 53932 | + if( iFirst && (p->pDirty || isCommit==0) ){ | |
| 53933 | + u32 iWrite = 0; | |
| 53934 | + VVA_ONLY(rc =) sqlite3WalFindFrame(pWal, p->pgno, &iWrite); | |
| 53935 | + assert( rc==SQLITE_OK || iWrite==0 ); | |
| 53936 | + if( iWrite>=iFirst ){ | |
| 53937 | + i64 iOff = walFrameOffset(iWrite, szPage) + WAL_FRAME_HDRSIZE; | |
| 53938 | + if( pWal->iReCksum==0 || iWrite<pWal->iReCksum ){ | |
| 53939 | + pWal->iReCksum = iWrite; | |
| 53940 | + } | |
| 53941 | + rc = sqlite3OsWrite(pWal->pWalFd, p->pData, szPage, iOff); | |
| 53942 | + if( rc ) return rc; | |
| 53943 | + p->flags &= ~PGHDR_WAL_APPEND; | |
| 53944 | + continue; | |
| 53945 | + } | |
| 53946 | + } | |
| 53947 | + | |
| 53746 | 53948 | iFrame++; |
| 53747 | 53949 | assert( iOffset==walFrameOffset(iFrame, szPage) ); |
| 53748 | 53950 | nDbSize = (isCommit && p->pDirty==0) ? nTruncate : 0; |
| 53749 | 53951 | rc = walWriteOneFrame(&w, p, nDbSize, iOffset); |
| 53750 | 53952 | if( rc ) return rc; |
| 53751 | 53953 | pLast = p; |
| 53752 | 53954 | iOffset += szFrame; |
| 53955 | + p->flags |= PGHDR_WAL_APPEND; | |
| 53956 | + } | |
| 53957 | + | |
| 53958 | + /* Recalculate checksums within the wal file if required. */ | |
| 53959 | + if( isCommit && pWal->iReCksum ){ | |
| 53960 | + rc = walRewriteChecksums(pWal, iFrame); | |
| 53961 | + if( rc ) return rc; | |
| 53753 | 53962 | } |
| 53754 | 53963 | |
| 53755 | 53964 | /* If this is the end of a transaction, then we might need to pad |
| 53756 | 53965 | ** the transaction and/or sync the WAL file. |
| 53757 | 53966 | ** |
| @@ -53799,10 +54008,11 @@ | ||
| 53799 | 54008 | ** guarantees that there are no other writers, and no data that may |
| 53800 | 54009 | ** be in use by existing readers is being overwritten. |
| 53801 | 54010 | */ |
| 53802 | 54011 | iFrame = pWal->hdr.mxFrame; |
| 53803 | 54012 | for(p=pList; p && rc==SQLITE_OK; p=p->pDirty){ |
| 54013 | + if( (p->flags & PGHDR_WAL_APPEND)==0 ) continue; | |
| 53804 | 54014 | iFrame++; |
| 53805 | 54015 | rc = walIndexAppend(pWal, iFrame, p->pgno); |
| 53806 | 54016 | } |
| 53807 | 54017 | while( rc==SQLITE_OK && nExtra>0 ){ |
| 53808 | 54018 | iFrame++; |
| @@ -53911,10 +54121,11 @@ | ||
| 53911 | 54121 | } |
| 53912 | 54122 | } |
| 53913 | 54123 | |
| 53914 | 54124 | /* Copy data from the log to the database file. */ |
| 53915 | 54125 | if( rc==SQLITE_OK ){ |
| 54126 | + | |
| 53916 | 54127 | if( pWal->hdr.mxFrame && walPagesize(pWal)!=nBuf ){ |
| 53917 | 54128 | rc = SQLITE_CORRUPT_BKPT; |
| 53918 | 54129 | }else{ |
| 53919 | 54130 | rc = walCheckpoint(pWal, eMode2, xBusy2, pBusyArg, sync_flags, zBuf); |
| 53920 | 54131 | } |
| @@ -54066,10 +54277,16 @@ | ||
| 54066 | 54277 | SQLITE_PRIVATE int sqlite3WalFramesize(Wal *pWal){ |
| 54067 | 54278 | assert( pWal==0 || pWal->readLock>=0 ); |
| 54068 | 54279 | return (pWal ? pWal->szPage : 0); |
| 54069 | 54280 | } |
| 54070 | 54281 | #endif |
| 54282 | + | |
| 54283 | +/* Return the sqlite3_file object for the WAL file | |
| 54284 | +*/ | |
| 54285 | +SQLITE_PRIVATE sqlite3_file *sqlite3WalFile(Wal *pWal){ | |
| 54286 | + return pWal->pWalFd; | |
| 54287 | +} | |
| 54071 | 54288 | |
| 54072 | 54289 | #endif /* #ifndef SQLITE_OMIT_WAL */ |
| 54073 | 54290 | |
| 54074 | 54291 | /************** End of wal.c *************************************************/ |
| 54075 | 54292 | /************** Begin file btmutex.c *****************************************/ |
| @@ -54368,11 +54585,10 @@ | ||
| 54368 | 54585 | struct MemPage { |
| 54369 | 54586 | u8 isInit; /* True if previously initialized. MUST BE FIRST! */ |
| 54370 | 54587 | u8 nOverflow; /* Number of overflow cell bodies in aCell[] */ |
| 54371 | 54588 | u8 intKey; /* True if table b-trees. False for index b-trees */ |
| 54372 | 54589 | u8 intKeyLeaf; /* True if the leaf of an intKey table */ |
| 54373 | - u8 noPayload; /* True if internal intKey page (thus w/o data) */ | |
| 54374 | 54590 | u8 leaf; /* True if a leaf page */ |
| 54375 | 54591 | u8 hdrOffset; /* 100 for page 1. 0 otherwise */ |
| 54376 | 54592 | u8 childPtrSize; /* 0 if leaf==1. 4 if leaf==0 */ |
| 54377 | 54593 | u8 max1bytePayload; /* min(maxLocal,127) */ |
| 54378 | 54594 | u8 bBusy; /* Prevent endless loops on corrupt database files */ |
| @@ -54955,25 +55171,10 @@ | ||
| 54955 | 55171 | |
| 54956 | 55172 | return (p->sharable==0 || p->locked); |
| 54957 | 55173 | } |
| 54958 | 55174 | #endif |
| 54959 | 55175 | |
| 54960 | - | |
| 54961 | -#ifndef SQLITE_OMIT_INCRBLOB | |
| 54962 | -/* | |
| 54963 | -** Enter and leave a mutex on a Btree given a cursor owned by that | |
| 54964 | -** Btree. These entry points are used by incremental I/O and can be | |
| 54965 | -** omitted if that module is not used. | |
| 54966 | -*/ | |
| 54967 | -SQLITE_PRIVATE void sqlite3BtreeEnterCursor(BtCursor *pCur){ | |
| 54968 | - sqlite3BtreeEnter(pCur->pBtree); | |
| 54969 | -} | |
| 54970 | -SQLITE_PRIVATE void sqlite3BtreeLeaveCursor(BtCursor *pCur){ | |
| 54971 | - sqlite3BtreeLeave(pCur->pBtree); | |
| 54972 | -} | |
| 54973 | -#endif /* SQLITE_OMIT_INCRBLOB */ | |
| 54974 | - | |
| 54975 | 55176 | |
| 54976 | 55177 | /* |
| 54977 | 55178 | ** Enter the mutex on every Btree associated with a database |
| 54978 | 55179 | ** connection. This is needed (for example) prior to parsing |
| 54979 | 55180 | ** a statement since we will be comparing table and column names |
| @@ -55004,18 +55205,10 @@ | ||
| 55004 | 55205 | p = db->aDb[i].pBt; |
| 55005 | 55206 | if( p ) sqlite3BtreeLeave(p); |
| 55006 | 55207 | } |
| 55007 | 55208 | } |
| 55008 | 55209 | |
| 55009 | -/* | |
| 55010 | -** Return true if a particular Btree requires a lock. Return FALSE if | |
| 55011 | -** no lock is ever required since it is not sharable. | |
| 55012 | -*/ | |
| 55013 | -SQLITE_PRIVATE int sqlite3BtreeSharable(Btree *p){ | |
| 55014 | - return p->sharable; | |
| 55015 | -} | |
| 55016 | - | |
| 55017 | 55210 | #ifndef NDEBUG |
| 55018 | 55211 | /* |
| 55019 | 55212 | ** Return true if the current thread holds the database connection |
| 55020 | 55213 | ** mutex and all required BtShared mutexes. |
| 55021 | 55214 | ** |
| @@ -55085,10 +55278,29 @@ | ||
| 55085 | 55278 | p->pBt->db = p->db; |
| 55086 | 55279 | } |
| 55087 | 55280 | } |
| 55088 | 55281 | } |
| 55089 | 55282 | #endif /* if SQLITE_THREADSAFE */ |
| 55283 | + | |
| 55284 | +#ifndef SQLITE_OMIT_INCRBLOB | |
| 55285 | +/* | |
| 55286 | +** Enter a mutex on a Btree given a cursor owned by that Btree. | |
| 55287 | +** | |
| 55288 | +** These entry points are used by incremental I/O only. Enter() is required | |
| 55289 | +** any time OMIT_SHARED_CACHE is not defined, regardless of whether or not | |
| 55290 | +** the build is threadsafe. Leave() is only required by threadsafe builds. | |
| 55291 | +*/ | |
| 55292 | +SQLITE_PRIVATE void sqlite3BtreeEnterCursor(BtCursor *pCur){ | |
| 55293 | + sqlite3BtreeEnter(pCur->pBtree); | |
| 55294 | +} | |
| 55295 | +# if SQLITE_THREADSAFE | |
| 55296 | +SQLITE_PRIVATE void sqlite3BtreeLeaveCursor(BtCursor *pCur){ | |
| 55297 | + sqlite3BtreeLeave(pCur->pBtree); | |
| 55298 | +} | |
| 55299 | +# endif | |
| 55300 | +#endif /* ifndef SQLITE_OMIT_INCRBLOB */ | |
| 55301 | + | |
| 55090 | 55302 | #endif /* ifndef SQLITE_OMIT_SHARED_CACHE */ |
| 55091 | 55303 | |
| 55092 | 55304 | /************** End of btmutex.c *********************************************/ |
| 55093 | 55305 | /************** Begin file btree.c *******************************************/ |
| 55094 | 55306 | /* |
| @@ -55541,10 +55753,14 @@ | ||
| 55541 | 55753 | */ |
| 55542 | 55754 | #ifdef SQLITE_DEBUG |
| 55543 | 55755 | static int cursorHoldsMutex(BtCursor *p){ |
| 55544 | 55756 | return sqlite3_mutex_held(p->pBt->mutex); |
| 55545 | 55757 | } |
| 55758 | +static int cursorOwnsBtShared(BtCursor *p){ | |
| 55759 | + assert( cursorHoldsMutex(p) ); | |
| 55760 | + return (p->pBtree->db==p->pBt->db); | |
| 55761 | +} | |
| 55546 | 55762 | #endif |
| 55547 | 55763 | |
| 55548 | 55764 | /* |
| 55549 | 55765 | ** Invalidate the overflow cache of the cursor passed as the first argument. |
| 55550 | 55766 | ** on the shared btree structure pBt. |
| @@ -55877,11 +56093,11 @@ | ||
| 55877 | 56093 | ** saveCursorPosition(). |
| 55878 | 56094 | */ |
| 55879 | 56095 | static int btreeRestoreCursorPosition(BtCursor *pCur){ |
| 55880 | 56096 | int rc; |
| 55881 | 56097 | int skipNext; |
| 55882 | - assert( cursorHoldsMutex(pCur) ); | |
| 56098 | + assert( cursorOwnsBtShared(pCur) ); | |
| 55883 | 56099 | assert( pCur->eState>=CURSOR_REQUIRESEEK ); |
| 55884 | 56100 | if( pCur->eState==CURSOR_FAULT ){ |
| 55885 | 56101 | return pCur->skipNext; |
| 55886 | 56102 | } |
| 55887 | 56103 | pCur->eState = CURSOR_INVALID; |
| @@ -56166,11 +56382,10 @@ | ||
| 56166 | 56382 | u8 *pCell, /* Pointer to the cell text. */ |
| 56167 | 56383 | CellInfo *pInfo /* Fill in this structure */ |
| 56168 | 56384 | ){ |
| 56169 | 56385 | assert( sqlite3_mutex_held(pPage->pBt->mutex) ); |
| 56170 | 56386 | assert( pPage->leaf==0 ); |
| 56171 | - assert( pPage->noPayload ); | |
| 56172 | 56387 | assert( pPage->childPtrSize==4 ); |
| 56173 | 56388 | #ifndef SQLITE_DEBUG |
| 56174 | 56389 | UNUSED_PARAMETER(pPage); |
| 56175 | 56390 | #endif |
| 56176 | 56391 | pInfo->nSize = 4 + getVarint(&pCell[4], (u64*)&pInfo->nKey); |
| @@ -56188,12 +56403,10 @@ | ||
| 56188 | 56403 | u32 nPayload; /* Number of bytes of cell payload */ |
| 56189 | 56404 | u64 iKey; /* Extracted Key value */ |
| 56190 | 56405 | |
| 56191 | 56406 | assert( sqlite3_mutex_held(pPage->pBt->mutex) ); |
| 56192 | 56407 | assert( pPage->leaf==0 || pPage->leaf==1 ); |
| 56193 | - assert( pPage->intKeyLeaf || pPage->noPayload ); | |
| 56194 | - assert( pPage->noPayload==0 ); | |
| 56195 | 56408 | assert( pPage->intKeyLeaf ); |
| 56196 | 56409 | assert( pPage->childPtrSize==0 ); |
| 56197 | 56410 | pIter = pCell; |
| 56198 | 56411 | |
| 56199 | 56412 | /* The next block of code is equivalent to: |
| @@ -56258,11 +56471,10 @@ | ||
| 56258 | 56471 | u32 nPayload; /* Number of bytes of cell payload */ |
| 56259 | 56472 | |
| 56260 | 56473 | assert( sqlite3_mutex_held(pPage->pBt->mutex) ); |
| 56261 | 56474 | assert( pPage->leaf==0 || pPage->leaf==1 ); |
| 56262 | 56475 | assert( pPage->intKeyLeaf==0 ); |
| 56263 | - assert( pPage->noPayload==0 ); | |
| 56264 | 56476 | pIter = pCell + pPage->childPtrSize; |
| 56265 | 56477 | nPayload = *pIter; |
| 56266 | 56478 | if( nPayload>=0x80 ){ |
| 56267 | 56479 | u8 *pEnd = &pIter[8]; |
| 56268 | 56480 | nPayload &= 0x7f; |
| @@ -56319,11 +56531,10 @@ | ||
| 56319 | 56531 | ** this function verifies that this invariant is not violated. */ |
| 56320 | 56532 | CellInfo debuginfo; |
| 56321 | 56533 | pPage->xParseCell(pPage, pCell, &debuginfo); |
| 56322 | 56534 | #endif |
| 56323 | 56535 | |
| 56324 | - assert( pPage->noPayload==0 ); | |
| 56325 | 56536 | nSize = *pIter; |
| 56326 | 56537 | if( nSize>=0x80 ){ |
| 56327 | 56538 | pEnd = &pIter[8]; |
| 56328 | 56539 | nSize &= 0x7f; |
| 56329 | 56540 | do{ |
| @@ -56777,15 +56988,13 @@ | ||
| 56777 | 56988 | ** table b-tree page. */ |
| 56778 | 56989 | assert( (PTF_LEAFDATA|PTF_INTKEY|PTF_LEAF)==13 ); |
| 56779 | 56990 | pPage->intKey = 1; |
| 56780 | 56991 | if( pPage->leaf ){ |
| 56781 | 56992 | pPage->intKeyLeaf = 1; |
| 56782 | - pPage->noPayload = 0; | |
| 56783 | 56993 | pPage->xParseCell = btreeParseCellPtr; |
| 56784 | 56994 | }else{ |
| 56785 | 56995 | pPage->intKeyLeaf = 0; |
| 56786 | - pPage->noPayload = 1; | |
| 56787 | 56996 | pPage->xCellSize = cellSizePtrNoPayload; |
| 56788 | 56997 | pPage->xParseCell = btreeParseCellPtrNoPayload; |
| 56789 | 56998 | } |
| 56790 | 56999 | pPage->maxLocal = pBt->maxLeaf; |
| 56791 | 57000 | pPage->minLocal = pBt->minLeaf; |
| @@ -56796,11 +57005,10 @@ | ||
| 56796 | 57005 | /* EVIDENCE-OF: R-16571-11615 A value of 10 means the page is a leaf |
| 56797 | 57006 | ** index b-tree page. */ |
| 56798 | 57007 | assert( (PTF_ZERODATA|PTF_LEAF)==10 ); |
| 56799 | 57008 | pPage->intKey = 0; |
| 56800 | 57009 | pPage->intKeyLeaf = 0; |
| 56801 | - pPage->noPayload = 0; | |
| 56802 | 57010 | pPage->xParseCell = btreeParseCellPtrIndex; |
| 56803 | 57011 | pPage->maxLocal = pBt->maxLocal; |
| 56804 | 57012 | pPage->minLocal = pBt->minLocal; |
| 56805 | 57013 | }else{ |
| 56806 | 57014 | /* EVIDENCE-OF: R-47608-56469 Any other value for the b-tree page type is |
| @@ -58217,11 +58425,10 @@ | ||
| 58217 | 58425 | ** no progress. By returning SQLITE_BUSY and not invoking the busy callback |
| 58218 | 58426 | ** when A already has a read lock, we encourage A to give up and let B |
| 58219 | 58427 | ** proceed. |
| 58220 | 58428 | */ |
| 58221 | 58429 | SQLITE_PRIVATE int sqlite3BtreeBeginTrans(Btree *p, int wrflag){ |
| 58222 | - sqlite3 *pBlock = 0; | |
| 58223 | 58430 | BtShared *pBt = p->pBt; |
| 58224 | 58431 | int rc = SQLITE_OK; |
| 58225 | 58432 | |
| 58226 | 58433 | sqlite3BtreeEnter(p); |
| 58227 | 58434 | btreeIntegrity(p); |
| @@ -58240,31 +58447,34 @@ | ||
| 58240 | 58447 | rc = SQLITE_READONLY; |
| 58241 | 58448 | goto trans_begun; |
| 58242 | 58449 | } |
| 58243 | 58450 | |
| 58244 | 58451 | #ifndef SQLITE_OMIT_SHARED_CACHE |
| 58245 | - /* If another database handle has already opened a write transaction | |
| 58246 | - ** on this shared-btree structure and a second write transaction is | |
| 58247 | - ** requested, return SQLITE_LOCKED. | |
| 58248 | - */ | |
| 58249 | - if( (wrflag && pBt->inTransaction==TRANS_WRITE) | |
| 58250 | - || (pBt->btsFlags & BTS_PENDING)!=0 | |
| 58251 | - ){ | |
| 58252 | - pBlock = pBt->pWriter->db; | |
| 58253 | - }else if( wrflag>1 ){ | |
| 58254 | - BtLock *pIter; | |
| 58255 | - for(pIter=pBt->pLock; pIter; pIter=pIter->pNext){ | |
| 58256 | - if( pIter->pBtree!=p ){ | |
| 58257 | - pBlock = pIter->pBtree->db; | |
| 58258 | - break; | |
| 58259 | - } | |
| 58260 | - } | |
| 58261 | - } | |
| 58262 | - if( pBlock ){ | |
| 58263 | - sqlite3ConnectionBlocked(p->db, pBlock); | |
| 58264 | - rc = SQLITE_LOCKED_SHAREDCACHE; | |
| 58265 | - goto trans_begun; | |
| 58452 | + { | |
| 58453 | + sqlite3 *pBlock = 0; | |
| 58454 | + /* If another database handle has already opened a write transaction | |
| 58455 | + ** on this shared-btree structure and a second write transaction is | |
| 58456 | + ** requested, return SQLITE_LOCKED. | |
| 58457 | + */ | |
| 58458 | + if( (wrflag && pBt->inTransaction==TRANS_WRITE) | |
| 58459 | + || (pBt->btsFlags & BTS_PENDING)!=0 | |
| 58460 | + ){ | |
| 58461 | + pBlock = pBt->pWriter->db; | |
| 58462 | + }else if( wrflag>1 ){ | |
| 58463 | + BtLock *pIter; | |
| 58464 | + for(pIter=pBt->pLock; pIter; pIter=pIter->pNext){ | |
| 58465 | + if( pIter->pBtree!=p ){ | |
| 58466 | + pBlock = pIter->pBtree->db; | |
| 58467 | + break; | |
| 58468 | + } | |
| 58469 | + } | |
| 58470 | + } | |
| 58471 | + if( pBlock ){ | |
| 58472 | + sqlite3ConnectionBlocked(p->db, pBlock); | |
| 58473 | + rc = SQLITE_LOCKED_SHAREDCACHE; | |
| 58474 | + goto trans_begun; | |
| 58475 | + } | |
| 58266 | 58476 | } |
| 58267 | 58477 | #endif |
| 58268 | 58478 | |
| 58269 | 58479 | /* Any read-only or read-write transaction implies a read-lock on |
| 58270 | 58480 | ** page 1. So if some other shared-cache client already has a write-lock |
| @@ -59377,11 +59587,11 @@ | ||
| 59377 | 59587 | ** Failure is not possible. This function always returns SQLITE_OK. |
| 59378 | 59588 | ** It might just as well be a procedure (returning void) but we continue |
| 59379 | 59589 | ** to return an integer result code for historical reasons. |
| 59380 | 59590 | */ |
| 59381 | 59591 | SQLITE_PRIVATE int sqlite3BtreeDataSize(BtCursor *pCur, u32 *pSize){ |
| 59382 | - assert( cursorHoldsMutex(pCur) ); | |
| 59592 | + assert( cursorOwnsBtShared(pCur) ); | |
| 59383 | 59593 | assert( pCur->eState==CURSOR_VALID ); |
| 59384 | 59594 | assert( pCur->iPage>=0 ); |
| 59385 | 59595 | assert( pCur->iPage<BTCURSOR_MAX_DEPTH ); |
| 59386 | 59596 | assert( pCur->apPage[pCur->iPage]->intKeyLeaf==1 ); |
| 59387 | 59597 | getCellInfo(pCur); |
| @@ -59757,11 +59967,11 @@ | ||
| 59757 | 59967 | if ( pCur->eState==CURSOR_INVALID ){ |
| 59758 | 59968 | return SQLITE_ABORT; |
| 59759 | 59969 | } |
| 59760 | 59970 | #endif |
| 59761 | 59971 | |
| 59762 | - assert( cursorHoldsMutex(pCur) ); | |
| 59972 | + assert( cursorOwnsBtShared(pCur) ); | |
| 59763 | 59973 | rc = restoreCursorPosition(pCur); |
| 59764 | 59974 | if( rc==SQLITE_OK ){ |
| 59765 | 59975 | assert( pCur->eState==CURSOR_VALID ); |
| 59766 | 59976 | assert( pCur->iPage>=0 && pCur->apPage[pCur->iPage] ); |
| 59767 | 59977 | assert( pCur->aiIdx[pCur->iPage]<pCur->apPage[pCur->iPage]->nCell ); |
| @@ -59795,11 +60005,11 @@ | ||
| 59795 | 60005 | ){ |
| 59796 | 60006 | u32 amt; |
| 59797 | 60007 | assert( pCur!=0 && pCur->iPage>=0 && pCur->apPage[pCur->iPage]); |
| 59798 | 60008 | assert( pCur->eState==CURSOR_VALID ); |
| 59799 | 60009 | assert( sqlite3_mutex_held(pCur->pBtree->db->mutex) ); |
| 59800 | - assert( cursorHoldsMutex(pCur) ); | |
| 60010 | + assert( cursorOwnsBtShared(pCur) ); | |
| 59801 | 60011 | assert( pCur->aiIdx[pCur->iPage]<pCur->apPage[pCur->iPage]->nCell ); |
| 59802 | 60012 | assert( pCur->info.nSize>0 ); |
| 59803 | 60013 | assert( pCur->info.pPayload>pCur->apPage[pCur->iPage]->aData || CORRUPT_DB ); |
| 59804 | 60014 | assert( pCur->info.pPayload<pCur->apPage[pCur->iPage]->aDataEnd ||CORRUPT_DB); |
| 59805 | 60015 | amt = (int)(pCur->apPage[pCur->iPage]->aDataEnd - pCur->info.pPayload); |
| @@ -59841,11 +60051,11 @@ | ||
| 59841 | 60051 | ** vice-versa). |
| 59842 | 60052 | */ |
| 59843 | 60053 | static int moveToChild(BtCursor *pCur, u32 newPgno){ |
| 59844 | 60054 | BtShared *pBt = pCur->pBt; |
| 59845 | 60055 | |
| 59846 | - assert( cursorHoldsMutex(pCur) ); | |
| 60056 | + assert( cursorOwnsBtShared(pCur) ); | |
| 59847 | 60057 | assert( pCur->eState==CURSOR_VALID ); |
| 59848 | 60058 | assert( pCur->iPage<BTCURSOR_MAX_DEPTH ); |
| 59849 | 60059 | assert( pCur->iPage>=0 ); |
| 59850 | 60060 | if( pCur->iPage>=(BTCURSOR_MAX_DEPTH-1) ){ |
| 59851 | 60061 | return SQLITE_CORRUPT_BKPT; |
| @@ -59887,11 +60097,11 @@ | ||
| 59887 | 60097 | ** to the page we are coming from. If we are coming from the |
| 59888 | 60098 | ** right-most child page then pCur->idx is set to one more than |
| 59889 | 60099 | ** the largest cell index. |
| 59890 | 60100 | */ |
| 59891 | 60101 | static void moveToParent(BtCursor *pCur){ |
| 59892 | - assert( cursorHoldsMutex(pCur) ); | |
| 60102 | + assert( cursorOwnsBtShared(pCur) ); | |
| 59893 | 60103 | assert( pCur->eState==CURSOR_VALID ); |
| 59894 | 60104 | assert( pCur->iPage>0 ); |
| 59895 | 60105 | assert( pCur->apPage[pCur->iPage] ); |
| 59896 | 60106 | assertParentIndex( |
| 59897 | 60107 | pCur->apPage[pCur->iPage-1], |
| @@ -59927,11 +60137,11 @@ | ||
| 59927 | 60137 | */ |
| 59928 | 60138 | static int moveToRoot(BtCursor *pCur){ |
| 59929 | 60139 | MemPage *pRoot; |
| 59930 | 60140 | int rc = SQLITE_OK; |
| 59931 | 60141 | |
| 59932 | - assert( cursorHoldsMutex(pCur) ); | |
| 60142 | + assert( cursorOwnsBtShared(pCur) ); | |
| 59933 | 60143 | assert( CURSOR_INVALID < CURSOR_REQUIRESEEK ); |
| 59934 | 60144 | assert( CURSOR_VALID < CURSOR_REQUIRESEEK ); |
| 59935 | 60145 | assert( CURSOR_FAULT > CURSOR_REQUIRESEEK ); |
| 59936 | 60146 | if( pCur->eState>=CURSOR_REQUIRESEEK ){ |
| 59937 | 60147 | if( pCur->eState==CURSOR_FAULT ){ |
| @@ -60006,11 +60216,11 @@ | ||
| 60006 | 60216 | static int moveToLeftmost(BtCursor *pCur){ |
| 60007 | 60217 | Pgno pgno; |
| 60008 | 60218 | int rc = SQLITE_OK; |
| 60009 | 60219 | MemPage *pPage; |
| 60010 | 60220 | |
| 60011 | - assert( cursorHoldsMutex(pCur) ); | |
| 60221 | + assert( cursorOwnsBtShared(pCur) ); | |
| 60012 | 60222 | assert( pCur->eState==CURSOR_VALID ); |
| 60013 | 60223 | while( rc==SQLITE_OK && !(pPage = pCur->apPage[pCur->iPage])->leaf ){ |
| 60014 | 60224 | assert( pCur->aiIdx[pCur->iPage]<pPage->nCell ); |
| 60015 | 60225 | pgno = get4byte(findCell(pPage, pCur->aiIdx[pCur->iPage])); |
| 60016 | 60226 | rc = moveToChild(pCur, pgno); |
| @@ -60031,11 +60241,11 @@ | ||
| 60031 | 60241 | static int moveToRightmost(BtCursor *pCur){ |
| 60032 | 60242 | Pgno pgno; |
| 60033 | 60243 | int rc = SQLITE_OK; |
| 60034 | 60244 | MemPage *pPage = 0; |
| 60035 | 60245 | |
| 60036 | - assert( cursorHoldsMutex(pCur) ); | |
| 60246 | + assert( cursorOwnsBtShared(pCur) ); | |
| 60037 | 60247 | assert( pCur->eState==CURSOR_VALID ); |
| 60038 | 60248 | while( !(pPage = pCur->apPage[pCur->iPage])->leaf ){ |
| 60039 | 60249 | pgno = get4byte(&pPage->aData[pPage->hdrOffset+8]); |
| 60040 | 60250 | pCur->aiIdx[pCur->iPage] = pPage->nCell; |
| 60041 | 60251 | rc = moveToChild(pCur, pgno); |
| @@ -60052,11 +60262,11 @@ | ||
| 60052 | 60262 | ** or set *pRes to 1 if the table is empty. |
| 60053 | 60263 | */ |
| 60054 | 60264 | SQLITE_PRIVATE int sqlite3BtreeFirst(BtCursor *pCur, int *pRes){ |
| 60055 | 60265 | int rc; |
| 60056 | 60266 | |
| 60057 | - assert( cursorHoldsMutex(pCur) ); | |
| 60267 | + assert( cursorOwnsBtShared(pCur) ); | |
| 60058 | 60268 | assert( sqlite3_mutex_held(pCur->pBtree->db->mutex) ); |
| 60059 | 60269 | rc = moveToRoot(pCur); |
| 60060 | 60270 | if( rc==SQLITE_OK ){ |
| 60061 | 60271 | if( pCur->eState==CURSOR_INVALID ){ |
| 60062 | 60272 | assert( pCur->pgnoRoot==0 || pCur->apPage[pCur->iPage]->nCell==0 ); |
| @@ -60075,11 +60285,11 @@ | ||
| 60075 | 60285 | ** or set *pRes to 1 if the table is empty. |
| 60076 | 60286 | */ |
| 60077 | 60287 | SQLITE_PRIVATE int sqlite3BtreeLast(BtCursor *pCur, int *pRes){ |
| 60078 | 60288 | int rc; |
| 60079 | 60289 | |
| 60080 | - assert( cursorHoldsMutex(pCur) ); | |
| 60290 | + assert( cursorOwnsBtShared(pCur) ); | |
| 60081 | 60291 | assert( sqlite3_mutex_held(pCur->pBtree->db->mutex) ); |
| 60082 | 60292 | |
| 60083 | 60293 | /* If the cursor already points to the last entry, this is a no-op. */ |
| 60084 | 60294 | if( CURSOR_VALID==pCur->eState && (pCur->curFlags & BTCF_AtLast)!=0 ){ |
| 60085 | 60295 | #ifdef SQLITE_DEBUG |
| @@ -60153,11 +60363,11 @@ | ||
| 60153 | 60363 | int *pRes /* Write search results here */ |
| 60154 | 60364 | ){ |
| 60155 | 60365 | int rc; |
| 60156 | 60366 | RecordCompare xRecordCompare; |
| 60157 | 60367 | |
| 60158 | - assert( cursorHoldsMutex(pCur) ); | |
| 60368 | + assert( cursorOwnsBtShared(pCur) ); | |
| 60159 | 60369 | assert( sqlite3_mutex_held(pCur->pBtree->db->mutex) ); |
| 60160 | 60370 | assert( pRes ); |
| 60161 | 60371 | assert( (pIdxKey==0)==(pCur->pKeyInfo==0) ); |
| 60162 | 60372 | |
| 60163 | 60373 | /* If the cursor is already positioned at the point we are trying |
| @@ -60401,11 +60611,11 @@ | ||
| 60401 | 60611 | static SQLITE_NOINLINE int btreeNext(BtCursor *pCur, int *pRes){ |
| 60402 | 60612 | int rc; |
| 60403 | 60613 | int idx; |
| 60404 | 60614 | MemPage *pPage; |
| 60405 | 60615 | |
| 60406 | - assert( cursorHoldsMutex(pCur) ); | |
| 60616 | + assert( cursorOwnsBtShared(pCur) ); | |
| 60407 | 60617 | assert( pCur->skipNext==0 || pCur->eState!=CURSOR_VALID ); |
| 60408 | 60618 | assert( *pRes==0 ); |
| 60409 | 60619 | if( pCur->eState!=CURSOR_VALID ){ |
| 60410 | 60620 | assert( (pCur->curFlags & BTCF_ValidOvfl)==0 ); |
| 60411 | 60621 | rc = restoreCursorPosition(pCur); |
| @@ -60465,11 +60675,11 @@ | ||
| 60465 | 60675 | return moveToLeftmost(pCur); |
| 60466 | 60676 | } |
| 60467 | 60677 | } |
| 60468 | 60678 | SQLITE_PRIVATE int sqlite3BtreeNext(BtCursor *pCur, int *pRes){ |
| 60469 | 60679 | MemPage *pPage; |
| 60470 | - assert( cursorHoldsMutex(pCur) ); | |
| 60680 | + assert( cursorOwnsBtShared(pCur) ); | |
| 60471 | 60681 | assert( pRes!=0 ); |
| 60472 | 60682 | assert( *pRes==0 || *pRes==1 ); |
| 60473 | 60683 | assert( pCur->skipNext==0 || pCur->eState!=CURSOR_VALID ); |
| 60474 | 60684 | pCur->info.nSize = 0; |
| 60475 | 60685 | pCur->curFlags &= ~(BTCF_ValidNKey|BTCF_ValidOvfl); |
| @@ -60510,11 +60720,11 @@ | ||
| 60510 | 60720 | */ |
| 60511 | 60721 | static SQLITE_NOINLINE int btreePrevious(BtCursor *pCur, int *pRes){ |
| 60512 | 60722 | int rc; |
| 60513 | 60723 | MemPage *pPage; |
| 60514 | 60724 | |
| 60515 | - assert( cursorHoldsMutex(pCur) ); | |
| 60725 | + assert( cursorOwnsBtShared(pCur) ); | |
| 60516 | 60726 | assert( pRes!=0 ); |
| 60517 | 60727 | assert( *pRes==0 ); |
| 60518 | 60728 | assert( pCur->skipNext==0 || pCur->eState!=CURSOR_VALID ); |
| 60519 | 60729 | assert( (pCur->curFlags & (BTCF_AtLast|BTCF_ValidOvfl|BTCF_ValidNKey))==0 ); |
| 60520 | 60730 | assert( pCur->info.nSize==0 ); |
| @@ -60566,11 +60776,11 @@ | ||
| 60566 | 60776 | } |
| 60567 | 60777 | } |
| 60568 | 60778 | return rc; |
| 60569 | 60779 | } |
| 60570 | 60780 | SQLITE_PRIVATE int sqlite3BtreePrevious(BtCursor *pCur, int *pRes){ |
| 60571 | - assert( cursorHoldsMutex(pCur) ); | |
| 60781 | + assert( cursorOwnsBtShared(pCur) ); | |
| 60572 | 60782 | assert( pRes!=0 ); |
| 60573 | 60783 | assert( *pRes==0 || *pRes==1 ); |
| 60574 | 60784 | assert( pCur->skipNext==0 || pCur->eState!=CURSOR_VALID ); |
| 60575 | 60785 | *pRes = 0; |
| 60576 | 60786 | pCur->curFlags &= ~(BTCF_AtLast|BTCF_ValidOvfl|BTCF_ValidNKey); |
| @@ -63046,11 +63256,11 @@ | ||
| 63046 | 63256 | if( pCur->eState==CURSOR_FAULT ){ |
| 63047 | 63257 | assert( pCur->skipNext!=SQLITE_OK ); |
| 63048 | 63258 | return pCur->skipNext; |
| 63049 | 63259 | } |
| 63050 | 63260 | |
| 63051 | - assert( cursorHoldsMutex(pCur) ); | |
| 63261 | + assert( cursorOwnsBtShared(pCur) ); | |
| 63052 | 63262 | assert( (pCur->curFlags & BTCF_WriteFlag)!=0 |
| 63053 | 63263 | && pBt->inTransaction==TRANS_WRITE |
| 63054 | 63264 | && (pBt->btsFlags & BTS_READ_ONLY)==0 ); |
| 63055 | 63265 | assert( hasSharedCacheTableLock(p, pCur->pgnoRoot, pCur->pKeyInfo!=0, 2) ); |
| 63056 | 63266 | |
| @@ -63193,11 +63403,11 @@ | ||
| 63193 | 63403 | int iCellIdx; /* Index of cell to delete */ |
| 63194 | 63404 | int iCellDepth; /* Depth of node containing pCell */ |
| 63195 | 63405 | u16 szCell; /* Size of the cell being deleted */ |
| 63196 | 63406 | int bSkipnext = 0; /* Leaf cursor in SKIPNEXT state */ |
| 63197 | 63407 | |
| 63198 | - assert( cursorHoldsMutex(pCur) ); | |
| 63408 | + assert( cursorOwnsBtShared(pCur) ); | |
| 63199 | 63409 | assert( pBt->inTransaction==TRANS_WRITE ); |
| 63200 | 63410 | assert( (pBt->btsFlags & BTS_READ_ONLY)==0 ); |
| 63201 | 63411 | assert( pCur->curFlags & BTCF_WriteFlag ); |
| 63202 | 63412 | assert( hasSharedCacheTableLock(p, pCur->pgnoRoot, pCur->pKeyInfo!=0, 2) ); |
| 63203 | 63413 | assert( !hasReadConflicts(p, pCur->pgnoRoot) ); |
| @@ -64655,11 +64865,11 @@ | ||
| 64655 | 64865 | ** parameters that attempt to write past the end of the existing data, |
| 64656 | 64866 | ** no modifications are made and SQLITE_CORRUPT is returned. |
| 64657 | 64867 | */ |
| 64658 | 64868 | SQLITE_PRIVATE int sqlite3BtreePutData(BtCursor *pCsr, u32 offset, u32 amt, void *z){ |
| 64659 | 64869 | int rc; |
| 64660 | - assert( cursorHoldsMutex(pCsr) ); | |
| 64870 | + assert( cursorOwnsBtShared(pCsr) ); | |
| 64661 | 64871 | assert( sqlite3_mutex_held(pCsr->pBtree->db->mutex) ); |
| 64662 | 64872 | assert( pCsr->curFlags & BTCF_Incrblob ); |
| 64663 | 64873 | |
| 64664 | 64874 | rc = restoreCursorPosition(pCsr); |
| 64665 | 64875 | if( rc!=SQLITE_OK ){ |
| @@ -64762,10 +64972,19 @@ | ||
| 64762 | 64972 | |
| 64763 | 64973 | /* |
| 64764 | 64974 | ** Return the size of the header added to each page by this module. |
| 64765 | 64975 | */ |
| 64766 | 64976 | SQLITE_PRIVATE int sqlite3HeaderSizeBtree(void){ return ROUND8(sizeof(MemPage)); } |
| 64977 | + | |
| 64978 | +#if !defined(SQLITE_OMIT_SHARED_CACHE) | |
| 64979 | +/* | |
| 64980 | +** Return true if the Btree passed as the only argument is sharable. | |
| 64981 | +*/ | |
| 64982 | +SQLITE_PRIVATE int sqlite3BtreeSharable(Btree *p){ | |
| 64983 | + return p->sharable; | |
| 64984 | +} | |
| 64985 | +#endif | |
| 64767 | 64986 | |
| 64768 | 64987 | /************** End of btree.c ***********************************************/ |
| 64769 | 64988 | /************** Begin file backup.c ******************************************/ |
| 64770 | 64989 | /* |
| 64771 | 64990 | ** 2009 January 28 |
| @@ -67593,12 +67812,11 @@ | ||
| 67593 | 67812 | ** The zWhere string must have been obtained from sqlite3_malloc(). |
| 67594 | 67813 | ** This routine will take ownership of the allocated memory. |
| 67595 | 67814 | */ |
| 67596 | 67815 | SQLITE_PRIVATE void sqlite3VdbeAddParseSchemaOp(Vdbe *p, int iDb, char *zWhere){ |
| 67597 | 67816 | int j; |
| 67598 | - int addr = sqlite3VdbeAddOp3(p, OP_ParseSchema, iDb, 0, 0); | |
| 67599 | - sqlite3VdbeChangeP4(p, addr, zWhere, P4_DYNAMIC); | |
| 67817 | + sqlite3VdbeAddOp4(p, OP_ParseSchema, iDb, 0, 0, zWhere, P4_DYNAMIC); | |
| 67600 | 67818 | for(j=0; j<p->db->nDb; j++) sqlite3VdbeUsesBtree(p, j); |
| 67601 | 67819 | } |
| 67602 | 67820 | |
| 67603 | 67821 | /* |
| 67604 | 67822 | ** Add an opcode that includes the p4 value as an integer. |
| @@ -68093,11 +68311,11 @@ | ||
| 68093 | 68311 | */ |
| 68094 | 68312 | static void vdbeFreeOpArray(sqlite3 *db, Op *aOp, int nOp){ |
| 68095 | 68313 | if( aOp ){ |
| 68096 | 68314 | Op *pOp; |
| 68097 | 68315 | for(pOp=aOp; pOp<&aOp[nOp]; pOp++){ |
| 68098 | - freeP4(db, pOp->p4type, pOp->p4.p); | |
| 68316 | + if( pOp->p4type ) freeP4(db, pOp->p4type, pOp->p4.p); | |
| 68099 | 68317 | #ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS |
| 68100 | 68318 | sqlite3DbFree(db, pOp->zComment); |
| 68101 | 68319 | #endif |
| 68102 | 68320 | } |
| 68103 | 68321 | } |
| @@ -68155,65 +68373,60 @@ | ||
| 68155 | 68373 | ** to a string or structure that is guaranteed to exist for the lifetime of |
| 68156 | 68374 | ** the Vdbe. In these cases we can just copy the pointer. |
| 68157 | 68375 | ** |
| 68158 | 68376 | ** If addr<0 then change P4 on the most recently inserted instruction. |
| 68159 | 68377 | */ |
| 68378 | +static void SQLITE_NOINLINE vdbeChangeP4Full( | |
| 68379 | + Vdbe *p, | |
| 68380 | + Op *pOp, | |
| 68381 | + const char *zP4, | |
| 68382 | + int n | |
| 68383 | +){ | |
| 68384 | + if( pOp->p4type ){ | |
| 68385 | + freeP4(p->db, pOp->p4type, pOp->p4.p); | |
| 68386 | + pOp->p4type = 0; | |
| 68387 | + pOp->p4.p = 0; | |
| 68388 | + } | |
| 68389 | + if( n<0 ){ | |
| 68390 | + sqlite3VdbeChangeP4(p, (int)(pOp - p->aOp), zP4, n); | |
| 68391 | + }else{ | |
| 68392 | + if( n==0 ) n = sqlite3Strlen30(zP4); | |
| 68393 | + pOp->p4.z = sqlite3DbStrNDup(p->db, zP4, n); | |
| 68394 | + pOp->p4type = P4_DYNAMIC; | |
| 68395 | + } | |
| 68396 | +} | |
| 68160 | 68397 | SQLITE_PRIVATE void sqlite3VdbeChangeP4(Vdbe *p, int addr, const char *zP4, int n){ |
| 68161 | 68398 | Op *pOp; |
| 68162 | 68399 | sqlite3 *db; |
| 68163 | 68400 | assert( p!=0 ); |
| 68164 | 68401 | db = p->db; |
| 68165 | 68402 | assert( p->magic==VDBE_MAGIC_INIT ); |
| 68166 | - if( p->aOp==0 || db->mallocFailed ){ | |
| 68167 | - if( n!=P4_VTAB ){ | |
| 68168 | - freeP4(db, n, (void*)*(char**)&zP4); | |
| 68169 | - } | |
| 68403 | + assert( p->aOp!=0 || db->mallocFailed ); | |
| 68404 | + if( db->mallocFailed ){ | |
| 68405 | + if( n!=P4_VTAB ) freeP4(db, n, (void*)*(char**)&zP4); | |
| 68170 | 68406 | return; |
| 68171 | 68407 | } |
| 68172 | 68408 | assert( p->nOp>0 ); |
| 68173 | 68409 | assert( addr<p->nOp ); |
| 68174 | 68410 | if( addr<0 ){ |
| 68175 | 68411 | addr = p->nOp - 1; |
| 68176 | 68412 | } |
| 68177 | 68413 | pOp = &p->aOp[addr]; |
| 68178 | - assert( pOp->p4type==P4_NOTUSED | |
| 68179 | - || pOp->p4type==P4_INT32 | |
| 68180 | - || pOp->p4type==P4_KEYINFO ); | |
| 68181 | - freeP4(db, pOp->p4type, pOp->p4.p); | |
| 68182 | - pOp->p4.p = 0; | |
| 68414 | + if( n>=0 || pOp->p4type ){ | |
| 68415 | + vdbeChangeP4Full(p, pOp, zP4, n); | |
| 68416 | + return; | |
| 68417 | + } | |
| 68183 | 68418 | if( n==P4_INT32 ){ |
| 68184 | 68419 | /* Note: this cast is safe, because the origin data point was an int |
| 68185 | 68420 | ** that was cast to a (const char *). */ |
| 68186 | 68421 | pOp->p4.i = SQLITE_PTR_TO_INT(zP4); |
| 68187 | 68422 | pOp->p4type = P4_INT32; |
| 68188 | - }else if( zP4==0 ){ | |
| 68189 | - pOp->p4.p = 0; | |
| 68190 | - pOp->p4type = P4_NOTUSED; | |
| 68191 | - }else if( n==P4_KEYINFO ){ | |
| 68192 | - pOp->p4.p = (void*)zP4; | |
| 68193 | - pOp->p4type = P4_KEYINFO; | |
| 68194 | -#ifdef SQLITE_ENABLE_CURSOR_HINTS | |
| 68195 | - }else if( n==P4_EXPR ){ | |
| 68196 | - /* Responsibility for deleting the Expr tree is handed over to the | |
| 68197 | - ** VDBE by this operation. The caller should have already invoked | |
| 68198 | - ** sqlite3ExprDup() or whatever other routine is needed to make a | |
| 68199 | - ** private copy of the tree. */ | |
| 68200 | - pOp->p4.pExpr = (Expr*)zP4; | |
| 68201 | - pOp->p4type = P4_EXPR; | |
| 68202 | -#endif | |
| 68203 | - }else if( n==P4_VTAB ){ | |
| 68204 | - pOp->p4.p = (void*)zP4; | |
| 68205 | - pOp->p4type = P4_VTAB; | |
| 68206 | - sqlite3VtabLock((VTable *)zP4); | |
| 68207 | - assert( ((VTable *)zP4)->db==p->db ); | |
| 68208 | - }else if( n<0 ){ | |
| 68423 | + }else if( zP4!=0 ){ | |
| 68424 | + assert( n<0 ); | |
| 68209 | 68425 | pOp->p4.p = (void*)zP4; |
| 68210 | 68426 | pOp->p4type = (signed char)n; |
| 68211 | - }else{ | |
| 68212 | - if( n==0 ) n = sqlite3Strlen30(zP4); | |
| 68213 | - pOp->p4.z = sqlite3DbStrNDup(p->db, zP4, n); | |
| 68214 | - pOp->p4type = P4_DYNAMIC; | |
| 68427 | + if( n==P4_VTAB ) sqlite3VtabLock((VTable*)zP4); | |
| 68215 | 68428 | } |
| 68216 | 68429 | } |
| 68217 | 68430 | |
| 68218 | 68431 | /* |
| 68219 | 68432 | ** Set the P4 on the most recently added opcode to the KeyInfo for the |
| @@ -68605,11 +68818,11 @@ | ||
| 68605 | 68818 | if( i!=1 && sqlite3BtreeSharable(p->db->aDb[i].pBt) ){ |
| 68606 | 68819 | DbMaskSet(p->lockMask, i); |
| 68607 | 68820 | } |
| 68608 | 68821 | } |
| 68609 | 68822 | |
| 68610 | -#if !defined(SQLITE_OMIT_SHARED_CACHE) && SQLITE_THREADSAFE>0 | |
| 68823 | +#if !defined(SQLITE_OMIT_SHARED_CACHE) | |
| 68611 | 68824 | /* |
| 68612 | 68825 | ** If SQLite is compiled to support shared-cache mode and to be threadsafe, |
| 68613 | 68826 | ** this routine obtains the mutex associated with each BtShared structure |
| 68614 | 68827 | ** that may be accessed by the VM passed as an argument. In doing so it also |
| 68615 | 68828 | ** sets the BtShared.db member of each of the BtShared structures, ensuring |
| @@ -76059,11 +76272,10 @@ | ||
| 76059 | 76272 | const u8 *zEndHdr; /* Pointer to first byte after the header */ |
| 76060 | 76273 | u32 offset; /* Offset into the data */ |
| 76061 | 76274 | u64 offset64; /* 64-bit offset */ |
| 76062 | 76275 | u32 avail; /* Number of bytes of available data */ |
| 76063 | 76276 | u32 t; /* A type code from the record header */ |
| 76064 | - u16 fx; /* pDest->flags value */ | |
| 76065 | 76277 | Mem *pReg; /* PseudoTable input register */ |
| 76066 | 76278 | |
| 76067 | 76279 | p2 = pOp->p2; |
| 76068 | 76280 | assert( pOp->p3>0 && pOp->p3<=(p->nMem-p->nCursor) ); |
| 76069 | 76281 | pDest = &aMem[pOp->p3]; |
| @@ -76237,14 +76449,35 @@ | ||
| 76237 | 76449 | assert( p2<pC->nHdrParsed ); |
| 76238 | 76450 | assert( rc==SQLITE_OK ); |
| 76239 | 76451 | assert( sqlite3VdbeCheckMemInvariants(pDest) ); |
| 76240 | 76452 | if( VdbeMemDynamic(pDest) ) sqlite3VdbeMemSetNull(pDest); |
| 76241 | 76453 | assert( t==pC->aType[p2] ); |
| 76454 | + pDest->enc = encoding; | |
| 76242 | 76455 | if( pC->szRow>=aOffset[p2+1] ){ |
| 76243 | 76456 | /* This is the common case where the desired content fits on the original |
| 76244 | 76457 | ** page - where the content is not on an overflow page */ |
| 76245 | - sqlite3VdbeSerialGet(pC->aRow+aOffset[p2], t, pDest); | |
| 76458 | + zData = pC->aRow + aOffset[p2]; | |
| 76459 | + if( t<12 ){ | |
| 76460 | + sqlite3VdbeSerialGet(zData, t, pDest); | |
| 76461 | + }else{ | |
| 76462 | + /* If the column value is a string, we need a persistent value, not | |
| 76463 | + ** a MEM_Ephem value. This branch is a fast short-cut that is equivalent | |
| 76464 | + ** to calling sqlite3VdbeSerialGet() and sqlite3VdbeDeephemeralize(). | |
| 76465 | + */ | |
| 76466 | + static const u16 aFlag[] = { MEM_Blob, MEM_Str|MEM_Term }; | |
| 76467 | + pDest->n = len = (t-12)/2; | |
| 76468 | + if( pDest->szMalloc < len+2 ){ | |
| 76469 | + pDest->flags = MEM_Null; | |
| 76470 | + if( sqlite3VdbeMemGrow(pDest, len+2, 0) ) goto no_mem; | |
| 76471 | + }else{ | |
| 76472 | + pDest->z = pDest->zMalloc; | |
| 76473 | + } | |
| 76474 | + memcpy(pDest->z, zData, len); | |
| 76475 | + pDest->z[len] = 0; | |
| 76476 | + pDest->z[len+1] = 0; | |
| 76477 | + pDest->flags = aFlag[t&1]; | |
| 76478 | + } | |
| 76246 | 76479 | }else{ |
| 76247 | 76480 | /* This branch happens only when content is on overflow pages */ |
| 76248 | 76481 | if( ((pOp->p5 & (OPFLAG_LENGTHARG|OPFLAG_TYPEOFARG))!=0 |
| 76249 | 76482 | && ((t>=12 && (t&1)==0) || (pOp->p5 & OPFLAG_TYPEOFARG)!=0)) |
| 76250 | 76483 | || (len = sqlite3VdbeSerialTypeLen(t))==0 |
| @@ -76252,42 +76485,24 @@ | ||
| 76252 | 76485 | /* Content is irrelevant for |
| 76253 | 76486 | ** 1. the typeof() function, |
| 76254 | 76487 | ** 2. the length(X) function if X is a blob, and |
| 76255 | 76488 | ** 3. if the content length is zero. |
| 76256 | 76489 | ** So we might as well use bogus content rather than reading |
| 76257 | - ** content from disk. NULL will work for the value for strings | |
| 76258 | - ** and blobs and whatever is in the payloadSize64 variable | |
| 76259 | - ** will work for everything else. */ | |
| 76260 | - sqlite3VdbeSerialGet(t<=13 ? (u8*)&payloadSize64 : 0, t, pDest); | |
| 76490 | + ** content from disk. */ | |
| 76491 | + static u8 aZero[8]; /* This is the bogus content */ | |
| 76492 | + sqlite3VdbeSerialGet(aZero, t, pDest); | |
| 76261 | 76493 | }else{ |
| 76262 | 76494 | rc = sqlite3VdbeMemFromBtree(pCrsr, aOffset[p2], len, !pC->isTable, |
| 76263 | 76495 | pDest); |
| 76264 | - if( rc!=SQLITE_OK ){ | |
| 76265 | - goto op_column_error; | |
| 76266 | - } | |
| 76267 | - sqlite3VdbeSerialGet((const u8*)pDest->z, t, pDest); | |
| 76268 | - pDest->flags &= ~MEM_Ephem; | |
| 76269 | - } | |
| 76270 | - } | |
| 76271 | - pDest->enc = encoding; | |
| 76272 | - | |
| 76273 | -op_column_out: | |
| 76274 | - /* If the column value is an ephemeral string, go ahead and persist | |
| 76275 | - ** that string in case the cursor moves before the column value is | |
| 76276 | - ** used. The following code does the equivalent of Deephemeralize() | |
| 76277 | - ** but does it faster. */ | |
| 76278 | - if( (pDest->flags & MEM_Ephem)!=0 && pDest->z ){ | |
| 76279 | - fx = pDest->flags & (MEM_Str|MEM_Blob); | |
| 76280 | - assert( fx!=0 ); | |
| 76281 | - zData = (const u8*)pDest->z; | |
| 76282 | - len = pDest->n; | |
| 76283 | - if( sqlite3VdbeMemClearAndResize(pDest, len+2) ) goto no_mem; | |
| 76284 | - memcpy(pDest->z, zData, len); | |
| 76285 | - pDest->z[len] = 0; | |
| 76286 | - pDest->z[len+1] = 0; | |
| 76287 | - pDest->flags = fx|MEM_Term; | |
| 76288 | - } | |
| 76496 | + if( rc==SQLITE_OK ){ | |
| 76497 | + sqlite3VdbeSerialGet((const u8*)pDest->z, t, pDest); | |
| 76498 | + pDest->flags &= ~MEM_Ephem; | |
| 76499 | + } | |
| 76500 | + } | |
| 76501 | + } | |
| 76502 | + | |
| 76503 | +op_column_out: | |
| 76289 | 76504 | op_column_error: |
| 76290 | 76505 | UPDATE_MAX_BLOBSIZE(pDest); |
| 76291 | 76506 | REGISTER_TRACE(pOp->p3, pDest); |
| 76292 | 76507 | break; |
| 76293 | 76508 | } |
| @@ -81685,11 +81900,11 @@ | ||
| 81685 | 81900 | assert( pReadr->aBuffer==0 ); |
| 81686 | 81901 | assert( pReadr->aMap==0 ); |
| 81687 | 81902 | |
| 81688 | 81903 | rc = vdbePmaReaderSeek(pTask, pReadr, pFile, iStart); |
| 81689 | 81904 | if( rc==SQLITE_OK ){ |
| 81690 | - u64 nByte; /* Size of PMA in bytes */ | |
| 81905 | + u64 nByte = 0; /* Size of PMA in bytes */ | |
| 81691 | 81906 | rc = vdbePmaReadVarint(pReadr, &nByte); |
| 81692 | 81907 | pReadr->iEof = pReadr->iReadOff + nByte; |
| 81693 | 81908 | *pnByte += nByte; |
| 81694 | 81909 | } |
| 81695 | 81910 | |
| @@ -84243,13 +84458,12 @@ | ||
| 84243 | 84458 | ** return the top-level walk call. |
| 84244 | 84459 | ** |
| 84245 | 84460 | ** The return value from this routine is WRC_Abort to abandon the tree walk |
| 84246 | 84461 | ** and WRC_Continue to continue. |
| 84247 | 84462 | */ |
| 84248 | -SQLITE_PRIVATE int sqlite3WalkExpr(Walker *pWalker, Expr *pExpr){ | |
| 84463 | +static SQLITE_NOINLINE int walkExpr(Walker *pWalker, Expr *pExpr){ | |
| 84249 | 84464 | int rc; |
| 84250 | - if( pExpr==0 ) return WRC_Continue; | |
| 84251 | 84465 | testcase( ExprHasProperty(pExpr, EP_TokenOnly) ); |
| 84252 | 84466 | testcase( ExprHasProperty(pExpr, EP_Reduced) ); |
| 84253 | 84467 | rc = pWalker->xExprCallback(pWalker, pExpr); |
| 84254 | 84468 | if( rc==WRC_Continue |
| 84255 | 84469 | && !ExprHasProperty(pExpr,EP_TokenOnly) ){ |
| @@ -84260,10 +84474,13 @@ | ||
| 84260 | 84474 | }else{ |
| 84261 | 84475 | if( sqlite3WalkExprList(pWalker, pExpr->x.pList) ) return WRC_Abort; |
| 84262 | 84476 | } |
| 84263 | 84477 | } |
| 84264 | 84478 | return rc & WRC_Abort; |
| 84479 | +} | |
| 84480 | +SQLITE_PRIVATE int sqlite3WalkExpr(Walker *pWalker, Expr *pExpr){ | |
| 84481 | + return pExpr ? walkExpr(pWalker,pExpr) : WRC_Continue; | |
| 84265 | 84482 | } |
| 84266 | 84483 | |
| 84267 | 84484 | /* |
| 84268 | 84485 | ** Call sqlite3WalkExpr() for every expression in list p or until |
| 84269 | 84486 | ** an abort request is seen. |
| @@ -86327,12 +86544,13 @@ | ||
| 86327 | 86544 | || sqlite3GetInt32(pToken->z, &iValue)==0 ){ |
| 86328 | 86545 | nExtra = pToken->n+1; |
| 86329 | 86546 | assert( iValue>=0 ); |
| 86330 | 86547 | } |
| 86331 | 86548 | } |
| 86332 | - pNew = sqlite3DbMallocZero(db, sizeof(Expr)+nExtra); | |
| 86549 | + pNew = sqlite3DbMallocRaw(db, sizeof(Expr)+nExtra); | |
| 86333 | 86550 | if( pNew ){ |
| 86551 | + memset(pNew, 0, sizeof(Expr)); | |
| 86334 | 86552 | pNew->op = (u8)op; |
| 86335 | 86553 | pNew->iAgg = -1; |
| 86336 | 86554 | if( pToken ){ |
| 86337 | 86555 | if( nExtra==0 ){ |
| 86338 | 86556 | pNew->flags |= EP_IntValue; |
| @@ -93723,19 +93941,10 @@ | ||
| 93723 | 93941 | ** COMMIT |
| 93724 | 93942 | ** ROLLBACK |
| 93725 | 93943 | */ |
| 93726 | 93944 | /* #include "sqliteInt.h" */ |
| 93727 | 93945 | |
| 93728 | -/* | |
| 93729 | -** This routine is called when a new SQL statement is beginning to | |
| 93730 | -** be parsed. Initialize the pParse structure as needed. | |
| 93731 | -*/ | |
| 93732 | -SQLITE_PRIVATE void sqlite3BeginParse(Parse *pParse, int explainFlag){ | |
| 93733 | - pParse->explain = (u8)explainFlag; | |
| 93734 | - pParse->nVar = 0; | |
| 93735 | -} | |
| 93736 | - | |
| 93737 | 93946 | #ifndef SQLITE_OMIT_SHARED_CACHE |
| 93738 | 93947 | /* |
| 93739 | 93948 | ** The TableLock structure is only used by the sqlite3TableLock() and |
| 93740 | 93949 | ** codeTableLocks() functions. |
| 93741 | 93950 | */ |
| @@ -100072,14 +100281,14 @@ | ||
| 100072 | 100281 | |
| 100073 | 100282 | /* |
| 100074 | 100283 | ** A structure defining how to do GLOB-style comparisons. |
| 100075 | 100284 | */ |
| 100076 | 100285 | struct compareInfo { |
| 100077 | - u8 matchAll; | |
| 100078 | - u8 matchOne; | |
| 100079 | - u8 matchSet; | |
| 100080 | - u8 noCase; | |
| 100286 | + u8 matchAll; /* "*" or "%" */ | |
| 100287 | + u8 matchOne; /* "?" or "_" */ | |
| 100288 | + u8 matchSet; /* "[" or 0 */ | |
| 100289 | + u8 noCase; /* true to ignore case differences */ | |
| 100081 | 100290 | }; |
| 100082 | 100291 | |
| 100083 | 100292 | /* |
| 100084 | 100293 | ** For LIKE and GLOB matching on EBCDIC machines, assume that every |
| 100085 | 100294 | ** character is exactly one byte in size. Also, provde the Utf8Read() |
| @@ -100138,26 +100347,18 @@ | ||
| 100138 | 100347 | */ |
| 100139 | 100348 | static int patternCompare( |
| 100140 | 100349 | const u8 *zPattern, /* The glob pattern */ |
| 100141 | 100350 | const u8 *zString, /* The string to compare against the glob */ |
| 100142 | 100351 | const struct compareInfo *pInfo, /* Information about how to do the compare */ |
| 100143 | - u32 esc /* The escape character */ | |
| 100352 | + u32 matchOther /* The escape char (LIKE) or '[' (GLOB) */ | |
| 100144 | 100353 | ){ |
| 100145 | 100354 | u32 c, c2; /* Next pattern and input string chars */ |
| 100146 | 100355 | u32 matchOne = pInfo->matchOne; /* "?" or "_" */ |
| 100147 | 100356 | u32 matchAll = pInfo->matchAll; /* "*" or "%" */ |
| 100148 | - u32 matchOther; /* "[" or the escape character */ | |
| 100149 | 100357 | u8 noCase = pInfo->noCase; /* True if uppercase==lowercase */ |
| 100150 | 100358 | const u8 *zEscaped = 0; /* One past the last escaped input char */ |
| 100151 | 100359 | |
| 100152 | - /* The GLOB operator does not have an ESCAPE clause. And LIKE does not | |
| 100153 | - ** have the matchSet operator. So we either have to look for one or | |
| 100154 | - ** the other, never both. Hence the single variable matchOther is used | |
| 100155 | - ** to store the one we have to look for. | |
| 100156 | - */ | |
| 100157 | - matchOther = esc ? esc : pInfo->matchSet; | |
| 100158 | - | |
| 100159 | 100360 | while( (c = Utf8Read(zPattern))!=0 ){ |
| 100160 | 100361 | if( c==matchAll ){ /* Match "*" */ |
| 100161 | 100362 | /* Skip over multiple "*" characters in the pattern. If there |
| 100162 | 100363 | ** are also "?" characters, skip those as well, but consume a |
| 100163 | 100364 | ** single character of the input string for each "?" skipped */ |
| @@ -100167,19 +100368,19 @@ | ||
| 100167 | 100368 | } |
| 100168 | 100369 | } |
| 100169 | 100370 | if( c==0 ){ |
| 100170 | 100371 | return 1; /* "*" at the end of the pattern matches */ |
| 100171 | 100372 | }else if( c==matchOther ){ |
| 100172 | - if( esc ){ | |
| 100373 | + if( pInfo->matchSet==0 ){ | |
| 100173 | 100374 | c = sqlite3Utf8Read(&zPattern); |
| 100174 | 100375 | if( c==0 ) return 0; |
| 100175 | 100376 | }else{ |
| 100176 | 100377 | /* "[...]" immediately follows the "*". We have to do a slow |
| 100177 | 100378 | ** recursive search in this case, but it is an unusual case. */ |
| 100178 | 100379 | assert( matchOther<0x80 ); /* '[' is a single-byte character */ |
| 100179 | 100380 | while( *zString |
| 100180 | - && patternCompare(&zPattern[-1],zString,pInfo,esc)==0 ){ | |
| 100381 | + && patternCompare(&zPattern[-1],zString,pInfo,matchOther)==0 ){ | |
| 100181 | 100382 | SQLITE_SKIP_UTF8(zString); |
| 100182 | 100383 | } |
| 100183 | 100384 | return *zString!=0; |
| 100184 | 100385 | } |
| 100185 | 100386 | } |
| @@ -100201,22 +100402,22 @@ | ||
| 100201 | 100402 | }else{ |
| 100202 | 100403 | cx = c; |
| 100203 | 100404 | } |
| 100204 | 100405 | while( (c2 = *(zString++))!=0 ){ |
| 100205 | 100406 | if( c2!=c && c2!=cx ) continue; |
| 100206 | - if( patternCompare(zPattern,zString,pInfo,esc) ) return 1; | |
| 100407 | + if( patternCompare(zPattern,zString,pInfo,matchOther) ) return 1; | |
| 100207 | 100408 | } |
| 100208 | 100409 | }else{ |
| 100209 | 100410 | while( (c2 = Utf8Read(zString))!=0 ){ |
| 100210 | 100411 | if( c2!=c ) continue; |
| 100211 | - if( patternCompare(zPattern,zString,pInfo,esc) ) return 1; | |
| 100412 | + if( patternCompare(zPattern,zString,pInfo,matchOther) ) return 1; | |
| 100212 | 100413 | } |
| 100213 | 100414 | } |
| 100214 | 100415 | return 0; |
| 100215 | 100416 | } |
| 100216 | 100417 | if( c==matchOther ){ |
| 100217 | - if( esc ){ | |
| 100418 | + if( pInfo->matchSet==0 ){ | |
| 100218 | 100419 | c = sqlite3Utf8Read(&zPattern); |
| 100219 | 100420 | if( c==0 ) return 0; |
| 100220 | 100421 | zEscaped = zPattern; |
| 100221 | 100422 | }else{ |
| 100222 | 100423 | u32 prior_c = 0; |
| @@ -100265,11 +100466,11 @@ | ||
| 100265 | 100466 | |
| 100266 | 100467 | /* |
| 100267 | 100468 | ** The sqlite3_strglob() interface. |
| 100268 | 100469 | */ |
| 100269 | 100470 | SQLITE_API int SQLITE_STDCALL sqlite3_strglob(const char *zGlobPattern, const char *zString){ |
| 100270 | - return patternCompare((u8*)zGlobPattern, (u8*)zString, &globInfo, 0)==0; | |
| 100471 | + return patternCompare((u8*)zGlobPattern, (u8*)zString, &globInfo, '[')==0; | |
| 100271 | 100472 | } |
| 100272 | 100473 | |
| 100273 | 100474 | /* |
| 100274 | 100475 | ** The sqlite3_strlike() interface. |
| 100275 | 100476 | */ |
| @@ -100303,13 +100504,14 @@ | ||
| 100303 | 100504 | sqlite3_context *context, |
| 100304 | 100505 | int argc, |
| 100305 | 100506 | sqlite3_value **argv |
| 100306 | 100507 | ){ |
| 100307 | 100508 | const unsigned char *zA, *zB; |
| 100308 | - u32 escape = 0; | |
| 100509 | + u32 escape; | |
| 100309 | 100510 | int nPat; |
| 100310 | 100511 | sqlite3 *db = sqlite3_context_db_handle(context); |
| 100512 | + struct compareInfo *pInfo = sqlite3_user_data(context); | |
| 100311 | 100513 | |
| 100312 | 100514 | #ifdef SQLITE_LIKE_DOESNT_MATCH_BLOBS |
| 100313 | 100515 | if( sqlite3_value_type(argv[0])==SQLITE_BLOB |
| 100314 | 100516 | || sqlite3_value_type(argv[1])==SQLITE_BLOB |
| 100315 | 100517 | ){ |
| @@ -100345,17 +100547,17 @@ | ||
| 100345 | 100547 | sqlite3_result_error(context, |
| 100346 | 100548 | "ESCAPE expression must be a single character", -1); |
| 100347 | 100549 | return; |
| 100348 | 100550 | } |
| 100349 | 100551 | escape = sqlite3Utf8Read(&zEsc); |
| 100552 | + }else{ | |
| 100553 | + escape = pInfo->matchSet; | |
| 100350 | 100554 | } |
| 100351 | 100555 | if( zA && zB ){ |
| 100352 | - struct compareInfo *pInfo = sqlite3_user_data(context); | |
| 100353 | 100556 | #ifdef SQLITE_TEST |
| 100354 | 100557 | sqlite3_like_count++; |
| 100355 | 100558 | #endif |
| 100356 | - | |
| 100357 | 100559 | sqlite3_result_int(context, patternCompare(zB, zA, pInfo, escape)); |
| 100358 | 100560 | } |
| 100359 | 100561 | } |
| 100360 | 100562 | |
| 100361 | 100563 | /* |
| @@ -109688,10 +109890,11 @@ | ||
| 109688 | 109890 | int nOBSat; /* Number of ORDER BY terms satisfied by indices */ |
| 109689 | 109891 | int iECursor; /* Cursor number for the sorter */ |
| 109690 | 109892 | int regReturn; /* Register holding block-output return address */ |
| 109691 | 109893 | int labelBkOut; /* Start label for the block-output subroutine */ |
| 109692 | 109894 | int addrSortIndex; /* Address of the OP_SorterOpen or OP_OpenEphemeral */ |
| 109895 | + int labelDone; /* Jump here when done, ex: LIMIT reached */ | |
| 109693 | 109896 | u8 sortFlags; /* Zero or more SORTFLAG_* bits */ |
| 109694 | 109897 | }; |
| 109695 | 109898 | #define SORTFLAG_UseSorter 0x01 /* Use SorterOpen instead of OpenEphemeral */ |
| 109696 | 109899 | |
| 109697 | 109900 | /* |
| @@ -109745,33 +109948,41 @@ | ||
| 109745 | 109948 | Expr *pOffset /* OFFSET value. NULL means no offset */ |
| 109746 | 109949 | ){ |
| 109747 | 109950 | Select *pNew; |
| 109748 | 109951 | Select standin; |
| 109749 | 109952 | sqlite3 *db = pParse->db; |
| 109750 | - pNew = sqlite3DbMallocZero(db, sizeof(*pNew) ); | |
| 109953 | + pNew = sqlite3DbMallocRaw(db, sizeof(*pNew) ); | |
| 109751 | 109954 | if( pNew==0 ){ |
| 109752 | 109955 | assert( db->mallocFailed ); |
| 109753 | 109956 | pNew = &standin; |
| 109754 | - memset(pNew, 0, sizeof(*pNew)); | |
| 109755 | 109957 | } |
| 109756 | 109958 | if( pEList==0 ){ |
| 109757 | 109959 | pEList = sqlite3ExprListAppend(pParse, 0, sqlite3Expr(db,TK_ASTERISK,0)); |
| 109758 | 109960 | } |
| 109759 | 109961 | pNew->pEList = pEList; |
| 109962 | + pNew->op = TK_SELECT; | |
| 109963 | + pNew->selFlags = selFlags; | |
| 109964 | + pNew->iLimit = 0; | |
| 109965 | + pNew->iOffset = 0; | |
| 109966 | +#if SELECTTRACE_ENABLED | |
| 109967 | + pNew->zSelName[0] = 0; | |
| 109968 | +#endif | |
| 109969 | + pNew->addrOpenEphm[0] = -1; | |
| 109970 | + pNew->addrOpenEphm[1] = -1; | |
| 109971 | + pNew->nSelectRow = 0; | |
| 109760 | 109972 | if( pSrc==0 ) pSrc = sqlite3DbMallocZero(db, sizeof(*pSrc)); |
| 109761 | 109973 | pNew->pSrc = pSrc; |
| 109762 | 109974 | pNew->pWhere = pWhere; |
| 109763 | 109975 | pNew->pGroupBy = pGroupBy; |
| 109764 | 109976 | pNew->pHaving = pHaving; |
| 109765 | 109977 | pNew->pOrderBy = pOrderBy; |
| 109766 | - pNew->selFlags = selFlags; | |
| 109767 | - pNew->op = TK_SELECT; | |
| 109978 | + pNew->pPrior = 0; | |
| 109979 | + pNew->pNext = 0; | |
| 109768 | 109980 | pNew->pLimit = pLimit; |
| 109769 | 109981 | pNew->pOffset = pOffset; |
| 109982 | + pNew->pWith = 0; | |
| 109770 | 109983 | assert( pOffset==0 || pLimit!=0 || pParse->nErr>0 || db->mallocFailed!=0 ); |
| 109771 | - pNew->addrOpenEphm[0] = -1; | |
| 109772 | - pNew->addrOpenEphm[1] = -1; | |
| 109773 | 109984 | if( db->mallocFailed ) { |
| 109774 | 109985 | clearSelect(db, pNew, pNew!=&standin); |
| 109775 | 109986 | pNew = 0; |
| 109776 | 109987 | }else{ |
| 109777 | 109988 | assert( pNew->pSrc!=0 || pParse->nErr>0 ); |
| @@ -110142,10 +110353,11 @@ | ||
| 110142 | 110353 | int nBase = nExpr + bSeq + nData; /* Fields in sorter record */ |
| 110143 | 110354 | int regBase; /* Regs for sorter record */ |
| 110144 | 110355 | int regRecord = ++pParse->nMem; /* Assembled sorter record */ |
| 110145 | 110356 | int nOBSat = pSort->nOBSat; /* ORDER BY terms to skip */ |
| 110146 | 110357 | int op; /* Opcode to add sorter record to sorter */ |
| 110358 | + int iLimit; /* LIMIT counter */ | |
| 110147 | 110359 | |
| 110148 | 110360 | assert( bSeq==0 || bSeq==1 ); |
| 110149 | 110361 | assert( nData==1 || regData==regOrigData ); |
| 110150 | 110362 | if( nPrefixReg ){ |
| 110151 | 110363 | assert( nPrefixReg==nExpr+bSeq ); |
| @@ -110152,19 +110364,21 @@ | ||
| 110152 | 110364 | regBase = regData - nExpr - bSeq; |
| 110153 | 110365 | }else{ |
| 110154 | 110366 | regBase = pParse->nMem + 1; |
| 110155 | 110367 | pParse->nMem += nBase; |
| 110156 | 110368 | } |
| 110369 | + assert( pSelect->iOffset==0 || pSelect->iLimit!=0 ); | |
| 110370 | + iLimit = pSelect->iOffset ? pSelect->iOffset+1 : pSelect->iLimit; | |
| 110371 | + pSort->labelDone = sqlite3VdbeMakeLabel(v); | |
| 110157 | 110372 | sqlite3ExprCodeExprList(pParse, pSort->pOrderBy, regBase, regOrigData, |
| 110158 | 110373 | SQLITE_ECEL_DUP|SQLITE_ECEL_REF); |
| 110159 | 110374 | if( bSeq ){ |
| 110160 | 110375 | sqlite3VdbeAddOp2(v, OP_Sequence, pSort->iECursor, regBase+nExpr); |
| 110161 | 110376 | } |
| 110162 | 110377 | if( nPrefixReg==0 ){ |
| 110163 | 110378 | sqlite3ExprCodeMove(pParse, regData, regBase+nExpr+bSeq, nData); |
| 110164 | 110379 | } |
| 110165 | - | |
| 110166 | 110380 | sqlite3VdbeAddOp3(v, OP_MakeRecord, regBase+nOBSat, nBase-nOBSat, regRecord); |
| 110167 | 110381 | if( nOBSat>0 ){ |
| 110168 | 110382 | int regPrevKey; /* The first nOBSat columns of the previous row */ |
| 110169 | 110383 | int addrFirst; /* Address of the OP_IfNot opcode */ |
| 110170 | 110384 | int addrJmp; /* Address of the OP_Jump opcode */ |
| @@ -110195,10 +110409,14 @@ | ||
| 110195 | 110409 | sqlite3VdbeAddOp3(v, OP_Jump, addrJmp+1, 0, addrJmp+1); VdbeCoverage(v); |
| 110196 | 110410 | pSort->labelBkOut = sqlite3VdbeMakeLabel(v); |
| 110197 | 110411 | pSort->regReturn = ++pParse->nMem; |
| 110198 | 110412 | sqlite3VdbeAddOp2(v, OP_Gosub, pSort->regReturn, pSort->labelBkOut); |
| 110199 | 110413 | sqlite3VdbeAddOp1(v, OP_ResetSorter, pSort->iECursor); |
| 110414 | + if( iLimit ){ | |
| 110415 | + sqlite3VdbeAddOp2(v, OP_IfNot, iLimit, pSort->labelDone); | |
| 110416 | + VdbeCoverage(v); | |
| 110417 | + } | |
| 110200 | 110418 | sqlite3VdbeJumpHere(v, addrFirst); |
| 110201 | 110419 | sqlite3ExprCodeMove(pParse, regBase, regPrevKey, pSort->nOBSat); |
| 110202 | 110420 | sqlite3VdbeJumpHere(v, addrJmp); |
| 110203 | 110421 | } |
| 110204 | 110422 | if( pSort->sortFlags & SORTFLAG_UseSorter ){ |
| @@ -110205,18 +110423,12 @@ | ||
| 110205 | 110423 | op = OP_SorterInsert; |
| 110206 | 110424 | }else{ |
| 110207 | 110425 | op = OP_IdxInsert; |
| 110208 | 110426 | } |
| 110209 | 110427 | sqlite3VdbeAddOp2(v, op, pSort->iECursor, regRecord); |
| 110210 | - if( pSelect->iLimit ){ | |
| 110428 | + if( iLimit ){ | |
| 110211 | 110429 | int addr; |
| 110212 | - int iLimit; | |
| 110213 | - if( pSelect->iOffset ){ | |
| 110214 | - iLimit = pSelect->iOffset+1; | |
| 110215 | - }else{ | |
| 110216 | - iLimit = pSelect->iLimit; | |
| 110217 | - } | |
| 110218 | 110430 | addr = sqlite3VdbeAddOp3(v, OP_IfNotZero, iLimit, 0, 1); VdbeCoverage(v); |
| 110219 | 110431 | sqlite3VdbeAddOp1(v, OP_Last, pSort->iECursor); |
| 110220 | 110432 | sqlite3VdbeAddOp1(v, OP_Delete, pSort->iECursor); |
| 110221 | 110433 | sqlite3VdbeJumpHere(v, addr); |
| 110222 | 110434 | } |
| @@ -110816,11 +111028,11 @@ | ||
| 110816 | 111028 | SortCtx *pSort, /* Information on the ORDER BY clause */ |
| 110817 | 111029 | int nColumn, /* Number of columns of data */ |
| 110818 | 111030 | SelectDest *pDest /* Write the sorted results here */ |
| 110819 | 111031 | ){ |
| 110820 | 111032 | Vdbe *v = pParse->pVdbe; /* The prepared statement */ |
| 110821 | - int addrBreak = sqlite3VdbeMakeLabel(v); /* Jump here to exit loop */ | |
| 111033 | + int addrBreak = pSort->labelDone; /* Jump here to exit loop */ | |
| 110822 | 111034 | int addrContinue = sqlite3VdbeMakeLabel(v); /* Jump here for next cycle */ |
| 110823 | 111035 | int addr; |
| 110824 | 111036 | int addrOnce = 0; |
| 110825 | 111037 | int iTab; |
| 110826 | 111038 | ExprList *pOrderBy = pSort->pOrderBy; |
| @@ -110835,10 +111047,11 @@ | ||
| 110835 | 111047 | int bSeq; /* True if sorter record includes seq. no. */ |
| 110836 | 111048 | #ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS |
| 110837 | 111049 | struct ExprList_item *aOutEx = p->pEList->a; |
| 110838 | 111050 | #endif |
| 110839 | 111051 | |
| 111052 | + assert( addrBreak<0 ); | |
| 110840 | 111053 | if( pSort->labelBkOut ){ |
| 110841 | 111054 | sqlite3VdbeAddOp2(v, OP_Gosub, pSort->regReturn, pSort->labelBkOut); |
| 110842 | 111055 | sqlite3VdbeGoto(v, addrBreak); |
| 110843 | 111056 | sqlite3VdbeResolveLabel(v, pSort->labelBkOut); |
| 110844 | 111057 | } |
| @@ -126875,12 +127088,11 @@ | ||
| 126875 | 127088 | testcase( pWInfo->eOnePass==ONEPASS_OFF && pTab->nCol==BMS ); |
| 126876 | 127089 | if( pWInfo->eOnePass==ONEPASS_OFF && pTab->nCol<BMS && HasRowid(pTab) ){ |
| 126877 | 127090 | Bitmask b = pTabItem->colUsed; |
| 126878 | 127091 | int n = 0; |
| 126879 | 127092 | for(; b; b=b>>1, n++){} |
| 126880 | - sqlite3VdbeChangeP4(v, sqlite3VdbeCurrentAddr(v)-1, | |
| 126881 | - SQLITE_INT_TO_PTR(n), P4_INT32); | |
| 127093 | + sqlite3VdbeChangeP4(v, -1, SQLITE_INT_TO_PTR(n), P4_INT32); | |
| 126882 | 127094 | assert( n<=pTab->nCol ); |
| 126883 | 127095 | } |
| 126884 | 127096 | #ifdef SQLITE_ENABLE_CURSOR_HINTS |
| 126885 | 127097 | if( pLoop->u.btree.pIndex!=0 ){ |
| 126886 | 127098 | sqlite3VdbeChangeP5(v, OPFLAG_SEEKEQ|bFordelete); |
| @@ -129343,18 +129555,15 @@ | ||
| 129343 | 129555 | ** { ... } // User supplied code |
| 129344 | 129556 | ** #line <lineno> <thisfile> |
| 129345 | 129557 | ** break; |
| 129346 | 129558 | */ |
| 129347 | 129559 | /********** Begin reduce actions **********************************************/ |
| 129348 | - case 5: /* explain ::= */ | |
| 129349 | -{ sqlite3BeginParse(pParse, 0); } | |
| 129350 | - break; | |
| 129351 | 129560 | case 6: /* explain ::= EXPLAIN */ |
| 129352 | -{ sqlite3BeginParse(pParse, 1); } | |
| 129561 | +{ pParse->explain = 1; } | |
| 129353 | 129562 | break; |
| 129354 | 129563 | case 7: /* explain ::= EXPLAIN QUERY PLAN */ |
| 129355 | -{ sqlite3BeginParse(pParse, 2); } | |
| 129564 | +{ pParse->explain = 2; } | |
| 129356 | 129565 | break; |
| 129357 | 129566 | case 8: /* cmdx ::= cmd */ |
| 129358 | 129567 | { sqlite3FinishCoding(pParse); } |
| 129359 | 129568 | break; |
| 129360 | 129569 | case 9: /* cmd ::= BEGIN transtype trans_opt */ |
| @@ -130525,10 +130734,11 @@ | ||
| 130525 | 130734 | /* (0) input ::= cmdlist */ yytestcase(yyruleno==0); |
| 130526 | 130735 | /* (1) cmdlist ::= cmdlist ecmd */ yytestcase(yyruleno==1); |
| 130527 | 130736 | /* (2) cmdlist ::= ecmd */ yytestcase(yyruleno==2); |
| 130528 | 130737 | /* (3) ecmd ::= SEMI */ yytestcase(yyruleno==3); |
| 130529 | 130738 | /* (4) ecmd ::= explain cmdx SEMI */ yytestcase(yyruleno==4); |
| 130739 | + /* (5) explain ::= */ yytestcase(yyruleno==5); | |
| 130530 | 130740 | /* (10) trans_opt ::= */ yytestcase(yyruleno==10); |
| 130531 | 130741 | /* (11) trans_opt ::= TRANSACTION */ yytestcase(yyruleno==11); |
| 130532 | 130742 | /* (12) trans_opt ::= TRANSACTION nm */ yytestcase(yyruleno==12); |
| 130533 | 130743 | /* (20) savepoint_opt ::= SAVEPOINT */ yytestcase(yyruleno==20); |
| 130534 | 130744 | /* (21) savepoint_opt ::= */ yytestcase(yyruleno==21); |
| @@ -135450,10 +135660,13 @@ | ||
| 135450 | 135660 | *(sqlite3_file**)pArg = fd; |
| 135451 | 135661 | rc = SQLITE_OK; |
| 135452 | 135662 | }else if( op==SQLITE_FCNTL_VFS_POINTER ){ |
| 135453 | 135663 | *(sqlite3_vfs**)pArg = sqlite3PagerVfs(pPager); |
| 135454 | 135664 | rc = SQLITE_OK; |
| 135665 | + }else if( op==SQLITE_FCNTL_JOURNAL_POINTER ){ | |
| 135666 | + *(sqlite3_file**)pArg = sqlite3PagerJrnlFile(pPager); | |
| 135667 | + rc = SQLITE_OK; | |
| 135455 | 135668 | }else if( fd->pMethods ){ |
| 135456 | 135669 | rc = sqlite3OsFileControl(fd, op, pArg); |
| 135457 | 135670 | }else{ |
| 135458 | 135671 | rc = SQLITE_NOTFOUND; |
| 135459 | 135672 | } |
| @@ -161083,11 +161296,11 @@ | ||
| 161083 | 161296 | */ |
| 161084 | 161297 | static void *rbuMalloc(sqlite3rbu *p, int nByte){ |
| 161085 | 161298 | void *pRet = 0; |
| 161086 | 161299 | if( p->rc==SQLITE_OK ){ |
| 161087 | 161300 | assert( nByte>0 ); |
| 161088 | - pRet = sqlite3_malloc(nByte); | |
| 161301 | + pRet = sqlite3_malloc64(nByte); | |
| 161089 | 161302 | if( pRet==0 ){ |
| 161090 | 161303 | p->rc = SQLITE_NOMEM; |
| 161091 | 161304 | }else{ |
| 161092 | 161305 | memset(pRet, 0, nByte); |
| 161093 | 161306 | } |
| @@ -161129,12 +161342,12 @@ | ||
| 161129 | 161342 | static char *rbuStrndup(const char *zStr, int *pRc){ |
| 161130 | 161343 | char *zRet = 0; |
| 161131 | 161344 | |
| 161132 | 161345 | assert( *pRc==SQLITE_OK ); |
| 161133 | 161346 | if( zStr ){ |
| 161134 | - int nCopy = strlen(zStr) + 1; | |
| 161135 | - zRet = (char*)sqlite3_malloc(nCopy); | |
| 161347 | + size_t nCopy = strlen(zStr) + 1; | |
| 161348 | + zRet = (char*)sqlite3_malloc64(nCopy); | |
| 161136 | 161349 | if( zRet ){ |
| 161137 | 161350 | memcpy(zRet, zStr, nCopy); |
| 161138 | 161351 | }else{ |
| 161139 | 161352 | *pRc = SQLITE_NOMEM; |
| 161140 | 161353 | } |
| @@ -162478,11 +162691,11 @@ | ||
| 162478 | 162691 | |
| 162479 | 162692 | pRbu->pgsz = iAmt; |
| 162480 | 162693 | if( pRbu->nFrame==pRbu->nFrameAlloc ){ |
| 162481 | 162694 | int nNew = (pRbu->nFrameAlloc ? pRbu->nFrameAlloc : 64) * 2; |
| 162482 | 162695 | RbuFrame *aNew; |
| 162483 | - aNew = (RbuFrame*)sqlite3_realloc(pRbu->aFrame, nNew * sizeof(RbuFrame)); | |
| 162696 | + aNew = (RbuFrame*)sqlite3_realloc64(pRbu->aFrame, nNew * sizeof(RbuFrame)); | |
| 162484 | 162697 | if( aNew==0 ) return SQLITE_NOMEM; |
| 162485 | 162698 | pRbu->aFrame = aNew; |
| 162486 | 162699 | pRbu->nFrameAlloc = nNew; |
| 162487 | 162700 | } |
| 162488 | 162701 | |
| @@ -162543,11 +162756,11 @@ | ||
| 162543 | 162756 | |
| 162544 | 162757 | nChar = MultiByteToWideChar(CP_UTF8, 0, zFilename, -1, NULL, 0); |
| 162545 | 162758 | if( nChar==0 ){ |
| 162546 | 162759 | return 0; |
| 162547 | 162760 | } |
| 162548 | - zWideFilename = sqlite3_malloc( nChar*sizeof(zWideFilename[0]) ); | |
| 162761 | + zWideFilename = sqlite3_malloc64( nChar*sizeof(zWideFilename[0]) ); | |
| 162549 | 162762 | if( zWideFilename==0 ){ |
| 162550 | 162763 | return 0; |
| 162551 | 162764 | } |
| 162552 | 162765 | memset(zWideFilename, 0, nChar*sizeof(zWideFilename[0])); |
| 162553 | 162766 | nChar = MultiByteToWideChar(CP_UTF8, 0, zFilename, -1, zWideFilename, |
| @@ -163177,15 +163390,16 @@ | ||
| 163177 | 163390 | const char *zTarget, |
| 163178 | 163391 | const char *zRbu, |
| 163179 | 163392 | const char *zState |
| 163180 | 163393 | ){ |
| 163181 | 163394 | sqlite3rbu *p; |
| 163182 | - int nTarget = strlen(zTarget); | |
| 163183 | - int nRbu = strlen(zRbu); | |
| 163184 | - int nState = zState ? strlen(zState) : 0; | |
| 163395 | + size_t nTarget = strlen(zTarget); | |
| 163396 | + size_t nRbu = strlen(zRbu); | |
| 163397 | + size_t nState = zState ? strlen(zState) : 0; | |
| 163398 | + size_t nByte = sizeof(sqlite3rbu) + nTarget+1 + nRbu+1+ nState+1; | |
| 163185 | 163399 | |
| 163186 | - p = (sqlite3rbu*)sqlite3_malloc(sizeof(sqlite3rbu)+nTarget+1+nRbu+1+nState+1); | |
| 163400 | + p = (sqlite3rbu*)sqlite3_malloc64(nByte); | |
| 163187 | 163401 | if( p ){ |
| 163188 | 163402 | RbuState *pState = 0; |
| 163189 | 163403 | |
| 163190 | 163404 | /* Create the custom VFS. */ |
| 163191 | 163405 | memset(p, 0, sizeof(sqlite3rbu)); |
| @@ -163318,11 +163532,11 @@ | ||
| 163318 | 163532 | ** the pattern "rbu_imp_[0-9]*". |
| 163319 | 163533 | */ |
| 163320 | 163534 | static void rbuEditErrmsg(sqlite3rbu *p){ |
| 163321 | 163535 | if( p->rc==SQLITE_CONSTRAINT && p->zErrmsg ){ |
| 163322 | 163536 | int i; |
| 163323 | - int nErrmsg = strlen(p->zErrmsg); | |
| 163537 | + size_t nErrmsg = strlen(p->zErrmsg); | |
| 163324 | 163538 | for(i=0; i<(nErrmsg-8); i++){ |
| 163325 | 163539 | if( memcmp(&p->zErrmsg[i], "rbu_imp_", 8)==0 ){ |
| 163326 | 163540 | int nDel = 8; |
| 163327 | 163541 | while( p->zErrmsg[i+nDel]>='0' && p->zErrmsg[i+nDel]<='9' ) nDel++; |
| 163328 | 163542 | memmove(&p->zErrmsg[i], &p->zErrmsg[i+nDel], nErrmsg + 1 - i - nDel); |
| @@ -163782,11 +163996,11 @@ | ||
| 163782 | 163996 | ** instead of a file on disk. */ |
| 163783 | 163997 | assert( p->openFlags & (SQLITE_OPEN_MAIN_DB|SQLITE_OPEN_TEMP_DB) ); |
| 163784 | 163998 | if( eStage==RBU_STAGE_OAL || eStage==RBU_STAGE_MOVE ){ |
| 163785 | 163999 | if( iRegion<=p->nShm ){ |
| 163786 | 164000 | int nByte = (iRegion+1) * sizeof(char*); |
| 163787 | - char **apNew = (char**)sqlite3_realloc(p->apShm, nByte); | |
| 164001 | + char **apNew = (char**)sqlite3_realloc64(p->apShm, nByte); | |
| 163788 | 164002 | if( apNew==0 ){ |
| 163789 | 164003 | rc = SQLITE_NOMEM; |
| 163790 | 164004 | }else{ |
| 163791 | 164005 | memset(&apNew[p->nShm], 0, sizeof(char*) * (1 + iRegion - p->nShm)); |
| 163792 | 164006 | p->apShm = apNew; |
| @@ -163793,11 +164007,11 @@ | ||
| 163793 | 164007 | p->nShm = iRegion+1; |
| 163794 | 164008 | } |
| 163795 | 164009 | } |
| 163796 | 164010 | |
| 163797 | 164011 | if( rc==SQLITE_OK && p->apShm[iRegion]==0 ){ |
| 163798 | - char *pNew = (char*)sqlite3_malloc(szRegion); | |
| 164012 | + char *pNew = (char*)sqlite3_malloc64(szRegion); | |
| 163799 | 164013 | if( pNew==0 ){ |
| 163800 | 164014 | rc = SQLITE_NOMEM; |
| 163801 | 164015 | }else{ |
| 163802 | 164016 | memset(pNew, 0, szRegion); |
| 163803 | 164017 | p->apShm[iRegion] = pNew; |
| @@ -163903,11 +164117,11 @@ | ||
| 163903 | 164117 | /* A main database has just been opened. The following block sets |
| 163904 | 164118 | ** (pFd->zWal) to point to a buffer owned by SQLite that contains |
| 163905 | 164119 | ** the name of the *-wal file this db connection will use. SQLite |
| 163906 | 164120 | ** happens to pass a pointer to this buffer when using xAccess() |
| 163907 | 164121 | ** or xOpen() to operate on the *-wal file. */ |
| 163908 | - int n = strlen(zName); | |
| 164122 | + int n = (int)strlen(zName); | |
| 163909 | 164123 | const char *z = &zName[n]; |
| 163910 | 164124 | if( flags & SQLITE_OPEN_URI ){ |
| 163911 | 164125 | int odd = 0; |
| 163912 | 164126 | while( 1 ){ |
| 163913 | 164127 | if( z[0]==0 ){ |
| @@ -163929,12 +164143,12 @@ | ||
| 163929 | 164143 | if( pDb->pRbu && pDb->pRbu->eStage==RBU_STAGE_OAL ){ |
| 163930 | 164144 | /* This call is to open a *-wal file. Intead, open the *-oal. This |
| 163931 | 164145 | ** code ensures that the string passed to xOpen() is terminated by a |
| 163932 | 164146 | ** pair of '\0' bytes in case the VFS attempts to extract a URI |
| 163933 | 164147 | ** parameter from it. */ |
| 163934 | - int nCopy = strlen(zName); | |
| 163935 | - char *zCopy = sqlite3_malloc(nCopy+2); | |
| 164148 | + size_t nCopy = strlen(zName); | |
| 164149 | + char *zCopy = sqlite3_malloc64(nCopy+2); | |
| 163936 | 164150 | if( zCopy ){ |
| 163937 | 164151 | memcpy(zCopy, zName, nCopy); |
| 163938 | 164152 | zCopy[nCopy-3] = 'o'; |
| 163939 | 164153 | zCopy[nCopy] = '\0'; |
| 163940 | 164154 | zCopy[nCopy+1] = '\0'; |
| @@ -164159,17 +164373,17 @@ | ||
| 164159 | 164373 | 0, /* xCurrentTimeInt64 (version 2) */ |
| 164160 | 164374 | 0, 0, 0 /* Unimplemented version 3 methods */ |
| 164161 | 164375 | }; |
| 164162 | 164376 | |
| 164163 | 164377 | rbu_vfs *pNew = 0; /* Newly allocated VFS */ |
| 164164 | - int nName; | |
| 164165 | 164378 | int rc = SQLITE_OK; |
| 164379 | + size_t nName; | |
| 164380 | + size_t nByte; | |
| 164166 | 164381 | |
| 164167 | - int nByte; | |
| 164168 | 164382 | nName = strlen(zName); |
| 164169 | 164383 | nByte = sizeof(rbu_vfs) + nName + 1; |
| 164170 | - pNew = (rbu_vfs*)sqlite3_malloc(nByte); | |
| 164384 | + pNew = (rbu_vfs*)sqlite3_malloc64(nByte); | |
| 164171 | 164385 | if( pNew==0 ){ |
| 164172 | 164386 | rc = SQLITE_NOMEM; |
| 164173 | 164387 | }else{ |
| 164174 | 164388 | sqlite3_vfs *pParent; /* Parent VFS */ |
| 164175 | 164389 | memset(pNew, 0, nByte); |
| @@ -167174,10 +167388,13 @@ | ||
| 167174 | 167388 | ** If parameter iCol is greater than or equal to the number of columns |
| 167175 | 167389 | ** in the table, SQLITE_RANGE is returned. Or, if an error occurs (e.g. |
| 167176 | 167390 | ** an OOM condition or IO error), an appropriate SQLite error code is |
| 167177 | 167391 | ** returned. |
| 167178 | 167392 | ** |
| 167393 | +** This function may be quite inefficient if used with an FTS5 table | |
| 167394 | +** created with the "columnsize=0" option. | |
| 167395 | +** | |
| 167179 | 167396 | ** xColumnText: |
| 167180 | 167397 | ** This function attempts to retrieve the text of column iCol of the |
| 167181 | 167398 | ** current document. If successful, (*pz) is set to point to a buffer |
| 167182 | 167399 | ** containing the text in utf-8 encoding, (*pn) is set to the size in bytes |
| 167183 | 167400 | ** (not characters) of the buffer and SQLITE_OK is returned. Otherwise, |
| @@ -167194,18 +167411,32 @@ | ||
| 167194 | 167411 | ** xInstCount: |
| 167195 | 167412 | ** Set *pnInst to the total number of occurrences of all phrases within |
| 167196 | 167413 | ** the query within the current row. Return SQLITE_OK if successful, or |
| 167197 | 167414 | ** an error code (i.e. SQLITE_NOMEM) if an error occurs. |
| 167198 | 167415 | ** |
| 167416 | +** This API can be quite slow if used with an FTS5 table created with the | |
| 167417 | +** "detail=none" or "detail=column" option. If the FTS5 table is created | |
| 167418 | +** with either "detail=none" or "detail=column" and "content=" option | |
| 167419 | +** (i.e. if it is a contentless table), then this API always returns 0. | |
| 167420 | +** | |
| 167199 | 167421 | ** xInst: |
| 167200 | 167422 | ** Query for the details of phrase match iIdx within the current row. |
| 167201 | 167423 | ** Phrase matches are numbered starting from zero, so the iIdx argument |
| 167202 | 167424 | ** should be greater than or equal to zero and smaller than the value |
| 167203 | 167425 | ** output by xInstCount(). |
| 167426 | +** | |
| 167427 | +** Usually, output parameter *piPhrase is set to the phrase number, *piCol | |
| 167428 | +** to the column in which it occurs and *piOff the token offset of the | |
| 167429 | +** first token of the phrase. The exception is if the table was created | |
| 167430 | +** with the offsets=0 option specified. In this case *piOff is always | |
| 167431 | +** set to -1. | |
| 167204 | 167432 | ** |
| 167205 | 167433 | ** Returns SQLITE_OK if successful, or an error code (i.e. SQLITE_NOMEM) |
| 167206 | 167434 | ** if an error occurs. |
| 167435 | +** | |
| 167436 | +** This API can be quite slow if used with an FTS5 table created with the | |
| 167437 | +** "detail=none" or "detail=column" option. | |
| 167207 | 167438 | ** |
| 167208 | 167439 | ** xRowid: |
| 167209 | 167440 | ** Returns the rowid of the current row. |
| 167210 | 167441 | ** |
| 167211 | 167442 | ** xTokenize: |
| @@ -167286,25 +167517,63 @@ | ||
| 167286 | 167517 | ** through instances of phrase iPhrase, use the following code: |
| 167287 | 167518 | ** |
| 167288 | 167519 | ** Fts5PhraseIter iter; |
| 167289 | 167520 | ** int iCol, iOff; |
| 167290 | 167521 | ** for(pApi->xPhraseFirst(pFts, iPhrase, &iter, &iCol, &iOff); |
| 167291 | -** iOff>=0; | |
| 167522 | +** iCol>=0; | |
| 167292 | 167523 | ** pApi->xPhraseNext(pFts, &iter, &iCol, &iOff) |
| 167293 | 167524 | ** ){ |
| 167294 | 167525 | ** // An instance of phrase iPhrase at offset iOff of column iCol |
| 167295 | 167526 | ** } |
| 167296 | 167527 | ** |
| 167297 | 167528 | ** The Fts5PhraseIter structure is defined above. Applications should not |
| 167298 | 167529 | ** modify this structure directly - it should only be used as shown above |
| 167299 | -** with the xPhraseFirst() and xPhraseNext() API methods. | |
| 167530 | +** with the xPhraseFirst() and xPhraseNext() API methods (and by | |
| 167531 | +** xPhraseFirstColumn() and xPhraseNextColumn() as illustrated below). | |
| 167532 | +** | |
| 167533 | +** This API can be quite slow if used with an FTS5 table created with the | |
| 167534 | +** "detail=none" or "detail=column" option. If the FTS5 table is created | |
| 167535 | +** with either "detail=none" or "detail=column" and "content=" option | |
| 167536 | +** (i.e. if it is a contentless table), then this API always iterates | |
| 167537 | +** through an empty set (all calls to xPhraseFirst() set iCol to -1). | |
| 167300 | 167538 | ** |
| 167301 | 167539 | ** xPhraseNext() |
| 167302 | 167540 | ** See xPhraseFirst above. |
| 167541 | +** | |
| 167542 | +** xPhraseFirstColumn() | |
| 167543 | +** This function and xPhraseNextColumn() are similar to the xPhraseFirst() | |
| 167544 | +** and xPhraseNext() APIs described above. The difference is that instead | |
| 167545 | +** of iterating through all instances of a phrase in the current row, these | |
| 167546 | +** APIs are used to iterate through the set of columns in the current row | |
| 167547 | +** that contain one or more instances of a specified phrase. For example: | |
| 167548 | +** | |
| 167549 | +** Fts5PhraseIter iter; | |
| 167550 | +** int iCol; | |
| 167551 | +** for(pApi->xPhraseFirstColumn(pFts, iPhrase, &iter, &iCol); | |
| 167552 | +** iCol>=0; | |
| 167553 | +** pApi->xPhraseNextColumn(pFts, &iter, &iCol) | |
| 167554 | +** ){ | |
| 167555 | +** // Column iCol contains at least one instance of phrase iPhrase | |
| 167556 | +** } | |
| 167557 | +** | |
| 167558 | +** This API can be quite slow if used with an FTS5 table created with the | |
| 167559 | +** "detail=none" option. If the FTS5 table is created with either | |
| 167560 | +** "detail=none" "content=" option (i.e. if it is a contentless table), | |
| 167561 | +** then this API always iterates through an empty set (all calls to | |
| 167562 | +** xPhraseFirstColumn() set iCol to -1). | |
| 167563 | +** | |
| 167564 | +** The information accessed using this API and its companion | |
| 167565 | +** xPhraseFirstColumn() may also be obtained using xPhraseFirst/xPhraseNext | |
| 167566 | +** (or xInst/xInstCount). The chief advantage of this API is that it is | |
| 167567 | +** significantly more efficient than those alternatives when used with | |
| 167568 | +** "detail=column" tables. | |
| 167569 | +** | |
| 167570 | +** xPhraseNextColumn() | |
| 167571 | +** See xPhraseFirstColumn above. | |
| 167303 | 167572 | */ |
| 167304 | 167573 | struct Fts5ExtensionApi { |
| 167305 | - int iVersion; /* Currently always set to 1 */ | |
| 167574 | + int iVersion; /* Currently always set to 3 */ | |
| 167306 | 167575 | |
| 167307 | 167576 | void *(*xUserData)(Fts5Context*); |
| 167308 | 167577 | |
| 167309 | 167578 | int (*xColumnCount)(Fts5Context*); |
| 167310 | 167579 | int (*xRowCount)(Fts5Context*, sqlite3_int64 *pnRow); |
| @@ -167330,12 +167599,15 @@ | ||
| 167330 | 167599 | int(*)(const Fts5ExtensionApi*,Fts5Context*,void*) |
| 167331 | 167600 | ); |
| 167332 | 167601 | int (*xSetAuxdata)(Fts5Context*, void *pAux, void(*xDelete)(void*)); |
| 167333 | 167602 | void *(*xGetAuxdata)(Fts5Context*, int bClear); |
| 167334 | 167603 | |
| 167335 | - void (*xPhraseFirst)(Fts5Context*, int iPhrase, Fts5PhraseIter*, int*, int*); | |
| 167604 | + int (*xPhraseFirst)(Fts5Context*, int iPhrase, Fts5PhraseIter*, int*, int*); | |
| 167336 | 167605 | void (*xPhraseNext)(Fts5Context*, Fts5PhraseIter*, int *piCol, int *piOff); |
| 167606 | + | |
| 167607 | + int (*xPhraseFirstColumn)(Fts5Context*, int iPhrase, Fts5PhraseIter*, int*); | |
| 167608 | + void (*xPhraseNextColumn)(Fts5Context*, Fts5PhraseIter*, int *piCol); | |
| 167337 | 167609 | }; |
| 167338 | 167610 | |
| 167339 | 167611 | /* |
| 167340 | 167612 | ** CUSTOM AUXILIARY FUNCTIONS |
| 167341 | 167613 | *************************************************************************/ |
| @@ -167762,10 +168034,11 @@ | ||
| 167762 | 168034 | int *aPrefix; /* Sizes in bytes of nPrefix prefix indexes */ |
| 167763 | 168035 | int eContent; /* An FTS5_CONTENT value */ |
| 167764 | 168036 | char *zContent; /* content table */ |
| 167765 | 168037 | char *zContentRowid; /* "content_rowid=" option value */ |
| 167766 | 168038 | int bColumnsize; /* "columnsize=" option value (dflt==1) */ |
| 168039 | + int eDetail; /* FTS5_DETAIL_XXX value */ | |
| 167767 | 168040 | char *zContentExprlist; |
| 167768 | 168041 | Fts5Tokenizer *pTok; |
| 167769 | 168042 | fts5_tokenizer *pTokApi; |
| 167770 | 168043 | |
| 167771 | 168044 | /* Values loaded from the %_config table */ |
| @@ -167790,10 +168063,13 @@ | ||
| 167790 | 168063 | |
| 167791 | 168064 | #define FTS5_CONTENT_NORMAL 0 |
| 167792 | 168065 | #define FTS5_CONTENT_NONE 1 |
| 167793 | 168066 | #define FTS5_CONTENT_EXTERNAL 2 |
| 167794 | 168067 | |
| 168068 | +#define FTS5_DETAIL_FULL 0 | |
| 168069 | +#define FTS5_DETAIL_NONE 1 | |
| 168070 | +#define FTS5_DETAIL_COLUMNS 2 | |
| 167795 | 168071 | |
| 167796 | 168072 | |
| 167797 | 168073 | |
| 167798 | 168074 | static int sqlite3Fts5ConfigParse( |
| 167799 | 168075 | Fts5Global*, sqlite3*, int, const char **, Fts5Config**, char** |
| @@ -167903,10 +168179,17 @@ | ||
| 167903 | 168179 | static char *sqlite3Fts5Strndup(int *pRc, const char *pIn, int nIn); |
| 167904 | 168180 | |
| 167905 | 168181 | /* Character set tests (like isspace(), isalpha() etc.) */ |
| 167906 | 168182 | static int sqlite3Fts5IsBareword(char t); |
| 167907 | 168183 | |
| 168184 | + | |
| 168185 | +/* Bucket of terms object used by the integrity-check in offsets=0 mode. */ | |
| 168186 | +typedef struct Fts5Termset Fts5Termset; | |
| 168187 | +static int sqlite3Fts5TermsetNew(Fts5Termset**); | |
| 168188 | +static int sqlite3Fts5TermsetAdd(Fts5Termset*, int, const char*, int, int *pbPresent); | |
| 168189 | +static void sqlite3Fts5TermsetFree(Fts5Termset*); | |
| 168190 | + | |
| 167908 | 168191 | /* |
| 167909 | 168192 | ** End of interface to code in fts5_buffer.c. |
| 167910 | 168193 | **************************************************************************/ |
| 167911 | 168194 | |
| 167912 | 168195 | /************************************************************************** |
| @@ -168024,11 +168307,10 @@ | ||
| 168024 | 168307 | static int sqlite3Fts5IndexSetAverages(Fts5Index *p, const u8*, int); |
| 168025 | 168308 | |
| 168026 | 168309 | /* |
| 168027 | 168310 | ** Functions called by the storage module as part of integrity-check. |
| 168028 | 168311 | */ |
| 168029 | -static u64 sqlite3Fts5IndexCksum(Fts5Config*,i64,int,int,const char*,int); | |
| 168030 | 168312 | static int sqlite3Fts5IndexIntegrityCheck(Fts5Index*, u64 cksum); |
| 168031 | 168313 | |
| 168032 | 168314 | /* |
| 168033 | 168315 | ** Called during virtual module initialization to register UDF |
| 168034 | 168316 | ** fts5_decode() with SQLite |
| @@ -168046,10 +168328,12 @@ | ||
| 168046 | 168328 | static int sqlite3Fts5IndexReinit(Fts5Index *p); |
| 168047 | 168329 | static int sqlite3Fts5IndexOptimize(Fts5Index *p); |
| 168048 | 168330 | static int sqlite3Fts5IndexMerge(Fts5Index *p, int nMerge); |
| 168049 | 168331 | |
| 168050 | 168332 | static int sqlite3Fts5IndexLoadConfig(Fts5Index *p); |
| 168333 | + | |
| 168334 | +static int sqlite3Fts5IterCollist(Fts5IndexIter*, const u8 **, int*); | |
| 168051 | 168335 | |
| 168052 | 168336 | /* |
| 168053 | 168337 | ** End of interface to code in fts5_index.c. |
| 168054 | 168338 | **************************************************************************/ |
| 168055 | 168339 | |
| @@ -168103,11 +168387,11 @@ | ||
| 168103 | 168387 | typedef struct Fts5Hash Fts5Hash; |
| 168104 | 168388 | |
| 168105 | 168389 | /* |
| 168106 | 168390 | ** Create a hash table, free a hash table. |
| 168107 | 168391 | */ |
| 168108 | -static int sqlite3Fts5HashNew(Fts5Hash**, int *pnSize); | |
| 168392 | +static int sqlite3Fts5HashNew(Fts5Config*, Fts5Hash**, int *pnSize); | |
| 168109 | 168393 | static void sqlite3Fts5HashFree(Fts5Hash*); |
| 168110 | 168394 | |
| 168111 | 168395 | static int sqlite3Fts5HashWrite( |
| 168112 | 168396 | Fts5Hash*, |
| 168113 | 168397 | i64 iRowid, /* Rowid for this entry */ |
| @@ -168239,12 +168523,22 @@ | ||
| 168239 | 168523 | static int sqlite3Fts5ExprInit(Fts5Global*, sqlite3*); |
| 168240 | 168524 | |
| 168241 | 168525 | static int sqlite3Fts5ExprPhraseCount(Fts5Expr*); |
| 168242 | 168526 | static int sqlite3Fts5ExprPhraseSize(Fts5Expr*, int iPhrase); |
| 168243 | 168527 | static int sqlite3Fts5ExprPoslist(Fts5Expr*, int, const u8 **); |
| 168528 | + | |
| 168529 | +typedef struct Fts5PoslistPopulator Fts5PoslistPopulator; | |
| 168530 | +static Fts5PoslistPopulator *sqlite3Fts5ExprClearPoslists(Fts5Expr*, int); | |
| 168531 | +static int sqlite3Fts5ExprPopulatePoslists( | |
| 168532 | + Fts5Config*, Fts5Expr*, Fts5PoslistPopulator*, int, const char*, int | |
| 168533 | +); | |
| 168534 | +static void sqlite3Fts5ExprCheckPoslists(Fts5Expr*, i64); | |
| 168535 | +static void sqlite3Fts5ExprClearEof(Fts5Expr*); | |
| 168244 | 168536 | |
| 168245 | 168537 | static int sqlite3Fts5ExprClonePhrase(Fts5Config*, Fts5Expr*, int, Fts5Expr**); |
| 168538 | + | |
| 168539 | +static int sqlite3Fts5ExprPhraseCollist(Fts5Expr *, int, const u8 **, int *); | |
| 168246 | 168540 | |
| 168247 | 168541 | /******************************************* |
| 168248 | 168542 | ** The fts5_expr.c API above this point is used by the other hand-written |
| 168249 | 168543 | ** C code in this module. The interfaces below this point are called by |
| 168250 | 168544 | ** the parser code in fts5parse.y. */ |
| @@ -170397,10 +170691,93 @@ | ||
| 170397 | 170691 | |
| 170398 | 170692 | return (t & 0x80) || aBareword[(int)t]; |
| 170399 | 170693 | } |
| 170400 | 170694 | |
| 170401 | 170695 | |
| 170696 | +/************************************************************************* | |
| 170697 | +*/ | |
| 170698 | +typedef struct Fts5TermsetEntry Fts5TermsetEntry; | |
| 170699 | +struct Fts5TermsetEntry { | |
| 170700 | + char *pTerm; | |
| 170701 | + int nTerm; | |
| 170702 | + int iIdx; /* Index (main or aPrefix[] entry) */ | |
| 170703 | + Fts5TermsetEntry *pNext; | |
| 170704 | +}; | |
| 170705 | + | |
| 170706 | +struct Fts5Termset { | |
| 170707 | + Fts5TermsetEntry *apHash[512]; | |
| 170708 | +}; | |
| 170709 | + | |
| 170710 | +static int sqlite3Fts5TermsetNew(Fts5Termset **pp){ | |
| 170711 | + int rc = SQLITE_OK; | |
| 170712 | + *pp = sqlite3Fts5MallocZero(&rc, sizeof(Fts5Termset)); | |
| 170713 | + return rc; | |
| 170714 | +} | |
| 170715 | + | |
| 170716 | +static int sqlite3Fts5TermsetAdd( | |
| 170717 | + Fts5Termset *p, | |
| 170718 | + int iIdx, | |
| 170719 | + const char *pTerm, int nTerm, | |
| 170720 | + int *pbPresent | |
| 170721 | +){ | |
| 170722 | + int rc = SQLITE_OK; | |
| 170723 | + *pbPresent = 0; | |
| 170724 | + if( p ){ | |
| 170725 | + int i; | |
| 170726 | + int hash; | |
| 170727 | + Fts5TermsetEntry *pEntry; | |
| 170728 | + | |
| 170729 | + /* Calculate a hash value for this term */ | |
| 170730 | + hash = 104 + iIdx; | |
| 170731 | + for(i=0; i<nTerm; i++){ | |
| 170732 | + hash += (hash << 3) + (int)pTerm[i]; | |
| 170733 | + } | |
| 170734 | + hash = hash % ArraySize(p->apHash); | |
| 170735 | + | |
| 170736 | + for(pEntry=p->apHash[hash]; pEntry; pEntry=pEntry->pNext){ | |
| 170737 | + if( pEntry->iIdx==iIdx | |
| 170738 | + && pEntry->nTerm==nTerm | |
| 170739 | + && memcmp(pEntry->pTerm, pTerm, nTerm)==0 | |
| 170740 | + ){ | |
| 170741 | + *pbPresent = 1; | |
| 170742 | + break; | |
| 170743 | + } | |
| 170744 | + } | |
| 170745 | + | |
| 170746 | + if( pEntry==0 ){ | |
| 170747 | + pEntry = sqlite3Fts5MallocZero(&rc, sizeof(Fts5TermsetEntry) + nTerm); | |
| 170748 | + if( pEntry ){ | |
| 170749 | + pEntry->pTerm = (char*)&pEntry[1]; | |
| 170750 | + pEntry->nTerm = nTerm; | |
| 170751 | + pEntry->iIdx = iIdx; | |
| 170752 | + memcpy(pEntry->pTerm, pTerm, nTerm); | |
| 170753 | + pEntry->pNext = p->apHash[hash]; | |
| 170754 | + p->apHash[hash] = pEntry; | |
| 170755 | + } | |
| 170756 | + } | |
| 170757 | + } | |
| 170758 | + | |
| 170759 | + return rc; | |
| 170760 | +} | |
| 170761 | + | |
| 170762 | +static void sqlite3Fts5TermsetFree(Fts5Termset *p){ | |
| 170763 | + if( p ){ | |
| 170764 | + int i; | |
| 170765 | + for(i=0; i<ArraySize(p->apHash); i++){ | |
| 170766 | + Fts5TermsetEntry *pEntry = p->apHash[i]; | |
| 170767 | + while( pEntry ){ | |
| 170768 | + Fts5TermsetEntry *pDel = pEntry; | |
| 170769 | + pEntry = pEntry->pNext; | |
| 170770 | + sqlite3_free(pDel); | |
| 170771 | + } | |
| 170772 | + } | |
| 170773 | + sqlite3_free(p); | |
| 170774 | + } | |
| 170775 | +} | |
| 170776 | + | |
| 170777 | + | |
| 170778 | + | |
| 170402 | 170779 | |
| 170403 | 170780 | /* |
| 170404 | 170781 | ** 2014 Jun 09 |
| 170405 | 170782 | ** |
| 170406 | 170783 | ** The author disclaims copyright to this source code. In place of |
| @@ -170412,11 +170789,10 @@ | ||
| 170412 | 170789 | ** |
| 170413 | 170790 | ****************************************************************************** |
| 170414 | 170791 | ** |
| 170415 | 170792 | ** This is an SQLite module implementing full-text search. |
| 170416 | 170793 | */ |
| 170417 | - | |
| 170418 | 170794 | |
| 170419 | 170795 | |
| 170420 | 170796 | /* #include "fts5Int.h" */ |
| 170421 | 170797 | |
| 170422 | 170798 | #define FTS5_DEFAULT_PAGE_SIZE 4050 |
| @@ -170595,10 +170971,37 @@ | ||
| 170595 | 170971 | if( quote=='[' || quote=='\'' || quote=='"' || quote=='`' ){ |
| 170596 | 170972 | fts5Dequote(z); |
| 170597 | 170973 | } |
| 170598 | 170974 | } |
| 170599 | 170975 | |
| 170976 | + | |
| 170977 | +struct Fts5Enum { | |
| 170978 | + const char *zName; | |
| 170979 | + int eVal; | |
| 170980 | +}; | |
| 170981 | +typedef struct Fts5Enum Fts5Enum; | |
| 170982 | + | |
| 170983 | +static int fts5ConfigSetEnum( | |
| 170984 | + const Fts5Enum *aEnum, | |
| 170985 | + const char *zEnum, | |
| 170986 | + int *peVal | |
| 170987 | +){ | |
| 170988 | + int nEnum = strlen(zEnum); | |
| 170989 | + int i; | |
| 170990 | + int iVal = -1; | |
| 170991 | + | |
| 170992 | + for(i=0; aEnum[i].zName; i++){ | |
| 170993 | + if( sqlite3_strnicmp(aEnum[i].zName, zEnum, nEnum)==0 ){ | |
| 170994 | + if( iVal>=0 ) return SQLITE_ERROR; | |
| 170995 | + iVal = aEnum[i].eVal; | |
| 170996 | + } | |
| 170997 | + } | |
| 170998 | + | |
| 170999 | + *peVal = iVal; | |
| 171000 | + return iVal<0 ? SQLITE_ERROR : SQLITE_OK; | |
| 171001 | +} | |
| 171002 | + | |
| 170600 | 171003 | /* |
| 170601 | 171004 | ** Parse a "special" CREATE VIRTUAL TABLE directive and update |
| 170602 | 171005 | ** configuration object pConfig as appropriate. |
| 170603 | 171006 | ** |
| 170604 | 171007 | ** If successful, object pConfig is updated and SQLITE_OK returned. If |
| @@ -170744,10 +171147,24 @@ | ||
| 170744 | 171147 | }else{ |
| 170745 | 171148 | pConfig->bColumnsize = (zArg[0]=='1'); |
| 170746 | 171149 | } |
| 170747 | 171150 | return rc; |
| 170748 | 171151 | } |
| 171152 | + | |
| 171153 | + if( sqlite3_strnicmp("detail", zCmd, nCmd)==0 ){ | |
| 171154 | + const Fts5Enum aDetail[] = { | |
| 171155 | + { "none", FTS5_DETAIL_NONE }, | |
| 171156 | + { "full", FTS5_DETAIL_FULL }, | |
| 171157 | + { "columns", FTS5_DETAIL_COLUMNS }, | |
| 171158 | + { 0, 0 } | |
| 171159 | + }; | |
| 171160 | + | |
| 171161 | + if( (rc = fts5ConfigSetEnum(aDetail, zArg, &pConfig->eDetail)) ){ | |
| 171162 | + *pzErr = sqlite3_mprintf("malformed detail=... directive"); | |
| 171163 | + } | |
| 171164 | + return rc; | |
| 171165 | + } | |
| 170749 | 171166 | |
| 170750 | 171167 | *pzErr = sqlite3_mprintf("unrecognized option: \"%.*s\"", nCmd, zCmd); |
| 170751 | 171168 | return SQLITE_ERROR; |
| 170752 | 171169 | } |
| 170753 | 171170 | |
| @@ -170900,10 +171317,11 @@ | ||
| 170900 | 171317 | pRet->azCol = (char**)sqlite3Fts5MallocZero(&rc, nByte); |
| 170901 | 171318 | pRet->abUnindexed = (u8*)&pRet->azCol[nArg]; |
| 170902 | 171319 | pRet->zDb = sqlite3Fts5Strndup(&rc, azArg[1], -1); |
| 170903 | 171320 | pRet->zName = sqlite3Fts5Strndup(&rc, azArg[2], -1); |
| 170904 | 171321 | pRet->bColumnsize = 1; |
| 171322 | + pRet->eDetail = FTS5_DETAIL_FULL; | |
| 170905 | 171323 | #ifdef SQLITE_DEBUG |
| 170906 | 171324 | pRet->bPrefixIndex = 1; |
| 170907 | 171325 | #endif |
| 170908 | 171326 | if( rc==SQLITE_OK && sqlite3_stricmp(pRet->zName, FTS5_RANK_NAME)==0 ){ |
| 170909 | 171327 | *pzErr = sqlite3_mprintf("reserved fts5 table name: %s", pRet->zName); |
| @@ -171346,10 +171764,11 @@ | ||
| 171346 | 171764 | #endif |
| 171347 | 171765 | |
| 171348 | 171766 | |
| 171349 | 171767 | struct Fts5Expr { |
| 171350 | 171768 | Fts5Index *pIndex; |
| 171769 | + Fts5Config *pConfig; | |
| 171351 | 171770 | Fts5ExprNode *pRoot; |
| 171352 | 171771 | int bDesc; /* Iterate in descending rowid order */ |
| 171353 | 171772 | int nPhrase; /* Number of phrases in expression */ |
| 171354 | 171773 | Fts5ExprPhrase **apExprPhrase; /* Pointers to phrase objects */ |
| 171355 | 171774 | }; |
| @@ -171541,10 +171960,11 @@ | ||
| 171541 | 171960 | sParse.rc = SQLITE_NOMEM; |
| 171542 | 171961 | sqlite3Fts5ParseNodeFree(sParse.pExpr); |
| 171543 | 171962 | }else{ |
| 171544 | 171963 | pNew->pRoot = sParse.pExpr; |
| 171545 | 171964 | pNew->pIndex = 0; |
| 171965 | + pNew->pConfig = pConfig; | |
| 171546 | 171966 | pNew->apExprPhrase = sParse.apPhrase; |
| 171547 | 171967 | pNew->nPhrase = sParse.nPhrase; |
| 171548 | 171968 | sParse.apPhrase = 0; |
| 171549 | 171969 | } |
| 171550 | 171970 | } |
| @@ -171605,12 +172025,13 @@ | ||
| 171605 | 172025 | } |
| 171606 | 172026 | |
| 171607 | 172027 | /* |
| 171608 | 172028 | ** Argument pTerm must be a synonym iterator. |
| 171609 | 172029 | */ |
| 171610 | -static int fts5ExprSynonymPoslist( | |
| 172030 | +static int fts5ExprSynonymList( | |
| 171611 | 172031 | Fts5ExprTerm *pTerm, |
| 172032 | + int bCollist, | |
| 171612 | 172033 | Fts5Colset *pColset, |
| 171613 | 172034 | i64 iRowid, |
| 171614 | 172035 | int *pbDel, /* OUT: Caller should sqlite3_free(*pa) */ |
| 171615 | 172036 | u8 **pa, int *pn |
| 171616 | 172037 | ){ |
| @@ -171625,13 +172046,20 @@ | ||
| 171625 | 172046 | for(p=pTerm; p; p=p->pSynonym){ |
| 171626 | 172047 | Fts5IndexIter *pIter = p->pIter; |
| 171627 | 172048 | if( sqlite3Fts5IterEof(pIter)==0 && sqlite3Fts5IterRowid(pIter)==iRowid ){ |
| 171628 | 172049 | const u8 *a; |
| 171629 | 172050 | int n; |
| 171630 | - i64 dummy; | |
| 171631 | - rc = sqlite3Fts5IterPoslist(pIter, pColset, &a, &n, &dummy); | |
| 172051 | + | |
| 172052 | + if( bCollist ){ | |
| 172053 | + rc = sqlite3Fts5IterCollist(pIter, &a, &n); | |
| 172054 | + }else{ | |
| 172055 | + i64 dummy; | |
| 172056 | + rc = sqlite3Fts5IterPoslist(pIter, pColset, &a, &n, &dummy); | |
| 172057 | + } | |
| 172058 | + | |
| 171632 | 172059 | if( rc!=SQLITE_OK ) goto synonym_poslist_out; |
| 172060 | + if( n==0 ) continue; | |
| 171633 | 172061 | if( nIter==nAlloc ){ |
| 171634 | 172062 | int nByte = sizeof(Fts5PoslistReader) * nAlloc * 2; |
| 171635 | 172063 | Fts5PoslistReader *aNew = (Fts5PoslistReader*)sqlite3_malloc(nByte); |
| 171636 | 172064 | if( aNew==0 ){ |
| 171637 | 172065 | rc = SQLITE_NOMEM; |
| @@ -171728,12 +172156,12 @@ | ||
| 171728 | 172156 | i64 dummy; |
| 171729 | 172157 | int n = 0; |
| 171730 | 172158 | int bFlag = 0; |
| 171731 | 172159 | const u8 *a = 0; |
| 171732 | 172160 | if( pTerm->pSynonym ){ |
| 171733 | - rc = fts5ExprSynonymPoslist( | |
| 171734 | - pTerm, pColset, pNode->iRowid, &bFlag, (u8**)&a, &n | |
| 172161 | + rc = fts5ExprSynonymList( | |
| 172162 | + pTerm, 0, pColset, pNode->iRowid, &bFlag, (u8**)&a, &n | |
| 171735 | 172163 | ); |
| 171736 | 172164 | }else{ |
| 171737 | 172165 | rc = sqlite3Fts5IterPoslist(pTerm->pIter, pColset, &a, &n, &dummy); |
| 171738 | 172166 | } |
| 171739 | 172167 | if( rc!=SQLITE_OK ) goto ismatch_out; |
| @@ -172063,34 +172491,55 @@ | ||
| 172063 | 172491 | Fts5Expr *pExpr, /* Expression that pNear is a part of */ |
| 172064 | 172492 | Fts5ExprNode *pNode /* The "NEAR" node (FTS5_STRING) */ |
| 172065 | 172493 | ){ |
| 172066 | 172494 | Fts5ExprNearset *pNear = pNode->pNear; |
| 172067 | 172495 | int rc = *pRc; |
| 172068 | - int i; | |
| 172069 | - | |
| 172070 | - /* Check that each phrase in the nearset matches the current row. | |
| 172071 | - ** Populate the pPhrase->poslist buffers at the same time. If any | |
| 172072 | - ** phrase is not a match, break out of the loop early. */ | |
| 172073 | - for(i=0; rc==SQLITE_OK && i<pNear->nPhrase; i++){ | |
| 172074 | - Fts5ExprPhrase *pPhrase = pNear->apPhrase[i]; | |
| 172075 | - if( pPhrase->nTerm>1 || pPhrase->aTerm[0].pSynonym || pNear->pColset ){ | |
| 172076 | - int bMatch = 0; | |
| 172077 | - rc = fts5ExprPhraseIsMatch(pNode, pNear->pColset, pPhrase, &bMatch); | |
| 172078 | - if( bMatch==0 ) break; | |
| 172079 | - }else{ | |
| 172080 | - rc = sqlite3Fts5IterPoslistBuffer( | |
| 172081 | - pPhrase->aTerm[0].pIter, &pPhrase->poslist | |
| 172082 | - ); | |
| 172083 | - } | |
| 172084 | - } | |
| 172085 | - | |
| 172086 | - *pRc = rc; | |
| 172087 | - if( i==pNear->nPhrase && (i==1 || fts5ExprNearIsMatch(pRc, pNear)) ){ | |
| 172088 | - return 1; | |
| 172089 | - } | |
| 172090 | - | |
| 172091 | - return 0; | |
| 172496 | + | |
| 172497 | + if( pExpr->pConfig->eDetail!=FTS5_DETAIL_FULL ){ | |
| 172498 | + Fts5ExprTerm *pTerm; | |
| 172499 | + Fts5ExprPhrase *pPhrase = pNear->apPhrase[0]; | |
| 172500 | + pPhrase->poslist.n = 0; | |
| 172501 | + for(pTerm=&pPhrase->aTerm[0]; pTerm; pTerm=pTerm->pSynonym){ | |
| 172502 | + Fts5IndexIter *pIter = pTerm->pIter; | |
| 172503 | + if( sqlite3Fts5IterEof(pIter)==0 ){ | |
| 172504 | + int n; | |
| 172505 | + i64 iRowid; | |
| 172506 | + rc = sqlite3Fts5IterPoslist(pIter, pNear->pColset, 0, &n, &iRowid); | |
| 172507 | + if( rc!=SQLITE_OK ){ | |
| 172508 | + *pRc = rc; | |
| 172509 | + return 0; | |
| 172510 | + }else if( iRowid==pNode->iRowid && n>0 ){ | |
| 172511 | + pPhrase->poslist.n = 1; | |
| 172512 | + } | |
| 172513 | + } | |
| 172514 | + } | |
| 172515 | + return pPhrase->poslist.n; | |
| 172516 | + }else{ | |
| 172517 | + int i; | |
| 172518 | + | |
| 172519 | + /* Check that each phrase in the nearset matches the current row. | |
| 172520 | + ** Populate the pPhrase->poslist buffers at the same time. If any | |
| 172521 | + ** phrase is not a match, break out of the loop early. */ | |
| 172522 | + for(i=0; rc==SQLITE_OK && i<pNear->nPhrase; i++){ | |
| 172523 | + Fts5ExprPhrase *pPhrase = pNear->apPhrase[i]; | |
| 172524 | + if( pPhrase->nTerm>1 || pPhrase->aTerm[0].pSynonym || pNear->pColset ){ | |
| 172525 | + int bMatch = 0; | |
| 172526 | + rc = fts5ExprPhraseIsMatch(pNode, pNear->pColset, pPhrase, &bMatch); | |
| 172527 | + if( bMatch==0 ) break; | |
| 172528 | + }else{ | |
| 172529 | + rc = sqlite3Fts5IterPoslistBuffer( | |
| 172530 | + pPhrase->aTerm[0].pIter, &pPhrase->poslist | |
| 172531 | + ); | |
| 172532 | + } | |
| 172533 | + } | |
| 172534 | + | |
| 172535 | + *pRc = rc; | |
| 172536 | + if( i==pNear->nPhrase && (i==1 || fts5ExprNearIsMatch(pRc, pNear)) ){ | |
| 172537 | + return 1; | |
| 172538 | + } | |
| 172539 | + return 0; | |
| 172540 | + } | |
| 172092 | 172541 | } |
| 172093 | 172542 | |
| 172094 | 172543 | static int fts5ExprTokenTest( |
| 172095 | 172544 | Fts5Expr *pExpr, /* Expression that pNear is a part of */ |
| 172096 | 172545 | Fts5ExprNode *pNode /* The "NEAR" node (FTS5_TERM) */ |
| @@ -172524,10 +172973,13 @@ | ||
| 172524 | 172973 | if( cmp || p2->bNomatch ) break; |
| 172525 | 172974 | rc = fts5ExprNodeNext(pExpr, p1, 0, 0); |
| 172526 | 172975 | } |
| 172527 | 172976 | pNode->bEof = p1->bEof; |
| 172528 | 172977 | pNode->iRowid = p1->iRowid; |
| 172978 | + if( p1->bEof ){ | |
| 172979 | + fts5ExprNodeZeroPoslist(p2); | |
| 172980 | + } | |
| 172529 | 172981 | break; |
| 172530 | 172982 | } |
| 172531 | 172983 | } |
| 172532 | 172984 | } |
| 172533 | 172985 | return rc; |
| @@ -172909,10 +173361,11 @@ | ||
| 172909 | 173361 | } |
| 172910 | 173362 | |
| 172911 | 173363 | if( rc==SQLITE_OK ){ |
| 172912 | 173364 | /* All the allocations succeeded. Put the expression object together. */ |
| 172913 | 173365 | pNew->pIndex = pExpr->pIndex; |
| 173366 | + pNew->pConfig = pExpr->pConfig; | |
| 172914 | 173367 | pNew->nPhrase = 1; |
| 172915 | 173368 | pNew->apExprPhrase[0] = sCtx.pPhrase; |
| 172916 | 173369 | pNew->pRoot->pNear->apPhrase[0] = sCtx.pPhrase; |
| 172917 | 173370 | pNew->pRoot->pNear->nPhrase = 1; |
| 172918 | 173371 | sCtx.pPhrase->pNode = pNew->pRoot; |
| @@ -173050,10 +173503,19 @@ | ||
| 173050 | 173503 | static void sqlite3Fts5ParseSetColset( |
| 173051 | 173504 | Fts5Parse *pParse, |
| 173052 | 173505 | Fts5ExprNearset *pNear, |
| 173053 | 173506 | Fts5Colset *pColset |
| 173054 | 173507 | ){ |
| 173508 | + if( pParse->pConfig->eDetail==FTS5_DETAIL_NONE ){ | |
| 173509 | + pParse->rc = SQLITE_ERROR; | |
| 173510 | + pParse->zErr = sqlite3_mprintf( | |
| 173511 | + "fts5: column queries are not supported (detail=none)" | |
| 173512 | + ); | |
| 173513 | + sqlite3_free(pColset); | |
| 173514 | + return; | |
| 173515 | + } | |
| 173516 | + | |
| 173055 | 173517 | if( pNear ){ |
| 173056 | 173518 | pNear->pColset = pColset; |
| 173057 | 173519 | }else{ |
| 173058 | 173520 | sqlite3_free(pColset); |
| 173059 | 173521 | } |
| @@ -173111,15 +173573,24 @@ | ||
| 173111 | 173573 | if( eType==FTS5_STRING ){ |
| 173112 | 173574 | int iPhrase; |
| 173113 | 173575 | for(iPhrase=0; iPhrase<pNear->nPhrase; iPhrase++){ |
| 173114 | 173576 | pNear->apPhrase[iPhrase]->pNode = pRet; |
| 173115 | 173577 | } |
| 173116 | - if( pNear->nPhrase==1 | |
| 173117 | - && pNear->apPhrase[0]->nTerm==1 | |
| 173118 | - && pNear->apPhrase[0]->aTerm[0].pSynonym==0 | |
| 173119 | - ){ | |
| 173120 | - pRet->eType = FTS5_TERM; | |
| 173578 | + if( pNear->nPhrase==1 && pNear->apPhrase[0]->nTerm==1 ){ | |
| 173579 | + if( pNear->apPhrase[0]->aTerm[0].pSynonym==0 ){ | |
| 173580 | + pRet->eType = FTS5_TERM; | |
| 173581 | + } | |
| 173582 | + }else if( pParse->pConfig->eDetail!=FTS5_DETAIL_FULL ){ | |
| 173583 | + assert( pParse->rc==SQLITE_OK ); | |
| 173584 | + pParse->rc = SQLITE_ERROR; | |
| 173585 | + assert( pParse->zErr==0 ); | |
| 173586 | + pParse->zErr = sqlite3_mprintf( | |
| 173587 | + "fts5: %s queries are not supported (detail!=full)", | |
| 173588 | + pNear->nPhrase==1 ? "phrase": "NEAR" | |
| 173589 | + ); | |
| 173590 | + sqlite3_free(pRet); | |
| 173591 | + pRet = 0; | |
| 173121 | 173592 | } |
| 173122 | 173593 | }else{ |
| 173123 | 173594 | fts5ExprAddChildren(pRet, pLeft); |
| 173124 | 173595 | fts5ExprAddChildren(pRet, pRight); |
| 173125 | 173596 | } |
| @@ -173229,10 +173700,13 @@ | ||
| 173229 | 173700 | |
| 173230 | 173701 | zRet = fts5PrintfAppend(zRet, " {"); |
| 173231 | 173702 | for(iTerm=0; zRet && iTerm<pPhrase->nTerm; iTerm++){ |
| 173232 | 173703 | char *zTerm = pPhrase->aTerm[iTerm].zTerm; |
| 173233 | 173704 | zRet = fts5PrintfAppend(zRet, "%s%s", iTerm==0?"":" ", zTerm); |
| 173705 | + if( pPhrase->aTerm[iTerm].bPrefix ){ | |
| 173706 | + zRet = fts5PrintfAppend(zRet, "*"); | |
| 173707 | + } | |
| 173234 | 173708 | } |
| 173235 | 173709 | |
| 173236 | 173710 | if( zRet ) zRet = fts5PrintfAppend(zRet, "}"); |
| 173237 | 173711 | if( zRet==0 ) return 0; |
| 173238 | 173712 | } |
| @@ -173541,10 +174015,237 @@ | ||
| 173541 | 174015 | *pa = 0; |
| 173542 | 174016 | nRet = 0; |
| 173543 | 174017 | } |
| 173544 | 174018 | return nRet; |
| 173545 | 174019 | } |
| 174020 | + | |
| 174021 | +struct Fts5PoslistPopulator { | |
| 174022 | + Fts5PoslistWriter writer; | |
| 174023 | + int bOk; /* True if ok to populate */ | |
| 174024 | + int bMiss; | |
| 174025 | +}; | |
| 174026 | + | |
| 174027 | +static Fts5PoslistPopulator *sqlite3Fts5ExprClearPoslists(Fts5Expr *pExpr, int bLive){ | |
| 174028 | + Fts5PoslistPopulator *pRet; | |
| 174029 | + pRet = sqlite3_malloc(sizeof(Fts5PoslistPopulator)*pExpr->nPhrase); | |
| 174030 | + if( pRet ){ | |
| 174031 | + int i; | |
| 174032 | + memset(pRet, 0, sizeof(Fts5PoslistPopulator)*pExpr->nPhrase); | |
| 174033 | + for(i=0; i<pExpr->nPhrase; i++){ | |
| 174034 | + Fts5Buffer *pBuf = &pExpr->apExprPhrase[i]->poslist; | |
| 174035 | + Fts5ExprNode *pNode = pExpr->apExprPhrase[i]->pNode; | |
| 174036 | + assert( pExpr->apExprPhrase[i]->nTerm==1 ); | |
| 174037 | + if( bLive && | |
| 174038 | + (pBuf->n==0 || pNode->iRowid!=pExpr->pRoot->iRowid || pNode->bEof) | |
| 174039 | + ){ | |
| 174040 | + pRet[i].bMiss = 1; | |
| 174041 | + }else{ | |
| 174042 | + pBuf->n = 0; | |
| 174043 | + } | |
| 174044 | + } | |
| 174045 | + } | |
| 174046 | + return pRet; | |
| 174047 | +} | |
| 174048 | + | |
| 174049 | +struct Fts5ExprCtx { | |
| 174050 | + Fts5Expr *pExpr; | |
| 174051 | + Fts5PoslistPopulator *aPopulator; | |
| 174052 | + i64 iOff; | |
| 174053 | +}; | |
| 174054 | +typedef struct Fts5ExprCtx Fts5ExprCtx; | |
| 174055 | + | |
| 174056 | +/* | |
| 174057 | +** TODO: Make this more efficient! | |
| 174058 | +*/ | |
| 174059 | +static int fts5ExprColsetTest(Fts5Colset *pColset, int iCol){ | |
| 174060 | + int i; | |
| 174061 | + for(i=0; i<pColset->nCol; i++){ | |
| 174062 | + if( pColset->aiCol[i]==iCol ) return 1; | |
| 174063 | + } | |
| 174064 | + return 0; | |
| 174065 | +} | |
| 174066 | + | |
| 174067 | +static int fts5ExprPopulatePoslistsCb( | |
| 174068 | + void *pCtx, /* Copy of 2nd argument to xTokenize() */ | |
| 174069 | + int tflags, /* Mask of FTS5_TOKEN_* flags */ | |
| 174070 | + const char *pToken, /* Pointer to buffer containing token */ | |
| 174071 | + int nToken, /* Size of token in bytes */ | |
| 174072 | + int iStart, /* Byte offset of token within input text */ | |
| 174073 | + int iEnd /* Byte offset of end of token within input text */ | |
| 174074 | +){ | |
| 174075 | + Fts5ExprCtx *p = (Fts5ExprCtx*)pCtx; | |
| 174076 | + Fts5Expr *pExpr = p->pExpr; | |
| 174077 | + int i; | |
| 174078 | + | |
| 174079 | + if( (tflags & FTS5_TOKEN_COLOCATED)==0 ) p->iOff++; | |
| 174080 | + for(i=0; i<pExpr->nPhrase; i++){ | |
| 174081 | + Fts5ExprTerm *pTerm; | |
| 174082 | + if( p->aPopulator[i].bOk==0 ) continue; | |
| 174083 | + for(pTerm=&pExpr->apExprPhrase[i]->aTerm[0]; pTerm; pTerm=pTerm->pSynonym){ | |
| 174084 | + int nTerm = strlen(pTerm->zTerm); | |
| 174085 | + if( (nTerm==nToken || (nTerm<nToken && pTerm->bPrefix)) | |
| 174086 | + && memcmp(pTerm->zTerm, pToken, nTerm)==0 | |
| 174087 | + ){ | |
| 174088 | + int rc = sqlite3Fts5PoslistWriterAppend( | |
| 174089 | + &pExpr->apExprPhrase[i]->poslist, &p->aPopulator[i].writer, p->iOff | |
| 174090 | + ); | |
| 174091 | + if( rc ) return rc; | |
| 174092 | + break; | |
| 174093 | + } | |
| 174094 | + } | |
| 174095 | + } | |
| 174096 | + return SQLITE_OK; | |
| 174097 | +} | |
| 174098 | + | |
| 174099 | +static int sqlite3Fts5ExprPopulatePoslists( | |
| 174100 | + Fts5Config *pConfig, | |
| 174101 | + Fts5Expr *pExpr, | |
| 174102 | + Fts5PoslistPopulator *aPopulator, | |
| 174103 | + int iCol, | |
| 174104 | + const char *z, int n | |
| 174105 | +){ | |
| 174106 | + int i; | |
| 174107 | + Fts5ExprCtx sCtx; | |
| 174108 | + sCtx.pExpr = pExpr; | |
| 174109 | + sCtx.aPopulator = aPopulator; | |
| 174110 | + sCtx.iOff = (((i64)iCol) << 32) - 1; | |
| 174111 | + | |
| 174112 | + for(i=0; i<pExpr->nPhrase; i++){ | |
| 174113 | + Fts5ExprNode *pNode = pExpr->apExprPhrase[i]->pNode; | |
| 174114 | + Fts5Colset *pColset = pNode->pNear->pColset; | |
| 174115 | + if( (pColset && 0==fts5ExprColsetTest(pColset, iCol)) | |
| 174116 | + || aPopulator[i].bMiss | |
| 174117 | + ){ | |
| 174118 | + aPopulator[i].bOk = 0; | |
| 174119 | + }else{ | |
| 174120 | + aPopulator[i].bOk = 1; | |
| 174121 | + } | |
| 174122 | + } | |
| 174123 | + | |
| 174124 | + return sqlite3Fts5Tokenize(pConfig, | |
| 174125 | + FTS5_TOKENIZE_AUX, z, n, (void*)&sCtx, fts5ExprPopulatePoslistsCb | |
| 174126 | + ); | |
| 174127 | +} | |
| 174128 | + | |
| 174129 | +static void fts5ExprClearPoslists(Fts5ExprNode *pNode){ | |
| 174130 | + if( pNode->eType==FTS5_TERM || pNode->eType==FTS5_STRING ){ | |
| 174131 | + pNode->pNear->apPhrase[0]->poslist.n = 0; | |
| 174132 | + }else{ | |
| 174133 | + int i; | |
| 174134 | + for(i=0; i<pNode->nChild; i++){ | |
| 174135 | + fts5ExprClearPoslists(pNode->apChild[i]); | |
| 174136 | + } | |
| 174137 | + } | |
| 174138 | +} | |
| 174139 | + | |
| 174140 | +static int fts5ExprCheckPoslists(Fts5ExprNode *pNode, i64 iRowid){ | |
| 174141 | + if( pNode ){ | |
| 174142 | + pNode->iRowid = iRowid; | |
| 174143 | + pNode->bEof = 0; | |
| 174144 | + switch( pNode->eType ){ | |
| 174145 | + case FTS5_TERM: | |
| 174146 | + case FTS5_STRING: | |
| 174147 | + return (pNode->pNear->apPhrase[0]->poslist.n>0); | |
| 174148 | + | |
| 174149 | + case FTS5_AND: { | |
| 174150 | + int i; | |
| 174151 | + for(i=0; i<pNode->nChild; i++){ | |
| 174152 | + if( fts5ExprCheckPoslists(pNode->apChild[i], iRowid)==0 ){ | |
| 174153 | + fts5ExprClearPoslists(pNode); | |
| 174154 | + return 0; | |
| 174155 | + } | |
| 174156 | + } | |
| 174157 | + break; | |
| 174158 | + } | |
| 174159 | + | |
| 174160 | + case FTS5_OR: { | |
| 174161 | + int i; | |
| 174162 | + int bRet = 0; | |
| 174163 | + for(i=0; i<pNode->nChild; i++){ | |
| 174164 | + if( fts5ExprCheckPoslists(pNode->apChild[i], iRowid) ){ | |
| 174165 | + bRet = 1; | |
| 174166 | + } | |
| 174167 | + } | |
| 174168 | + if( bRet==0 ){ | |
| 174169 | + fts5ExprClearPoslists(pNode); | |
| 174170 | + } | |
| 174171 | + return bRet; | |
| 174172 | + } | |
| 174173 | + | |
| 174174 | + default: { | |
| 174175 | + assert( pNode->eType==FTS5_NOT ); | |
| 174176 | + if( 0==fts5ExprCheckPoslists(pNode->apChild[0], iRowid) | |
| 174177 | + || 0!=fts5ExprCheckPoslists(pNode->apChild[1], iRowid) | |
| 174178 | + ){ | |
| 174179 | + fts5ExprClearPoslists(pNode); | |
| 174180 | + return 0; | |
| 174181 | + } | |
| 174182 | + break; | |
| 174183 | + } | |
| 174184 | + } | |
| 174185 | + } | |
| 174186 | + return 1; | |
| 174187 | +} | |
| 174188 | + | |
| 174189 | +static void sqlite3Fts5ExprCheckPoslists(Fts5Expr *pExpr, i64 iRowid){ | |
| 174190 | + fts5ExprCheckPoslists(pExpr->pRoot, iRowid); | |
| 174191 | +} | |
| 174192 | + | |
| 174193 | +static void fts5ExprClearEof(Fts5ExprNode *pNode){ | |
| 174194 | + int i; | |
| 174195 | + for(i=0; i<pNode->nChild; i++){ | |
| 174196 | + fts5ExprClearEof(pNode->apChild[i]); | |
| 174197 | + } | |
| 174198 | + pNode->bEof = 0; | |
| 174199 | +} | |
| 174200 | +static void sqlite3Fts5ExprClearEof(Fts5Expr *pExpr){ | |
| 174201 | + fts5ExprClearEof(pExpr->pRoot); | |
| 174202 | +} | |
| 174203 | + | |
| 174204 | +/* | |
| 174205 | +** This function is only called for detail=columns tables. | |
| 174206 | +*/ | |
| 174207 | +static int sqlite3Fts5ExprPhraseCollist( | |
| 174208 | + Fts5Expr *pExpr, | |
| 174209 | + int iPhrase, | |
| 174210 | + const u8 **ppCollist, | |
| 174211 | + int *pnCollist | |
| 174212 | +){ | |
| 174213 | + Fts5ExprPhrase *pPhrase = pExpr->apExprPhrase[iPhrase]; | |
| 174214 | + Fts5ExprNode *pNode = pPhrase->pNode; | |
| 174215 | + int rc = SQLITE_OK; | |
| 174216 | + | |
| 174217 | + assert( iPhrase>=0 && iPhrase<pExpr->nPhrase ); | |
| 174218 | + if( pNode->bEof==0 | |
| 174219 | + && pNode->iRowid==pExpr->pRoot->iRowid | |
| 174220 | + && pPhrase->poslist.n>0 | |
| 174221 | + ){ | |
| 174222 | + Fts5ExprTerm *pTerm = &pPhrase->aTerm[0]; | |
| 174223 | + if( pTerm->pSynonym ){ | |
| 174224 | + int bDel = 0; | |
| 174225 | + u8 *a; | |
| 174226 | + rc = fts5ExprSynonymList( | |
| 174227 | + pTerm, 1, 0, pNode->iRowid, &bDel, &a, pnCollist | |
| 174228 | + ); | |
| 174229 | + if( bDel ){ | |
| 174230 | + sqlite3Fts5BufferSet(&rc, &pPhrase->poslist, *pnCollist, a); | |
| 174231 | + *ppCollist = pPhrase->poslist.p; | |
| 174232 | + sqlite3_free(a); | |
| 174233 | + }else{ | |
| 174234 | + *ppCollist = a; | |
| 174235 | + } | |
| 174236 | + }else{ | |
| 174237 | + sqlite3Fts5IterCollist(pPhrase->aTerm[0].pIter, ppCollist, pnCollist); | |
| 174238 | + } | |
| 174239 | + }else{ | |
| 174240 | + *ppCollist = 0; | |
| 174241 | + *pnCollist = 0; | |
| 174242 | + } | |
| 174243 | + | |
| 174244 | + return rc; | |
| 174245 | +} | |
| 174246 | + | |
| 173546 | 174247 | |
| 173547 | 174248 | /* |
| 173548 | 174249 | ** 2014 August 11 |
| 173549 | 174250 | ** |
| 173550 | 174251 | ** The author disclaims copyright to this source code. In place of |
| @@ -173570,10 +174271,11 @@ | ||
| 173570 | 174271 | ** segment. |
| 173571 | 174272 | */ |
| 173572 | 174273 | |
| 173573 | 174274 | |
| 173574 | 174275 | struct Fts5Hash { |
| 174276 | + int eDetail; /* Copy of Fts5Config.eDetail */ | |
| 173575 | 174277 | int *pnByte; /* Pointer to bytes counter */ |
| 173576 | 174278 | int nEntry; /* Number of entries currently in hash */ |
| 173577 | 174279 | int nSlot; /* Size of aSlot[] array */ |
| 173578 | 174280 | Fts5HashEntry *pScan; /* Current ordered scan item */ |
| 173579 | 174281 | Fts5HashEntry **aSlot; /* Array of hash slots */ |
| @@ -173606,10 +174308,11 @@ | ||
| 173606 | 174308 | |
| 173607 | 174309 | int nAlloc; /* Total size of allocation */ |
| 173608 | 174310 | int iSzPoslist; /* Offset of space for 4-byte poslist size */ |
| 173609 | 174311 | int nData; /* Total bytes of data (incl. structure) */ |
| 173610 | 174312 | u8 bDel; /* Set delete-flag @ iSzPoslist */ |
| 174313 | + u8 bContent; /* Set content-flag (detail=none mode) */ | |
| 173611 | 174314 | |
| 173612 | 174315 | int iCol; /* Column of last value written */ |
| 173613 | 174316 | int iPos; /* Position of last value written */ |
| 173614 | 174317 | i64 iRowid; /* Rowid of last value written */ |
| 173615 | 174318 | char zKey[8]; /* Nul-terminated entry key */ |
| @@ -173623,11 +174326,11 @@ | ||
| 173623 | 174326 | |
| 173624 | 174327 | |
| 173625 | 174328 | /* |
| 173626 | 174329 | ** Allocate a new hash table. |
| 173627 | 174330 | */ |
| 173628 | -static int sqlite3Fts5HashNew(Fts5Hash **ppNew, int *pnByte){ | |
| 174331 | +static int sqlite3Fts5HashNew(Fts5Config *pConfig, Fts5Hash **ppNew, int *pnByte){ | |
| 173629 | 174332 | int rc = SQLITE_OK; |
| 173630 | 174333 | Fts5Hash *pNew; |
| 173631 | 174334 | |
| 173632 | 174335 | *ppNew = pNew = (Fts5Hash*)sqlite3_malloc(sizeof(Fts5Hash)); |
| 173633 | 174336 | if( pNew==0 ){ |
| @@ -173634,10 +174337,11 @@ | ||
| 173634 | 174337 | rc = SQLITE_NOMEM; |
| 173635 | 174338 | }else{ |
| 173636 | 174339 | int nByte; |
| 173637 | 174340 | memset(pNew, 0, sizeof(Fts5Hash)); |
| 173638 | 174341 | pNew->pnByte = pnByte; |
| 174342 | + pNew->eDetail = pConfig->eDetail; | |
| 173639 | 174343 | |
| 173640 | 174344 | pNew->nSlot = 1024; |
| 173641 | 174345 | nByte = sizeof(Fts5HashEntry*) * pNew->nSlot; |
| 173642 | 174346 | pNew->aSlot = (Fts5HashEntry**)sqlite3_malloc(nByte); |
| 173643 | 174347 | if( pNew->aSlot==0 ){ |
| @@ -173726,30 +174430,50 @@ | ||
| 173726 | 174430 | pHash->nSlot = nNew; |
| 173727 | 174431 | pHash->aSlot = apNew; |
| 173728 | 174432 | return SQLITE_OK; |
| 173729 | 174433 | } |
| 173730 | 174434 | |
| 173731 | -static void fts5HashAddPoslistSize(Fts5HashEntry *p){ | |
| 174435 | +static void fts5HashAddPoslistSize(Fts5Hash *pHash, Fts5HashEntry *p){ | |
| 173732 | 174436 | if( p->iSzPoslist ){ |
| 173733 | 174437 | u8 *pPtr = (u8*)p; |
| 173734 | - int nSz = (p->nData - p->iSzPoslist - 1); /* Size in bytes */ | |
| 173735 | - int nPos = nSz*2 + p->bDel; /* Value of nPos field */ | |
| 173736 | - | |
| 173737 | - assert( p->bDel==0 || p->bDel==1 ); | |
| 173738 | - if( nPos<=127 ){ | |
| 173739 | - pPtr[p->iSzPoslist] = (u8)nPos; | |
| 173740 | - }else{ | |
| 173741 | - int nByte = sqlite3Fts5GetVarintLen((u32)nPos); | |
| 173742 | - memmove(&pPtr[p->iSzPoslist + nByte], &pPtr[p->iSzPoslist + 1], nSz); | |
| 173743 | - sqlite3Fts5PutVarint(&pPtr[p->iSzPoslist], nPos); | |
| 173744 | - p->nData += (nByte-1); | |
| 173745 | - } | |
| 173746 | - p->bDel = 0; | |
| 174438 | + if( pHash->eDetail==FTS5_DETAIL_NONE ){ | |
| 174439 | + assert( p->nData==p->iSzPoslist ); | |
| 174440 | + if( p->bDel ){ | |
| 174441 | + pPtr[p->nData++] = 0x00; | |
| 174442 | + if( p->bContent ){ | |
| 174443 | + pPtr[p->nData++] = 0x00; | |
| 174444 | + } | |
| 174445 | + } | |
| 174446 | + }else{ | |
| 174447 | + int nSz = (p->nData - p->iSzPoslist - 1); /* Size in bytes */ | |
| 174448 | + int nPos = nSz*2 + p->bDel; /* Value of nPos field */ | |
| 174449 | + | |
| 174450 | + assert( p->bDel==0 || p->bDel==1 ); | |
| 174451 | + if( nPos<=127 ){ | |
| 174452 | + pPtr[p->iSzPoslist] = (u8)nPos; | |
| 174453 | + }else{ | |
| 174454 | + int nByte = sqlite3Fts5GetVarintLen((u32)nPos); | |
| 174455 | + memmove(&pPtr[p->iSzPoslist + nByte], &pPtr[p->iSzPoslist + 1], nSz); | |
| 174456 | + sqlite3Fts5PutVarint(&pPtr[p->iSzPoslist], nPos); | |
| 174457 | + p->nData += (nByte-1); | |
| 174458 | + } | |
| 174459 | + } | |
| 174460 | + | |
| 173747 | 174461 | p->iSzPoslist = 0; |
| 174462 | + p->bDel = 0; | |
| 174463 | + p->bContent = 0; | |
| 173748 | 174464 | } |
| 173749 | 174465 | } |
| 173750 | 174466 | |
| 174467 | +/* | |
| 174468 | +** Add an entry to the in-memory hash table. The key is the concatenation | |
| 174469 | +** of bByte and (pToken/nToken). The value is (iRowid/iCol/iPos). | |
| 174470 | +** | |
| 174471 | +** (bByte || pToken) -> (iRowid,iCol,iPos) | |
| 174472 | +** | |
| 174473 | +** Or, if iCol is negative, then the value is a delete marker. | |
| 174474 | +*/ | |
| 173751 | 174475 | static int sqlite3Fts5HashWrite( |
| 173752 | 174476 | Fts5Hash *pHash, |
| 173753 | 174477 | i64 iRowid, /* Rowid for this entry */ |
| 173754 | 174478 | int iCol, /* Column token appears in (-ve -> delete) */ |
| 173755 | 174479 | int iPos, /* Position of token within column */ |
| @@ -173758,10 +174482,13 @@ | ||
| 173758 | 174482 | ){ |
| 173759 | 174483 | unsigned int iHash; |
| 173760 | 174484 | Fts5HashEntry *p; |
| 173761 | 174485 | u8 *pPtr; |
| 173762 | 174486 | int nIncr = 0; /* Amount to increment (*pHash->pnByte) by */ |
| 174487 | + int bNew; /* If non-delete entry should be written */ | |
| 174488 | + | |
| 174489 | + bNew = (pHash->eDetail==FTS5_DETAIL_FULL); | |
| 173763 | 174490 | |
| 173764 | 174491 | /* Attempt to locate an existing hash entry */ |
| 173765 | 174492 | iHash = fts5HashKey2(pHash->nSlot, (u8)bByte, (const u8*)pToken, nToken); |
| 173766 | 174493 | for(p=pHash->aSlot[iHash]; p; p=p->pHashNext){ |
| 173767 | 174494 | if( p->zKey[0]==bByte |
| @@ -173772,92 +174499,120 @@ | ||
| 173772 | 174499 | } |
| 173773 | 174500 | } |
| 173774 | 174501 | |
| 173775 | 174502 | /* If an existing hash entry cannot be found, create a new one. */ |
| 173776 | 174503 | if( p==0 ){ |
| 174504 | + /* Figure out how much space to allocate */ | |
| 173777 | 174505 | int nByte = FTS5_HASHENTRYSIZE + (nToken+1) + 1 + 64; |
| 173778 | 174506 | if( nByte<128 ) nByte = 128; |
| 173779 | 174507 | |
| 174508 | + /* Grow the Fts5Hash.aSlot[] array if necessary. */ | |
| 173780 | 174509 | if( (pHash->nEntry*2)>=pHash->nSlot ){ |
| 173781 | 174510 | int rc = fts5HashResize(pHash); |
| 173782 | 174511 | if( rc!=SQLITE_OK ) return rc; |
| 173783 | 174512 | iHash = fts5HashKey2(pHash->nSlot, (u8)bByte, (const u8*)pToken, nToken); |
| 173784 | 174513 | } |
| 173785 | 174514 | |
| 174515 | + /* Allocate new Fts5HashEntry and add it to the hash table. */ | |
| 173786 | 174516 | p = (Fts5HashEntry*)sqlite3_malloc(nByte); |
| 173787 | 174517 | if( !p ) return SQLITE_NOMEM; |
| 173788 | 174518 | memset(p, 0, FTS5_HASHENTRYSIZE); |
| 173789 | 174519 | p->nAlloc = nByte; |
| 173790 | 174520 | p->zKey[0] = bByte; |
| 173791 | 174521 | memcpy(&p->zKey[1], pToken, nToken); |
| 173792 | 174522 | assert( iHash==fts5HashKey(pHash->nSlot, (u8*)p->zKey, nToken+1) ); |
| 173793 | 174523 | p->zKey[nToken+1] = '\0'; |
| 173794 | 174524 | p->nData = nToken+1 + 1 + FTS5_HASHENTRYSIZE; |
| 173795 | - p->nData += sqlite3Fts5PutVarint(&((u8*)p)[p->nData], iRowid); | |
| 173796 | - p->iSzPoslist = p->nData; | |
| 173797 | - p->nData += 1; | |
| 173798 | - p->iRowid = iRowid; | |
| 173799 | 174525 | p->pHashNext = pHash->aSlot[iHash]; |
| 173800 | 174526 | pHash->aSlot[iHash] = p; |
| 173801 | 174527 | pHash->nEntry++; |
| 174528 | + | |
| 174529 | + /* Add the first rowid field to the hash-entry */ | |
| 174530 | + p->nData += sqlite3Fts5PutVarint(&((u8*)p)[p->nData], iRowid); | |
| 174531 | + p->iRowid = iRowid; | |
| 174532 | + | |
| 174533 | + p->iSzPoslist = p->nData; | |
| 174534 | + if( pHash->eDetail!=FTS5_DETAIL_NONE ){ | |
| 174535 | + p->nData += 1; | |
| 174536 | + p->iCol = (pHash->eDetail==FTS5_DETAIL_FULL ? 0 : -1); | |
| 174537 | + } | |
| 174538 | + | |
| 173802 | 174539 | nIncr += p->nData; |
| 173803 | - } | |
| 173804 | - | |
| 173805 | - /* Check there is enough space to append a new entry. Worst case scenario | |
| 173806 | - ** is: | |
| 173807 | - ** | |
| 173808 | - ** + 9 bytes for a new rowid, | |
| 173809 | - ** + 4 byte reserved for the "poslist size" varint. | |
| 173810 | - ** + 1 byte for a "new column" byte, | |
| 173811 | - ** + 3 bytes for a new column number (16-bit max) as a varint, | |
| 173812 | - ** + 5 bytes for the new position offset (32-bit max). | |
| 173813 | - */ | |
| 173814 | - if( (p->nAlloc - p->nData) < (9 + 4 + 1 + 3 + 5) ){ | |
| 173815 | - int nNew = p->nAlloc * 2; | |
| 173816 | - Fts5HashEntry *pNew; | |
| 173817 | - Fts5HashEntry **pp; | |
| 173818 | - pNew = (Fts5HashEntry*)sqlite3_realloc(p, nNew); | |
| 173819 | - if( pNew==0 ) return SQLITE_NOMEM; | |
| 173820 | - pNew->nAlloc = nNew; | |
| 173821 | - for(pp=&pHash->aSlot[iHash]; *pp!=p; pp=&(*pp)->pHashNext); | |
| 173822 | - *pp = pNew; | |
| 173823 | - p = pNew; | |
| 173824 | - } | |
| 173825 | - pPtr = (u8*)p; | |
| 173826 | - nIncr -= p->nData; | |
| 174540 | + }else{ | |
| 174541 | + | |
| 174542 | + /* Appending to an existing hash-entry. Check that there is enough | |
| 174543 | + ** space to append the largest possible new entry. Worst case scenario | |
| 174544 | + ** is: | |
| 174545 | + ** | |
| 174546 | + ** + 9 bytes for a new rowid, | |
| 174547 | + ** + 4 byte reserved for the "poslist size" varint. | |
| 174548 | + ** + 1 byte for a "new column" byte, | |
| 174549 | + ** + 3 bytes for a new column number (16-bit max) as a varint, | |
| 174550 | + ** + 5 bytes for the new position offset (32-bit max). | |
| 174551 | + */ | |
| 174552 | + if( (p->nAlloc - p->nData) < (9 + 4 + 1 + 3 + 5) ){ | |
| 174553 | + int nNew = p->nAlloc * 2; | |
| 174554 | + Fts5HashEntry *pNew; | |
| 174555 | + Fts5HashEntry **pp; | |
| 174556 | + pNew = (Fts5HashEntry*)sqlite3_realloc(p, nNew); | |
| 174557 | + if( pNew==0 ) return SQLITE_NOMEM; | |
| 174558 | + pNew->nAlloc = nNew; | |
| 174559 | + for(pp=&pHash->aSlot[iHash]; *pp!=p; pp=&(*pp)->pHashNext); | |
| 174560 | + *pp = pNew; | |
| 174561 | + p = pNew; | |
| 174562 | + } | |
| 174563 | + nIncr -= p->nData; | |
| 174564 | + } | |
| 174565 | + assert( (p->nAlloc - p->nData) >= (9 + 4 + 1 + 3 + 5) ); | |
| 174566 | + | |
| 174567 | + pPtr = (u8*)p; | |
| 173827 | 174568 | |
| 173828 | 174569 | /* If this is a new rowid, append the 4-byte size field for the previous |
| 173829 | 174570 | ** entry, and the new rowid for this entry. */ |
| 173830 | 174571 | if( iRowid!=p->iRowid ){ |
| 173831 | - fts5HashAddPoslistSize(p); | |
| 174572 | + fts5HashAddPoslistSize(pHash, p); | |
| 173832 | 174573 | p->nData += sqlite3Fts5PutVarint(&pPtr[p->nData], iRowid - p->iRowid); |
| 174574 | + p->iRowid = iRowid; | |
| 174575 | + bNew = 1; | |
| 173833 | 174576 | p->iSzPoslist = p->nData; |
| 173834 | - p->nData += 1; | |
| 173835 | - p->iCol = 0; | |
| 173836 | - p->iPos = 0; | |
| 173837 | - p->iRowid = iRowid; | |
| 174577 | + if( pHash->eDetail!=FTS5_DETAIL_NONE ){ | |
| 174578 | + p->nData += 1; | |
| 174579 | + p->iCol = (pHash->eDetail==FTS5_DETAIL_FULL ? 0 : -1); | |
| 174580 | + p->iPos = 0; | |
| 174581 | + } | |
| 173838 | 174582 | } |
| 173839 | 174583 | |
| 173840 | 174584 | if( iCol>=0 ){ |
| 173841 | - /* Append a new column value, if necessary */ | |
| 173842 | - assert( iCol>=p->iCol ); | |
| 173843 | - if( iCol!=p->iCol ){ | |
| 173844 | - pPtr[p->nData++] = 0x01; | |
| 173845 | - p->nData += sqlite3Fts5PutVarint(&pPtr[p->nData], iCol); | |
| 173846 | - p->iCol = iCol; | |
| 173847 | - p->iPos = 0; | |
| 173848 | - } | |
| 173849 | - | |
| 173850 | - /* Append the new position offset */ | |
| 173851 | - p->nData += sqlite3Fts5PutVarint(&pPtr[p->nData], iPos - p->iPos + 2); | |
| 173852 | - p->iPos = iPos; | |
| 174585 | + if( pHash->eDetail==FTS5_DETAIL_NONE ){ | |
| 174586 | + p->bContent = 1; | |
| 174587 | + }else{ | |
| 174588 | + /* Append a new column value, if necessary */ | |
| 174589 | + assert( iCol>=p->iCol ); | |
| 174590 | + if( iCol!=p->iCol ){ | |
| 174591 | + if( pHash->eDetail==FTS5_DETAIL_FULL ){ | |
| 174592 | + pPtr[p->nData++] = 0x01; | |
| 174593 | + p->nData += sqlite3Fts5PutVarint(&pPtr[p->nData], iCol); | |
| 174594 | + p->iCol = iCol; | |
| 174595 | + p->iPos = 0; | |
| 174596 | + }else{ | |
| 174597 | + bNew = 1; | |
| 174598 | + p->iCol = iPos = iCol; | |
| 174599 | + } | |
| 174600 | + } | |
| 174601 | + | |
| 174602 | + /* Append the new position offset, if necessary */ | |
| 174603 | + if( bNew ){ | |
| 174604 | + p->nData += sqlite3Fts5PutVarint(&pPtr[p->nData], iPos - p->iPos + 2); | |
| 174605 | + p->iPos = iPos; | |
| 174606 | + } | |
| 174607 | + } | |
| 173853 | 174608 | }else{ |
| 173854 | 174609 | /* This is a delete. Set the delete flag. */ |
| 173855 | 174610 | p->bDel = 1; |
| 173856 | 174611 | } |
| 174612 | + | |
| 173857 | 174613 | nIncr += p->nData; |
| 173858 | - | |
| 173859 | 174614 | *pHash->pnByte += nIncr; |
| 173860 | 174615 | return SQLITE_OK; |
| 173861 | 174616 | } |
| 173862 | 174617 | |
| 173863 | 174618 | |
| @@ -173967,11 +174722,11 @@ | ||
| 173967 | 174722 | for(p=pHash->aSlot[iHash]; p; p=p->pHashNext){ |
| 173968 | 174723 | if( memcmp(p->zKey, pTerm, nTerm)==0 && p->zKey[nTerm]==0 ) break; |
| 173969 | 174724 | } |
| 173970 | 174725 | |
| 173971 | 174726 | if( p ){ |
| 173972 | - fts5HashAddPoslistSize(p); | |
| 174727 | + fts5HashAddPoslistSize(pHash, p); | |
| 173973 | 174728 | *ppDoclist = (const u8*)&p->zKey[nTerm+1]; |
| 173974 | 174729 | *pnDoclist = p->nData - (FTS5_HASHENTRYSIZE + nTerm + 1); |
| 173975 | 174730 | }else{ |
| 173976 | 174731 | *ppDoclist = 0; |
| 173977 | 174732 | *pnDoclist = 0; |
| @@ -174003,11 +174758,11 @@ | ||
| 174003 | 174758 | int *pnDoclist /* OUT: size of doclist in bytes */ |
| 174004 | 174759 | ){ |
| 174005 | 174760 | Fts5HashEntry *p; |
| 174006 | 174761 | if( (p = pHash->pScan) ){ |
| 174007 | 174762 | int nTerm = (int)strlen(p->zKey); |
| 174008 | - fts5HashAddPoslistSize(p); | |
| 174763 | + fts5HashAddPoslistSize(pHash, p); | |
| 174009 | 174764 | *pzTerm = p->zKey; |
| 174010 | 174765 | *ppDoclist = (const u8*)&p->zKey[nTerm+1]; |
| 174011 | 174766 | *pnDoclist = p->nData - (FTS5_HASHENTRYSIZE + nTerm + 1); |
| 174012 | 174767 | }else{ |
| 174013 | 174768 | *pzTerm = 0; |
| @@ -174450,10 +175205,13 @@ | ||
| 174450 | 175205 | int iLeafPgno; /* Current leaf page number */ |
| 174451 | 175206 | Fts5Data *pLeaf; /* Current leaf data */ |
| 174452 | 175207 | Fts5Data *pNextLeaf; /* Leaf page (iLeafPgno+1) */ |
| 174453 | 175208 | int iLeafOffset; /* Byte offset within current leaf */ |
| 174454 | 175209 | |
| 175210 | + /* Next method */ | |
| 175211 | + void (*xNext)(Fts5Index*, Fts5SegIter*, int*); | |
| 175212 | + | |
| 174455 | 175213 | /* The page and offset from which the current term was read. The offset |
| 174456 | 175214 | ** is the offset of the first rowid in the current doclist. */ |
| 174457 | 175215 | int iTermLeafPgno; |
| 174458 | 175216 | int iTermLeafOffset; |
| 174459 | 175217 | |
| @@ -174469,11 +175227,11 @@ | ||
| 174469 | 175227 | |
| 174470 | 175228 | /* Variables populated based on current entry. */ |
| 174471 | 175229 | Fts5Buffer term; /* Current term */ |
| 174472 | 175230 | i64 iRowid; /* Current rowid */ |
| 174473 | 175231 | int nPos; /* Number of bytes in current position list */ |
| 174474 | - int bDel; /* True if the delete flag is set */ | |
| 175232 | + u8 bDel; /* True if the delete flag is set */ | |
| 174475 | 175233 | }; |
| 174476 | 175234 | |
| 174477 | 175235 | /* |
| 174478 | 175236 | ** Argument is a pointer to an Fts5Data structure that contains a |
| 174479 | 175237 | ** leaf page. |
| @@ -174482,11 +175240,10 @@ | ||
| 174482 | 175240 | (x)->szLeaf==(x)->nn || (x)->szLeaf==fts5GetU16(&(x)->p[2]) \ |
| 174483 | 175241 | ) |
| 174484 | 175242 | |
| 174485 | 175243 | #define FTS5_SEGITER_ONETERM 0x01 |
| 174486 | 175244 | #define FTS5_SEGITER_REVERSE 0x02 |
| 174487 | - | |
| 174488 | 175245 | |
| 174489 | 175246 | /* |
| 174490 | 175247 | ** Argument is a pointer to an Fts5Data structure that contains a leaf |
| 174491 | 175248 | ** page. This macro evaluates to true if the leaf contains no terms, or |
| 174492 | 175249 | ** false if it contains at least one term. |
| @@ -175509,17 +176266,33 @@ | ||
| 175509 | 176266 | ** position list content (if any). |
| 175510 | 176267 | */ |
| 175511 | 176268 | static void fts5SegIterLoadNPos(Fts5Index *p, Fts5SegIter *pIter){ |
| 175512 | 176269 | if( p->rc==SQLITE_OK ){ |
| 175513 | 176270 | int iOff = pIter->iLeafOffset; /* Offset to read at */ |
| 175514 | - int nSz; | |
| 175515 | 176271 | ASSERT_SZLEAF_OK(pIter->pLeaf); |
| 175516 | - fts5FastGetVarint32(pIter->pLeaf->p, iOff, nSz); | |
| 175517 | - pIter->bDel = (nSz & 0x0001); | |
| 175518 | - pIter->nPos = nSz>>1; | |
| 176272 | + if( p->pConfig->eDetail==FTS5_DETAIL_NONE ){ | |
| 176273 | + int iEod = MIN(pIter->iEndofDoclist, pIter->pLeaf->szLeaf); | |
| 176274 | + pIter->bDel = 0; | |
| 176275 | + pIter->nPos = 1; | |
| 176276 | + if( iOff<iEod && pIter->pLeaf->p[iOff]==0 ){ | |
| 176277 | + pIter->bDel = 1; | |
| 176278 | + iOff++; | |
| 176279 | + if( iOff<iEod && pIter->pLeaf->p[iOff]==0 ){ | |
| 176280 | + pIter->nPos = 1; | |
| 176281 | + iOff++; | |
| 176282 | + }else{ | |
| 176283 | + pIter->nPos = 0; | |
| 176284 | + } | |
| 176285 | + } | |
| 176286 | + }else{ | |
| 176287 | + int nSz; | |
| 176288 | + fts5FastGetVarint32(pIter->pLeaf->p, iOff, nSz); | |
| 176289 | + pIter->bDel = (nSz & 0x0001); | |
| 176290 | + pIter->nPos = nSz>>1; | |
| 176291 | + assert_nc( pIter->nPos>=0 ); | |
| 176292 | + } | |
| 175519 | 176293 | pIter->iLeafOffset = iOff; |
| 175520 | - assert_nc( pIter->nPos>=0 ); | |
| 175521 | 176294 | } |
| 175522 | 176295 | } |
| 175523 | 176296 | |
| 175524 | 176297 | static void fts5SegIterLoadRowid(Fts5Index *p, Fts5SegIter *pIter){ |
| 175525 | 176298 | u8 *a = pIter->pLeaf->p; /* Buffer to read data from */ |
| @@ -175575,10 +176348,24 @@ | ||
| 175575 | 176348 | pIter->iEndofDoclist += nExtra; |
| 175576 | 176349 | } |
| 175577 | 176350 | |
| 175578 | 176351 | fts5SegIterLoadRowid(p, pIter); |
| 175579 | 176352 | } |
| 176353 | + | |
| 176354 | +static void fts5SegIterNext(Fts5Index*, Fts5SegIter*, int*); | |
| 176355 | +static void fts5SegIterNext_Reverse(Fts5Index*, Fts5SegIter*, int*); | |
| 176356 | +static void fts5SegIterNext_None(Fts5Index*, Fts5SegIter*, int*); | |
| 176357 | + | |
| 176358 | +static void fts5SegIterSetNext(Fts5Index *p, Fts5SegIter *pIter){ | |
| 176359 | + if( pIter->flags & FTS5_SEGITER_REVERSE ){ | |
| 176360 | + pIter->xNext = fts5SegIterNext_Reverse; | |
| 176361 | + }else if( p->pConfig->eDetail==FTS5_DETAIL_NONE ){ | |
| 176362 | + pIter->xNext = fts5SegIterNext_None; | |
| 176363 | + }else{ | |
| 176364 | + pIter->xNext = fts5SegIterNext; | |
| 176365 | + } | |
| 176366 | +} | |
| 175580 | 176367 | |
| 175581 | 176368 | /* |
| 175582 | 176369 | ** Initialize the iterator object pIter to iterate through the entries in |
| 175583 | 176370 | ** segment pSeg. The iterator is left pointing to the first entry when |
| 175584 | 176371 | ** this function returns. |
| @@ -175601,10 +176388,11 @@ | ||
| 175601 | 176388 | return; |
| 175602 | 176389 | } |
| 175603 | 176390 | |
| 175604 | 176391 | if( p->rc==SQLITE_OK ){ |
| 175605 | 176392 | memset(pIter, 0, sizeof(*pIter)); |
| 176393 | + fts5SegIterSetNext(p, pIter); | |
| 175606 | 176394 | pIter->pSeg = pSeg; |
| 175607 | 176395 | pIter->iLeafPgno = pSeg->pgnoFirst-1; |
| 175608 | 176396 | fts5SegIterNextPage(p, pIter); |
| 175609 | 176397 | } |
| 175610 | 176398 | |
| @@ -175632,10 +176420,11 @@ | ||
| 175632 | 176420 | ** aRowidOffset[] and iRowidOffset variables. At this point the iterator |
| 175633 | 176421 | ** is in its regular state - Fts5SegIter.iLeafOffset points to the first |
| 175634 | 176422 | ** byte of the position list content associated with said rowid. |
| 175635 | 176423 | */ |
| 175636 | 176424 | static void fts5SegIterReverseInitPage(Fts5Index *p, Fts5SegIter *pIter){ |
| 176425 | + int eDetail = p->pConfig->eDetail; | |
| 175637 | 176426 | int n = pIter->pLeaf->szLeaf; |
| 175638 | 176427 | int i = pIter->iLeafOffset; |
| 175639 | 176428 | u8 *a = pIter->pLeaf->p; |
| 175640 | 176429 | int iRowidOffset = 0; |
| 175641 | 176430 | |
| @@ -175644,19 +176433,28 @@ | ||
| 175644 | 176433 | } |
| 175645 | 176434 | |
| 175646 | 176435 | ASSERT_SZLEAF_OK(pIter->pLeaf); |
| 175647 | 176436 | while( 1 ){ |
| 175648 | 176437 | i64 iDelta = 0; |
| 175649 | - int nPos; | |
| 175650 | - int bDummy; | |
| 175651 | 176438 | |
| 175652 | - i += fts5GetPoslistSize(&a[i], &nPos, &bDummy); | |
| 175653 | - i += nPos; | |
| 176439 | + if( eDetail==FTS5_DETAIL_NONE ){ | |
| 176440 | + /* todo */ | |
| 176441 | + if( i<n && a[i]==0 ){ | |
| 176442 | + i++; | |
| 176443 | + if( i<n && a[i]==0 ) i++; | |
| 176444 | + } | |
| 176445 | + }else{ | |
| 176446 | + int nPos; | |
| 176447 | + int bDummy; | |
| 176448 | + i += fts5GetPoslistSize(&a[i], &nPos, &bDummy); | |
| 176449 | + i += nPos; | |
| 176450 | + } | |
| 175654 | 176451 | if( i>=n ) break; |
| 175655 | 176452 | i += fts5GetVarint(&a[i], (u64*)&iDelta); |
| 175656 | 176453 | pIter->iRowid += iDelta; |
| 175657 | 176454 | |
| 176455 | + /* If necessary, grow the pIter->aRowidOffset[] array. */ | |
| 175658 | 176456 | if( iRowidOffset>=pIter->nRowidOffset ){ |
| 175659 | 176457 | int nNew = pIter->nRowidOffset + 8; |
| 175660 | 176458 | int *aNew = (int*)sqlite3_realloc(pIter->aRowidOffset, nNew*sizeof(int)); |
| 175661 | 176459 | if( aNew==0 ){ |
| 175662 | 176460 | p->rc = SQLITE_NOMEM; |
| @@ -175730,10 +176528,112 @@ | ||
| 175730 | 176528 | */ |
| 175731 | 176529 | static int fts5MultiIterIsEmpty(Fts5Index *p, Fts5IndexIter *pIter){ |
| 175732 | 176530 | Fts5SegIter *pSeg = &pIter->aSeg[pIter->aFirst[1].iFirst]; |
| 175733 | 176531 | return (p->rc==SQLITE_OK && pSeg->pLeaf && pSeg->nPos==0); |
| 175734 | 176532 | } |
| 176533 | + | |
| 176534 | +/* | |
| 176535 | +** Advance iterator pIter to the next entry. | |
| 176536 | +** | |
| 176537 | +** This version of fts5SegIterNext() is only used by reverse iterators. | |
| 176538 | +*/ | |
| 176539 | +static void fts5SegIterNext_Reverse( | |
| 176540 | + Fts5Index *p, /* FTS5 backend object */ | |
| 176541 | + Fts5SegIter *pIter, /* Iterator to advance */ | |
| 176542 | + int *pbNewTerm /* OUT: Set for new term */ | |
| 176543 | +){ | |
| 176544 | + assert( pIter->flags & FTS5_SEGITER_REVERSE ); | |
| 176545 | + assert( pIter->pNextLeaf==0 ); | |
| 176546 | + if( pIter->iRowidOffset>0 ){ | |
| 176547 | + u8 *a = pIter->pLeaf->p; | |
| 176548 | + int iOff; | |
| 176549 | + i64 iDelta; | |
| 176550 | + | |
| 176551 | + pIter->iRowidOffset--; | |
| 176552 | + pIter->iLeafOffset = pIter->aRowidOffset[pIter->iRowidOffset]; | |
| 176553 | + fts5SegIterLoadNPos(p, pIter); | |
| 176554 | + iOff = pIter->iLeafOffset; | |
| 176555 | + if( p->pConfig->eDetail!=FTS5_DETAIL_NONE ){ | |
| 176556 | + iOff += pIter->nPos; | |
| 176557 | + } | |
| 176558 | + fts5GetVarint(&a[iOff], (u64*)&iDelta); | |
| 176559 | + pIter->iRowid -= iDelta; | |
| 176560 | + }else{ | |
| 176561 | + fts5SegIterReverseNewPage(p, pIter); | |
| 176562 | + } | |
| 176563 | +} | |
| 176564 | + | |
| 176565 | +/* | |
| 176566 | +** Advance iterator pIter to the next entry. | |
| 176567 | +** | |
| 176568 | +** This version of fts5SegIterNext() is only used if detail=none and the | |
| 176569 | +** iterator is not a reverse direction iterator. | |
| 176570 | +*/ | |
| 176571 | +static void fts5SegIterNext_None( | |
| 176572 | + Fts5Index *p, /* FTS5 backend object */ | |
| 176573 | + Fts5SegIter *pIter, /* Iterator to advance */ | |
| 176574 | + int *pbNewTerm /* OUT: Set for new term */ | |
| 176575 | +){ | |
| 176576 | + int iOff; | |
| 176577 | + | |
| 176578 | + assert( p->rc==SQLITE_OK ); | |
| 176579 | + assert( (pIter->flags & FTS5_SEGITER_REVERSE)==0 ); | |
| 176580 | + assert( p->pConfig->eDetail==FTS5_DETAIL_NONE ); | |
| 176581 | + | |
| 176582 | + ASSERT_SZLEAF_OK(pIter->pLeaf); | |
| 176583 | + iOff = pIter->iLeafOffset; | |
| 176584 | + | |
| 176585 | + /* Next entry is on the next page */ | |
| 176586 | + if( pIter->pSeg && iOff>=pIter->pLeaf->szLeaf ){ | |
| 176587 | + fts5SegIterNextPage(p, pIter); | |
| 176588 | + if( p->rc || pIter->pLeaf==0 ) return; | |
| 176589 | + pIter->iRowid = 0; | |
| 176590 | + iOff = 4; | |
| 176591 | + } | |
| 176592 | + | |
| 176593 | + if( iOff<pIter->iEndofDoclist ){ | |
| 176594 | + /* Next entry is on the current page */ | |
| 176595 | + i64 iDelta; | |
| 176596 | + iOff += sqlite3Fts5GetVarint(&pIter->pLeaf->p[iOff], (u64*)&iDelta); | |
| 176597 | + pIter->iLeafOffset = iOff; | |
| 176598 | + pIter->iRowid += iDelta; | |
| 176599 | + }else if( (pIter->flags & FTS5_SEGITER_ONETERM)==0 ){ | |
| 176600 | + if( pIter->pSeg ){ | |
| 176601 | + int nKeep = 0; | |
| 176602 | + if( iOff!=fts5LeafFirstTermOff(pIter->pLeaf) ){ | |
| 176603 | + iOff += fts5GetVarint32(&pIter->pLeaf->p[iOff], nKeep); | |
| 176604 | + } | |
| 176605 | + pIter->iLeafOffset = iOff; | |
| 176606 | + fts5SegIterLoadTerm(p, pIter, nKeep); | |
| 176607 | + }else{ | |
| 176608 | + const u8 *pList = 0; | |
| 176609 | + const char *zTerm = 0; | |
| 176610 | + int nList; | |
| 176611 | + sqlite3Fts5HashScanNext(p->pHash); | |
| 176612 | + sqlite3Fts5HashScanEntry(p->pHash, &zTerm, &pList, &nList); | |
| 176613 | + if( pList==0 ) goto next_none_eof; | |
| 176614 | + pIter->pLeaf->p = (u8*)pList; | |
| 176615 | + pIter->pLeaf->nn = nList; | |
| 176616 | + pIter->pLeaf->szLeaf = nList; | |
| 176617 | + pIter->iEndofDoclist = nList; | |
| 176618 | + sqlite3Fts5BufferSet(&p->rc,&pIter->term, (int)strlen(zTerm), (u8*)zTerm); | |
| 176619 | + pIter->iLeafOffset = fts5GetVarint(pList, (u64*)&pIter->iRowid); | |
| 176620 | + } | |
| 176621 | + | |
| 176622 | + if( pbNewTerm ) *pbNewTerm = 1; | |
| 176623 | + }else{ | |
| 176624 | + goto next_none_eof; | |
| 176625 | + } | |
| 176626 | + | |
| 176627 | + fts5SegIterLoadNPos(p, pIter); | |
| 176628 | + | |
| 176629 | + return; | |
| 176630 | + next_none_eof: | |
| 176631 | + fts5DataRelease(pIter->pLeaf); | |
| 176632 | + pIter->pLeaf = 0; | |
| 176633 | +} | |
| 176634 | + | |
| 175735 | 176635 | |
| 175736 | 176636 | /* |
| 175737 | 176637 | ** Advance iterator pIter to the next entry. |
| 175738 | 176638 | ** |
| 175739 | 176639 | ** If an error occurs, Fts5Index.rc is set to an appropriate error code. It |
| @@ -175743,144 +176643,133 @@ | ||
| 175743 | 176643 | static void fts5SegIterNext( |
| 175744 | 176644 | Fts5Index *p, /* FTS5 backend object */ |
| 175745 | 176645 | Fts5SegIter *pIter, /* Iterator to advance */ |
| 175746 | 176646 | int *pbNewTerm /* OUT: Set for new term */ |
| 175747 | 176647 | ){ |
| 175748 | - assert( pbNewTerm==0 || *pbNewTerm==0 ); | |
| 175749 | - if( p->rc==SQLITE_OK ){ | |
| 175750 | - if( pIter->flags & FTS5_SEGITER_REVERSE ){ | |
| 175751 | - assert( pIter->pNextLeaf==0 ); | |
| 175752 | - if( pIter->iRowidOffset>0 ){ | |
| 175753 | - u8 *a = pIter->pLeaf->p; | |
| 175754 | - int iOff; | |
| 175755 | - int nPos; | |
| 175756 | - int bDummy; | |
| 175757 | - i64 iDelta; | |
| 175758 | - | |
| 175759 | - pIter->iRowidOffset--; | |
| 175760 | - pIter->iLeafOffset = iOff = pIter->aRowidOffset[pIter->iRowidOffset]; | |
| 175761 | - iOff += fts5GetPoslistSize(&a[iOff], &nPos, &bDummy); | |
| 175762 | - iOff += nPos; | |
| 175763 | - fts5GetVarint(&a[iOff], (u64*)&iDelta); | |
| 175764 | - pIter->iRowid -= iDelta; | |
| 175765 | - fts5SegIterLoadNPos(p, pIter); | |
| 175766 | - }else{ | |
| 175767 | - fts5SegIterReverseNewPage(p, pIter); | |
| 175768 | - } | |
| 175769 | - }else{ | |
| 175770 | - Fts5Data *pLeaf = pIter->pLeaf; | |
| 175771 | - int iOff; | |
| 175772 | - int bNewTerm = 0; | |
| 175773 | - int nKeep = 0; | |
| 175774 | - | |
| 175775 | - /* Search for the end of the position list within the current page. */ | |
| 175776 | - u8 *a = pLeaf->p; | |
| 175777 | - int n = pLeaf->szLeaf; | |
| 175778 | - | |
| 175779 | - ASSERT_SZLEAF_OK(pLeaf); | |
| 175780 | - iOff = pIter->iLeafOffset + pIter->nPos; | |
| 175781 | - | |
| 175782 | - if( iOff<n ){ | |
| 175783 | - /* The next entry is on the current page. */ | |
| 175784 | - assert_nc( iOff<=pIter->iEndofDoclist ); | |
| 175785 | - if( iOff>=pIter->iEndofDoclist ){ | |
| 175786 | - bNewTerm = 1; | |
| 175787 | - if( iOff!=fts5LeafFirstTermOff(pLeaf) ){ | |
| 175788 | - iOff += fts5GetVarint32(&a[iOff], nKeep); | |
| 175789 | - } | |
| 175790 | - }else{ | |
| 175791 | - u64 iDelta; | |
| 175792 | - iOff += sqlite3Fts5GetVarint(&a[iOff], &iDelta); | |
| 175793 | - pIter->iRowid += iDelta; | |
| 175794 | - assert_nc( iDelta>0 ); | |
| 175795 | - } | |
| 175796 | - pIter->iLeafOffset = iOff; | |
| 175797 | - | |
| 175798 | - }else if( pIter->pSeg==0 ){ | |
| 175799 | - const u8 *pList = 0; | |
| 175800 | - const char *zTerm = 0; | |
| 175801 | - int nList = 0; | |
| 175802 | - assert( (pIter->flags & FTS5_SEGITER_ONETERM) || pbNewTerm ); | |
| 175803 | - if( 0==(pIter->flags & FTS5_SEGITER_ONETERM) ){ | |
| 175804 | - sqlite3Fts5HashScanNext(p->pHash); | |
| 175805 | - sqlite3Fts5HashScanEntry(p->pHash, &zTerm, &pList, &nList); | |
| 175806 | - } | |
| 175807 | - if( pList==0 ){ | |
| 175808 | - fts5DataRelease(pIter->pLeaf); | |
| 175809 | - pIter->pLeaf = 0; | |
| 175810 | - }else{ | |
| 175811 | - pIter->pLeaf->p = (u8*)pList; | |
| 175812 | - pIter->pLeaf->nn = nList; | |
| 175813 | - pIter->pLeaf->szLeaf = nList; | |
| 175814 | - pIter->iEndofDoclist = nList+1; | |
| 175815 | - sqlite3Fts5BufferSet(&p->rc, &pIter->term, (int)strlen(zTerm), | |
| 175816 | - (u8*)zTerm); | |
| 175817 | - pIter->iLeafOffset = fts5GetVarint(pList, (u64*)&pIter->iRowid); | |
| 175818 | - *pbNewTerm = 1; | |
| 175819 | - } | |
| 175820 | - }else{ | |
| 175821 | - iOff = 0; | |
| 175822 | - /* Next entry is not on the current page */ | |
| 175823 | - while( iOff==0 ){ | |
| 175824 | - fts5SegIterNextPage(p, pIter); | |
| 175825 | - pLeaf = pIter->pLeaf; | |
| 175826 | - if( pLeaf==0 ) break; | |
| 175827 | - ASSERT_SZLEAF_OK(pLeaf); | |
| 175828 | - if( (iOff = fts5LeafFirstRowidOff(pLeaf)) && iOff<pLeaf->szLeaf ){ | |
| 175829 | - iOff += sqlite3Fts5GetVarint(&pLeaf->p[iOff], (u64*)&pIter->iRowid); | |
| 175830 | - pIter->iLeafOffset = iOff; | |
| 175831 | - | |
| 175832 | - if( pLeaf->nn>pLeaf->szLeaf ){ | |
| 175833 | - pIter->iPgidxOff = pLeaf->szLeaf + fts5GetVarint32( | |
| 175834 | - &pLeaf->p[pLeaf->szLeaf], pIter->iEndofDoclist | |
| 175835 | - ); | |
| 175836 | - } | |
| 175837 | - | |
| 175838 | - } | |
| 175839 | - else if( pLeaf->nn>pLeaf->szLeaf ){ | |
| 175840 | - pIter->iPgidxOff = pLeaf->szLeaf + fts5GetVarint32( | |
| 175841 | - &pLeaf->p[pLeaf->szLeaf], iOff | |
| 175842 | - ); | |
| 175843 | - pIter->iLeafOffset = iOff; | |
| 175844 | - pIter->iEndofDoclist = iOff; | |
| 175845 | - bNewTerm = 1; | |
| 175846 | - } | |
| 175847 | - if( iOff>=pLeaf->szLeaf ){ | |
| 175848 | - p->rc = FTS5_CORRUPT; | |
| 175849 | - return; | |
| 175850 | - } | |
| 175851 | - } | |
| 175852 | - } | |
| 175853 | - | |
| 175854 | - /* Check if the iterator is now at EOF. If so, return early. */ | |
| 175855 | - if( pIter->pLeaf ){ | |
| 175856 | - if( bNewTerm ){ | |
| 175857 | - if( pIter->flags & FTS5_SEGITER_ONETERM ){ | |
| 175858 | - fts5DataRelease(pIter->pLeaf); | |
| 175859 | - pIter->pLeaf = 0; | |
| 175860 | - }else{ | |
| 175861 | - fts5SegIterLoadTerm(p, pIter, nKeep); | |
| 175862 | - fts5SegIterLoadNPos(p, pIter); | |
| 175863 | - if( pbNewTerm ) *pbNewTerm = 1; | |
| 175864 | - } | |
| 175865 | - }else{ | |
| 175866 | - /* The following could be done by calling fts5SegIterLoadNPos(). But | |
| 175867 | - ** this block is particularly performance critical, so equivalent | |
| 175868 | - ** code is inlined. */ | |
| 175869 | - int nSz; | |
| 175870 | - assert( p->rc==SQLITE_OK ); | |
| 175871 | - fts5FastGetVarint32(pIter->pLeaf->p, pIter->iLeafOffset, nSz); | |
| 175872 | - pIter->bDel = (nSz & 0x0001); | |
| 175873 | - pIter->nPos = nSz>>1; | |
| 175874 | - assert_nc( pIter->nPos>=0 ); | |
| 175875 | - } | |
| 175876 | - } | |
| 176648 | + Fts5Data *pLeaf = pIter->pLeaf; | |
| 176649 | + int iOff; | |
| 176650 | + int bNewTerm = 0; | |
| 176651 | + int nKeep = 0; | |
| 176652 | + | |
| 176653 | + assert( pbNewTerm==0 || *pbNewTerm==0 ); | |
| 176654 | + assert( p->pConfig->eDetail!=FTS5_DETAIL_NONE ); | |
| 176655 | + | |
| 176656 | + /* Search for the end of the position list within the current page. */ | |
| 176657 | + u8 *a = pLeaf->p; | |
| 176658 | + int n = pLeaf->szLeaf; | |
| 176659 | + | |
| 176660 | + ASSERT_SZLEAF_OK(pLeaf); | |
| 176661 | + iOff = pIter->iLeafOffset + pIter->nPos; | |
| 176662 | + | |
| 176663 | + if( iOff<n ){ | |
| 176664 | + /* The next entry is on the current page. */ | |
| 176665 | + assert_nc( iOff<=pIter->iEndofDoclist ); | |
| 176666 | + if( iOff>=pIter->iEndofDoclist ){ | |
| 176667 | + bNewTerm = 1; | |
| 176668 | + if( iOff!=fts5LeafFirstTermOff(pLeaf) ){ | |
| 176669 | + iOff += fts5GetVarint32(&a[iOff], nKeep); | |
| 176670 | + } | |
| 176671 | + }else{ | |
| 176672 | + u64 iDelta; | |
| 176673 | + iOff += sqlite3Fts5GetVarint(&a[iOff], &iDelta); | |
| 176674 | + pIter->iRowid += iDelta; | |
| 176675 | + assert_nc( iDelta>0 ); | |
| 176676 | + } | |
| 176677 | + pIter->iLeafOffset = iOff; | |
| 176678 | + | |
| 176679 | + }else if( pIter->pSeg==0 ){ | |
| 176680 | + const u8 *pList = 0; | |
| 176681 | + const char *zTerm = 0; | |
| 176682 | + int nList = 0; | |
| 176683 | + assert( (pIter->flags & FTS5_SEGITER_ONETERM) || pbNewTerm ); | |
| 176684 | + if( 0==(pIter->flags & FTS5_SEGITER_ONETERM) ){ | |
| 176685 | + sqlite3Fts5HashScanNext(p->pHash); | |
| 176686 | + sqlite3Fts5HashScanEntry(p->pHash, &zTerm, &pList, &nList); | |
| 176687 | + } | |
| 176688 | + if( pList==0 ){ | |
| 176689 | + fts5DataRelease(pIter->pLeaf); | |
| 176690 | + pIter->pLeaf = 0; | |
| 176691 | + }else{ | |
| 176692 | + pIter->pLeaf->p = (u8*)pList; | |
| 176693 | + pIter->pLeaf->nn = nList; | |
| 176694 | + pIter->pLeaf->szLeaf = nList; | |
| 176695 | + pIter->iEndofDoclist = nList+1; | |
| 176696 | + sqlite3Fts5BufferSet(&p->rc, &pIter->term, (int)strlen(zTerm), | |
| 176697 | + (u8*)zTerm); | |
| 176698 | + pIter->iLeafOffset = fts5GetVarint(pList, (u64*)&pIter->iRowid); | |
| 176699 | + *pbNewTerm = 1; | |
| 176700 | + } | |
| 176701 | + }else{ | |
| 176702 | + iOff = 0; | |
| 176703 | + /* Next entry is not on the current page */ | |
| 176704 | + while( iOff==0 ){ | |
| 176705 | + fts5SegIterNextPage(p, pIter); | |
| 176706 | + pLeaf = pIter->pLeaf; | |
| 176707 | + if( pLeaf==0 ) break; | |
| 176708 | + ASSERT_SZLEAF_OK(pLeaf); | |
| 176709 | + if( (iOff = fts5LeafFirstRowidOff(pLeaf)) && iOff<pLeaf->szLeaf ){ | |
| 176710 | + iOff += sqlite3Fts5GetVarint(&pLeaf->p[iOff], (u64*)&pIter->iRowid); | |
| 176711 | + pIter->iLeafOffset = iOff; | |
| 176712 | + | |
| 176713 | + if( pLeaf->nn>pLeaf->szLeaf ){ | |
| 176714 | + pIter->iPgidxOff = pLeaf->szLeaf + fts5GetVarint32( | |
| 176715 | + &pLeaf->p[pLeaf->szLeaf], pIter->iEndofDoclist | |
| 176716 | + ); | |
| 176717 | + } | |
| 176718 | + | |
| 176719 | + } | |
| 176720 | + else if( pLeaf->nn>pLeaf->szLeaf ){ | |
| 176721 | + pIter->iPgidxOff = pLeaf->szLeaf + fts5GetVarint32( | |
| 176722 | + &pLeaf->p[pLeaf->szLeaf], iOff | |
| 176723 | + ); | |
| 176724 | + pIter->iLeafOffset = iOff; | |
| 176725 | + pIter->iEndofDoclist = iOff; | |
| 176726 | + bNewTerm = 1; | |
| 176727 | + } | |
| 176728 | + assert_nc( iOff<pLeaf->szLeaf ); | |
| 176729 | + if( iOff>pLeaf->szLeaf ){ | |
| 176730 | + p->rc = FTS5_CORRUPT; | |
| 176731 | + return; | |
| 176732 | + } | |
| 176733 | + } | |
| 176734 | + } | |
| 176735 | + | |
| 176736 | + /* Check if the iterator is now at EOF. If so, return early. */ | |
| 176737 | + if( pIter->pLeaf ){ | |
| 176738 | + if( bNewTerm ){ | |
| 176739 | + if( pIter->flags & FTS5_SEGITER_ONETERM ){ | |
| 176740 | + fts5DataRelease(pIter->pLeaf); | |
| 176741 | + pIter->pLeaf = 0; | |
| 176742 | + }else{ | |
| 176743 | + fts5SegIterLoadTerm(p, pIter, nKeep); | |
| 176744 | + fts5SegIterLoadNPos(p, pIter); | |
| 176745 | + if( pbNewTerm ) *pbNewTerm = 1; | |
| 176746 | + } | |
| 176747 | + }else{ | |
| 176748 | + /* The following could be done by calling fts5SegIterLoadNPos(). But | |
| 176749 | + ** this block is particularly performance critical, so equivalent | |
| 176750 | + ** code is inlined. | |
| 176751 | + ** | |
| 176752 | + ** Later: Switched back to fts5SegIterLoadNPos() because it supports | |
| 176753 | + ** detail=none mode. Not ideal. | |
| 176754 | + */ | |
| 176755 | + int nSz; | |
| 176756 | + assert( p->rc==SQLITE_OK ); | |
| 176757 | + fts5FastGetVarint32(pIter->pLeaf->p, pIter->iLeafOffset, nSz); | |
| 176758 | + pIter->bDel = (nSz & 0x0001); | |
| 176759 | + pIter->nPos = nSz>>1; | |
| 176760 | + assert_nc( pIter->nPos>=0 ); | |
| 175877 | 176761 | } |
| 175878 | 176762 | } |
| 175879 | 176763 | } |
| 175880 | 176764 | |
| 175881 | 176765 | #define SWAPVAL(T, a, b) { T tmp; tmp=a; a=b; b=tmp; } |
| 176766 | + | |
| 176767 | +#define fts5IndexSkipVarint(a, iOff) { \ | |
| 176768 | + int iEnd = iOff+9; \ | |
| 176769 | + while( (a[iOff++] & 0x80) && iOff<iEnd ); \ | |
| 176770 | +} | |
| 175882 | 176771 | |
| 175883 | 176772 | /* |
| 175884 | 176773 | ** Iterator pIter currently points to the first rowid in a doclist. This |
| 175885 | 176774 | ** function sets the iterator up so that iterates in reverse order through |
| 175886 | 176775 | ** the doclist. |
| @@ -175898,11 +176787,21 @@ | ||
| 175898 | 176787 | Fts5Data *pLeaf = pIter->pLeaf; /* Current leaf data */ |
| 175899 | 176788 | |
| 175900 | 176789 | /* Currently, Fts5SegIter.iLeafOffset points to the first byte of |
| 175901 | 176790 | ** position-list content for the current rowid. Back it up so that it |
| 175902 | 176791 | ** points to the start of the position-list size field. */ |
| 175903 | - pIter->iLeafOffset -= sqlite3Fts5GetVarintLen(pIter->nPos*2+pIter->bDel); | |
| 176792 | + int iPoslist; | |
| 176793 | + if( pIter->iTermLeafPgno==pIter->iLeafPgno ){ | |
| 176794 | + iPoslist = pIter->iTermLeafOffset; | |
| 176795 | + }else{ | |
| 176796 | + iPoslist = 4; | |
| 176797 | + } | |
| 176798 | + fts5IndexSkipVarint(pLeaf->p, iPoslist); | |
| 176799 | + assert( p->pConfig->eDetail==FTS5_DETAIL_NONE || iPoslist==( | |
| 176800 | + pIter->iLeafOffset - sqlite3Fts5GetVarintLen(pIter->nPos*2+pIter->bDel) | |
| 176801 | + )); | |
| 176802 | + pIter->iLeafOffset = iPoslist; | |
| 175904 | 176803 | |
| 175905 | 176804 | /* If this condition is true then the largest rowid for the current |
| 175906 | 176805 | ** term may not be stored on the current page. So search forward to |
| 175907 | 176806 | ** see where said rowid really is. */ |
| 175908 | 176807 | if( pIter->iEndofDoclist>=pLeaf->szLeaf ){ |
| @@ -175982,15 +176881,10 @@ | ||
| 175982 | 176881 | } |
| 175983 | 176882 | |
| 175984 | 176883 | pIter->pDlidx = fts5DlidxIterInit(p, bRev, iSeg, pIter->iTermLeafPgno); |
| 175985 | 176884 | } |
| 175986 | 176885 | |
| 175987 | -#define fts5IndexSkipVarint(a, iOff) { \ | |
| 175988 | - int iEnd = iOff+9; \ | |
| 175989 | - while( (a[iOff++] & 0x80) && iOff<iEnd ); \ | |
| 175990 | -} | |
| 175991 | - | |
| 175992 | 176886 | /* |
| 175993 | 176887 | ** The iterator object passed as the second argument currently contains |
| 175994 | 176888 | ** no valid values except for the Fts5SegIter.pLeaf member variable. This |
| 175995 | 176889 | ** function searches the leaf page for a term matching (pTerm/nTerm). |
| 175996 | 176890 | ** |
| @@ -176189,10 +177083,12 @@ | ||
| 176189 | 177083 | fts5SegIterReverse(p, pIter); |
| 176190 | 177084 | } |
| 176191 | 177085 | } |
| 176192 | 177086 | } |
| 176193 | 177087 | |
| 177088 | + fts5SegIterSetNext(p, pIter); | |
| 177089 | + | |
| 176194 | 177090 | /* Either: |
| 176195 | 177091 | ** |
| 176196 | 177092 | ** 1) an error has occurred, or |
| 176197 | 177093 | ** 2) the iterator points to EOF, or |
| 176198 | 177094 | ** 3) the iterator points to an entry with term (pTerm/nTerm), or |
| @@ -176246,19 +177142,21 @@ | ||
| 176246 | 177142 | if( pLeaf==0 ) return; |
| 176247 | 177143 | pLeaf->p = (u8*)pList; |
| 176248 | 177144 | pLeaf->nn = pLeaf->szLeaf = nList; |
| 176249 | 177145 | pIter->pLeaf = pLeaf; |
| 176250 | 177146 | pIter->iLeafOffset = fts5GetVarint(pLeaf->p, (u64*)&pIter->iRowid); |
| 176251 | - pIter->iEndofDoclist = pLeaf->nn+1; | |
| 177147 | + pIter->iEndofDoclist = pLeaf->nn; | |
| 176252 | 177148 | |
| 176253 | 177149 | if( flags & FTS5INDEX_QUERY_DESC ){ |
| 176254 | 177150 | pIter->flags |= FTS5_SEGITER_REVERSE; |
| 176255 | 177151 | fts5SegIterReverseInitPage(p, pIter); |
| 176256 | 177152 | }else{ |
| 176257 | 177153 | fts5SegIterLoadNPos(p, pIter); |
| 176258 | 177154 | } |
| 176259 | 177155 | } |
| 177156 | + | |
| 177157 | + fts5SegIterSetNext(p, pIter); | |
| 176260 | 177158 | } |
| 176261 | 177159 | |
| 176262 | 177160 | /* |
| 176263 | 177161 | ** Zero the iterator passed as the only argument. |
| 176264 | 177162 | */ |
| @@ -176498,11 +177396,11 @@ | ||
| 176498 | 177396 | bMove = 0; |
| 176499 | 177397 | } |
| 176500 | 177398 | } |
| 176501 | 177399 | |
| 176502 | 177400 | do{ |
| 176503 | - if( bMove ) fts5SegIterNext(p, pIter, 0); | |
| 177401 | + if( bMove && p->rc==SQLITE_OK ) pIter->xNext(p, pIter, 0); | |
| 176504 | 177402 | if( pIter->pLeaf==0 ) break; |
| 176505 | 177403 | if( bRev==0 && pIter->iRowid>=iMatch ) break; |
| 176506 | 177404 | if( bRev!=0 && pIter->iRowid<=iMatch ) break; |
| 176507 | 177405 | bMove = 1; |
| 176508 | 177406 | }while( p->rc==SQLITE_OK ); |
| @@ -176532,11 +177430,13 @@ | ||
| 176532 | 177430 | ){ |
| 176533 | 177431 | int i; |
| 176534 | 177432 | for(i=(pIter->nSeg+iChanged)/2; i>=iMinset && p->rc==SQLITE_OK; i=i/2){ |
| 176535 | 177433 | int iEq; |
| 176536 | 177434 | if( (iEq = fts5MultiIterDoCompare(pIter, i)) ){ |
| 176537 | - fts5SegIterNext(p, &pIter->aSeg[iEq], 0); | |
| 177435 | + Fts5SegIter *pSeg = &pIter->aSeg[iEq]; | |
| 177436 | + assert( p->rc==SQLITE_OK ); | |
| 177437 | + pSeg->xNext(p, pSeg, 0); | |
| 176538 | 177438 | i = pIter->nSeg + iEq; |
| 176539 | 177439 | } |
| 176540 | 177440 | } |
| 176541 | 177441 | } |
| 176542 | 177442 | |
| @@ -176619,11 +177519,11 @@ | ||
| 176619 | 177519 | Fts5SegIter *pSeg = &pIter->aSeg[iFirst]; |
| 176620 | 177520 | assert( p->rc==SQLITE_OK ); |
| 176621 | 177521 | if( bUseFrom && pSeg->pDlidx ){ |
| 176622 | 177522 | fts5SegIterNextFrom(p, pSeg, iFrom); |
| 176623 | 177523 | }else{ |
| 176624 | - fts5SegIterNext(p, pSeg, &bNewTerm); | |
| 177524 | + pSeg->xNext(p, pSeg, &bNewTerm); | |
| 176625 | 177525 | } |
| 176626 | 177526 | |
| 176627 | 177527 | if( pSeg->pLeaf==0 || bNewTerm |
| 176628 | 177528 | || fts5MultiIterAdvanceRowid(p, pIter, iFirst) |
| 176629 | 177529 | ){ |
| @@ -176647,11 +177547,12 @@ | ||
| 176647 | 177547 | do { |
| 176648 | 177548 | int iFirst = pIter->aFirst[1].iFirst; |
| 176649 | 177549 | Fts5SegIter *pSeg = &pIter->aSeg[iFirst]; |
| 176650 | 177550 | int bNewTerm = 0; |
| 176651 | 177551 | |
| 176652 | - fts5SegIterNext(p, pSeg, &bNewTerm); | |
| 177552 | + assert( p->rc==SQLITE_OK ); | |
| 177553 | + pSeg->xNext(p, pSeg, &bNewTerm); | |
| 176653 | 177554 | if( pSeg->pLeaf==0 || bNewTerm |
| 176654 | 177555 | || fts5MultiIterAdvanceRowid(p, pIter, iFirst) |
| 176655 | 177556 | ){ |
| 176656 | 177557 | fts5MultiIterAdvanced(p, pIter, iFirst, 1); |
| 176657 | 177558 | fts5MultiIterSetEof(pIter); |
| @@ -176767,11 +177668,12 @@ | ||
| 176767 | 177668 | ** object and set the output variable to NULL. */ |
| 176768 | 177669 | if( p->rc==SQLITE_OK ){ |
| 176769 | 177670 | for(iIter=pNew->nSeg-1; iIter>0; iIter--){ |
| 176770 | 177671 | int iEq; |
| 176771 | 177672 | if( (iEq = fts5MultiIterDoCompare(pNew, iIter)) ){ |
| 176772 | - fts5SegIterNext(p, &pNew->aSeg[iEq], 0); | |
| 177673 | + Fts5SegIter *pSeg = &pNew->aSeg[iEq]; | |
| 177674 | + if( p->rc==SQLITE_OK ) pSeg->xNext(p, pSeg, 0); | |
| 176773 | 177675 | fts5MultiIterAdvanced(p, pNew, iEq, iIter); |
| 176774 | 177676 | } |
| 176775 | 177677 | } |
| 176776 | 177678 | fts5MultiIterSetEof(pNew); |
| 176777 | 177679 | fts5AssertMultiIterSetup(p, pNew); |
| @@ -176817,10 +177719,11 @@ | ||
| 176817 | 177719 | } |
| 176818 | 177720 | pData = 0; |
| 176819 | 177721 | }else{ |
| 176820 | 177722 | pNew->bEof = 1; |
| 176821 | 177723 | } |
| 177724 | + fts5SegIterSetNext(p, pIter); | |
| 176822 | 177725 | |
| 176823 | 177726 | *ppOut = pNew; |
| 176824 | 177727 | } |
| 176825 | 177728 | |
| 176826 | 177729 | fts5DataRelease(pData); |
| @@ -176885,10 +177788,13 @@ | ||
| 176885 | 177788 | Fts5Data *pData = 0; |
| 176886 | 177789 | u8 *pChunk = &pSeg->pLeaf->p[pSeg->iLeafOffset]; |
| 176887 | 177790 | int nChunk = MIN(nRem, pSeg->pLeaf->szLeaf - pSeg->iLeafOffset); |
| 176888 | 177791 | int pgno = pSeg->iLeafPgno; |
| 176889 | 177792 | int pgnoSave = 0; |
| 177793 | + | |
| 177794 | + /* This function does notmwork with detail=none databases. */ | |
| 177795 | + assert( p->pConfig->eDetail!=FTS5_DETAIL_NONE ); | |
| 176890 | 177796 | |
| 176891 | 177797 | if( (pSeg->flags & FTS5_SEGITER_REVERSE)==0 ){ |
| 176892 | 177798 | pgnoSave = pgno+1; |
| 176893 | 177799 | } |
| 176894 | 177800 | |
| @@ -177309,12 +178215,11 @@ | ||
| 177309 | 178215 | ** Append a rowid and position-list size field to the writers output. |
| 177310 | 178216 | */ |
| 177311 | 178217 | static void fts5WriteAppendRowid( |
| 177312 | 178218 | Fts5Index *p, |
| 177313 | 178219 | Fts5SegWriter *pWriter, |
| 177314 | - i64 iRowid, | |
| 177315 | - int nPos | |
| 178220 | + i64 iRowid | |
| 177316 | 178221 | ){ |
| 177317 | 178222 | if( p->rc==SQLITE_OK ){ |
| 177318 | 178223 | Fts5PageWriter *pPage = &pWriter->writer; |
| 177319 | 178224 | |
| 177320 | 178225 | if( (pPage->buf.n + pPage->pgidx.n)>=p->pConfig->pgsz ){ |
| @@ -177337,12 +178242,10 @@ | ||
| 177337 | 178242 | fts5BufferAppendVarint(&p->rc, &pPage->buf, iRowid - pWriter->iPrevRowid); |
| 177338 | 178243 | } |
| 177339 | 178244 | pWriter->iPrevRowid = iRowid; |
| 177340 | 178245 | pWriter->bFirstRowidInDoclist = 0; |
| 177341 | 178246 | pWriter->bFirstRowidInPage = 0; |
| 177342 | - | |
| 177343 | - fts5BufferAppendVarint(&p->rc, &pPage->buf, nPos); | |
| 177344 | 178247 | } |
| 177345 | 178248 | } |
| 177346 | 178249 | |
| 177347 | 178250 | static void fts5WriteAppendPoslistData( |
| 177348 | 178251 | Fts5Index *p, |
| @@ -177534,10 +178437,11 @@ | ||
| 177534 | 178437 | int nInput; /* Number of input segments */ |
| 177535 | 178438 | Fts5SegWriter writer; /* Writer object */ |
| 177536 | 178439 | Fts5StructureSegment *pSeg; /* Output segment */ |
| 177537 | 178440 | Fts5Buffer term; |
| 177538 | 178441 | int bOldest; /* True if the output segment is the oldest */ |
| 178442 | + int eDetail = p->pConfig->eDetail; | |
| 177539 | 178443 | |
| 177540 | 178444 | assert( iLvl<pStruct->nLevel ); |
| 177541 | 178445 | assert( pLvl->nMerge<=pLvl->nSeg ); |
| 177542 | 178446 | |
| 177543 | 178447 | memset(&writer, 0, sizeof(Fts5SegWriter)); |
| @@ -177603,15 +178507,25 @@ | ||
| 177603 | 178507 | fts5BufferSet(&p->rc, &term, nTerm, pTerm); |
| 177604 | 178508 | } |
| 177605 | 178509 | |
| 177606 | 178510 | /* Append the rowid to the output */ |
| 177607 | 178511 | /* WRITEPOSLISTSIZE */ |
| 177608 | - nPos = pSegIter->nPos*2 + pSegIter->bDel; | |
| 177609 | - fts5WriteAppendRowid(p, &writer, fts5MultiIterRowid(pIter), nPos); | |
| 178512 | + fts5WriteAppendRowid(p, &writer, fts5MultiIterRowid(pIter)); | |
| 177610 | 178513 | |
| 177611 | - /* Append the position-list data to the output */ | |
| 177612 | - fts5ChunkIterate(p, pSegIter, (void*)&writer, fts5MergeChunkCallback); | |
| 178514 | + if( eDetail==FTS5_DETAIL_NONE ){ | |
| 178515 | + if( pSegIter->bDel ){ | |
| 178516 | + fts5BufferAppendVarint(&p->rc, &writer.writer.buf, 0); | |
| 178517 | + if( pSegIter->nPos>0 ){ | |
| 178518 | + fts5BufferAppendVarint(&p->rc, &writer.writer.buf, 0); | |
| 178519 | + } | |
| 178520 | + } | |
| 178521 | + }else{ | |
| 178522 | + /* Append the position-list data to the output */ | |
| 178523 | + nPos = pSegIter->nPos*2 + pSegIter->bDel; | |
| 178524 | + fts5BufferAppendVarint(&p->rc, &writer.writer.buf, nPos); | |
| 178525 | + fts5ChunkIterate(p, pSegIter, (void*)&writer, fts5MergeChunkCallback); | |
| 178526 | + } | |
| 177613 | 178527 | } |
| 177614 | 178528 | |
| 177615 | 178529 | /* Flush the last leaf page to disk. Set the output segment b-tree height |
| 177616 | 178530 | ** and last leaf page number at the same time. */ |
| 177617 | 178531 | fts5WriteFinish(p, &writer, &pSeg->pgnoLast); |
| @@ -177795,11 +178709,11 @@ | ||
| 177795 | 178709 | pStruct = fts5StructureRead(p); |
| 177796 | 178710 | iSegid = fts5AllocateSegid(p, pStruct); |
| 177797 | 178711 | |
| 177798 | 178712 | if( iSegid ){ |
| 177799 | 178713 | const int pgsz = p->pConfig->pgsz; |
| 177800 | - | |
| 178714 | + int eDetail = p->pConfig->eDetail; | |
| 177801 | 178715 | Fts5StructureSegment *pSeg; /* New segment within pStruct */ |
| 177802 | 178716 | Fts5Buffer *pBuf; /* Buffer in which to assemble leaf page */ |
| 177803 | 178717 | Fts5Buffer *pPgidx; /* Buffer in which to assemble pgidx */ |
| 177804 | 178718 | |
| 177805 | 178719 | Fts5SegWriter writer; |
| @@ -177838,16 +178752,11 @@ | ||
| 177838 | 178752 | |
| 177839 | 178753 | /* The entire doclist will not fit on this leaf. The following |
| 177840 | 178754 | ** loop iterates through the poslists that make up the current |
| 177841 | 178755 | ** doclist. */ |
| 177842 | 178756 | while( p->rc==SQLITE_OK && iOff<nDoclist ){ |
| 177843 | - int nPos; | |
| 177844 | - int nCopy; | |
| 177845 | - int bDummy; | |
| 177846 | 178757 | iOff += fts5GetVarint(&pDoclist[iOff], (u64*)&iDelta); |
| 177847 | - nCopy = fts5GetPoslistSize(&pDoclist[iOff], &nPos, &bDummy); | |
| 177848 | - nCopy += nPos; | |
| 177849 | 178758 | iRowid += iDelta; |
| 177850 | 178759 | |
| 177851 | 178760 | if( writer.bFirstRowidInPage ){ |
| 177852 | 178761 | fts5PutU16(&pBuf->p[0], (u16)pBuf->n); /* first rowid on page */ |
| 177853 | 178762 | pBuf->n += sqlite3Fts5PutVarint(&pBuf->p[pBuf->n], iRowid); |
| @@ -177856,38 +178765,56 @@ | ||
| 177856 | 178765 | }else{ |
| 177857 | 178766 | pBuf->n += sqlite3Fts5PutVarint(&pBuf->p[pBuf->n], iDelta); |
| 177858 | 178767 | } |
| 177859 | 178768 | assert( pBuf->n<=pBuf->nSpace ); |
| 177860 | 178769 | |
| 177861 | - if( (pBuf->n + pPgidx->n + nCopy) <= pgsz ){ | |
| 177862 | - /* The entire poslist will fit on the current leaf. So copy | |
| 177863 | - ** it in one go. */ | |
| 177864 | - fts5BufferSafeAppendBlob(pBuf, &pDoclist[iOff], nCopy); | |
| 177865 | - }else{ | |
| 177866 | - /* The entire poslist will not fit on this leaf. So it needs | |
| 177867 | - ** to be broken into sections. The only qualification being | |
| 177868 | - ** that each varint must be stored contiguously. */ | |
| 177869 | - const u8 *pPoslist = &pDoclist[iOff]; | |
| 177870 | - int iPos = 0; | |
| 177871 | - while( p->rc==SQLITE_OK ){ | |
| 177872 | - int nSpace = pgsz - pBuf->n - pPgidx->n; | |
| 177873 | - int n = 0; | |
| 177874 | - if( (nCopy - iPos)<=nSpace ){ | |
| 177875 | - n = nCopy - iPos; | |
| 177876 | - }else{ | |
| 177877 | - n = fts5PoslistPrefix(&pPoslist[iPos], nSpace); | |
| 177878 | - } | |
| 177879 | - assert( n>0 ); | |
| 177880 | - fts5BufferSafeAppendBlob(pBuf, &pPoslist[iPos], n); | |
| 177881 | - iPos += n; | |
| 177882 | - if( (pBuf->n + pPgidx->n)>=pgsz ){ | |
| 177883 | - fts5WriteFlushLeaf(p, &writer); | |
| 177884 | - } | |
| 177885 | - if( iPos>=nCopy ) break; | |
| 177886 | - } | |
| 177887 | - } | |
| 177888 | - iOff += nCopy; | |
| 178770 | + if( eDetail==FTS5_DETAIL_NONE ){ | |
| 178771 | + if( iOff<nDoclist && pDoclist[iOff]==0 ){ | |
| 178772 | + pBuf->p[pBuf->n++] = 0; | |
| 178773 | + iOff++; | |
| 178774 | + if( iOff<nDoclist && pDoclist[iOff]==0 ){ | |
| 178775 | + pBuf->p[pBuf->n++] = 0; | |
| 178776 | + iOff++; | |
| 178777 | + } | |
| 178778 | + } | |
| 178779 | + if( (pBuf->n + pPgidx->n)>=pgsz ){ | |
| 178780 | + fts5WriteFlushLeaf(p, &writer); | |
| 178781 | + } | |
| 178782 | + }else{ | |
| 178783 | + int bDummy; | |
| 178784 | + int nPos; | |
| 178785 | + int nCopy = fts5GetPoslistSize(&pDoclist[iOff], &nPos, &bDummy); | |
| 178786 | + nCopy += nPos; | |
| 178787 | + if( (pBuf->n + pPgidx->n + nCopy) <= pgsz ){ | |
| 178788 | + /* The entire poslist will fit on the current leaf. So copy | |
| 178789 | + ** it in one go. */ | |
| 178790 | + fts5BufferSafeAppendBlob(pBuf, &pDoclist[iOff], nCopy); | |
| 178791 | + }else{ | |
| 178792 | + /* The entire poslist will not fit on this leaf. So it needs | |
| 178793 | + ** to be broken into sections. The only qualification being | |
| 178794 | + ** that each varint must be stored contiguously. */ | |
| 178795 | + const u8 *pPoslist = &pDoclist[iOff]; | |
| 178796 | + int iPos = 0; | |
| 178797 | + while( p->rc==SQLITE_OK ){ | |
| 178798 | + int nSpace = pgsz - pBuf->n - pPgidx->n; | |
| 178799 | + int n = 0; | |
| 178800 | + if( (nCopy - iPos)<=nSpace ){ | |
| 178801 | + n = nCopy - iPos; | |
| 178802 | + }else{ | |
| 178803 | + n = fts5PoslistPrefix(&pPoslist[iPos], nSpace); | |
| 178804 | + } | |
| 178805 | + assert( n>0 ); | |
| 178806 | + fts5BufferSafeAppendBlob(pBuf, &pPoslist[iPos], n); | |
| 178807 | + iPos += n; | |
| 178808 | + if( (pBuf->n + pPgidx->n)>=pgsz ){ | |
| 178809 | + fts5WriteFlushLeaf(p, &writer); | |
| 178810 | + } | |
| 178811 | + if( iPos>=nCopy ) break; | |
| 178812 | + } | |
| 178813 | + } | |
| 178814 | + iOff += nCopy; | |
| 178815 | + } | |
| 177889 | 178816 | } |
| 177890 | 178817 | } |
| 177891 | 178818 | |
| 177892 | 178819 | /* TODO2: Doclist terminator written here. */ |
| 177893 | 178820 | /* pBuf->p[pBuf->n++] = '\0'; */ |
| @@ -178018,10 +178945,18 @@ | ||
| 178018 | 178945 | Fts5Buffer *pBuf; /* Append to this buffer */ |
| 178019 | 178946 | Fts5Colset *pColset; /* Restrict matches to this column */ |
| 178020 | 178947 | int eState; /* See above */ |
| 178021 | 178948 | }; |
| 178022 | 178949 | |
| 178950 | +typedef struct PoslistOffsetsCtx PoslistOffsetsCtx; | |
| 178951 | +struct PoslistOffsetsCtx { | |
| 178952 | + Fts5Buffer *pBuf; /* Append to this buffer */ | |
| 178953 | + Fts5Colset *pColset; /* Restrict matches to this column */ | |
| 178954 | + int iRead; | |
| 178955 | + int iWrite; | |
| 178956 | +}; | |
| 178957 | + | |
| 178023 | 178958 | /* |
| 178024 | 178959 | ** TODO: Make this more efficient! |
| 178025 | 178960 | */ |
| 178026 | 178961 | static int fts5IndexColsetTest(Fts5Colset *pColset, int iCol){ |
| 178027 | 178962 | int i; |
| @@ -178028,10 +178963,32 @@ | ||
| 178028 | 178963 | for(i=0; i<pColset->nCol; i++){ |
| 178029 | 178964 | if( pColset->aiCol[i]==iCol ) return 1; |
| 178030 | 178965 | } |
| 178031 | 178966 | return 0; |
| 178032 | 178967 | } |
| 178968 | + | |
| 178969 | +static void fts5PoslistOffsetsCallback( | |
| 178970 | + Fts5Index *p, | |
| 178971 | + void *pContext, | |
| 178972 | + const u8 *pChunk, int nChunk | |
| 178973 | +){ | |
| 178974 | + PoslistOffsetsCtx *pCtx = (PoslistOffsetsCtx*)pContext; | |
| 178975 | + assert_nc( nChunk>=0 ); | |
| 178976 | + if( nChunk>0 ){ | |
| 178977 | + int i = 0; | |
| 178978 | + while( i<nChunk ){ | |
| 178979 | + int iVal; | |
| 178980 | + i += fts5GetVarint32(&pChunk[i], iVal); | |
| 178981 | + iVal += pCtx->iRead - 2; | |
| 178982 | + pCtx->iRead = iVal; | |
| 178983 | + if( fts5IndexColsetTest(pCtx->pColset, iVal) ){ | |
| 178984 | + fts5BufferSafeAppendVarint(pCtx->pBuf, iVal + 2 - pCtx->iWrite); | |
| 178985 | + pCtx->iWrite = iVal; | |
| 178986 | + } | |
| 178987 | + } | |
| 178988 | + } | |
| 178989 | +} | |
| 178033 | 178990 | |
| 178034 | 178991 | static void fts5PoslistFilterCallback( |
| 178035 | 178992 | Fts5Index *p, |
| 178036 | 178993 | void *pContext, |
| 178037 | 178994 | const u8 *pChunk, int nChunk |
| @@ -178096,16 +179053,24 @@ | ||
| 178096 | 179053 | ){ |
| 178097 | 179054 | if( 0==fts5BufferGrow(&p->rc, pBuf, pSeg->nPos) ){ |
| 178098 | 179055 | if( pColset==0 ){ |
| 178099 | 179056 | fts5ChunkIterate(p, pSeg, (void*)pBuf, fts5PoslistCallback); |
| 178100 | 179057 | }else{ |
| 178101 | - PoslistCallbackCtx sCtx; | |
| 178102 | - sCtx.pBuf = pBuf; | |
| 178103 | - sCtx.pColset = pColset; | |
| 178104 | - sCtx.eState = fts5IndexColsetTest(pColset, 0); | |
| 178105 | - assert( sCtx.eState==0 || sCtx.eState==1 ); | |
| 178106 | - fts5ChunkIterate(p, pSeg, (void*)&sCtx, fts5PoslistFilterCallback); | |
| 179058 | + if( p->pConfig->eDetail==FTS5_DETAIL_FULL ){ | |
| 179059 | + PoslistCallbackCtx sCtx; | |
| 179060 | + sCtx.pBuf = pBuf; | |
| 179061 | + sCtx.pColset = pColset; | |
| 179062 | + sCtx.eState = fts5IndexColsetTest(pColset, 0); | |
| 179063 | + assert( sCtx.eState==0 || sCtx.eState==1 ); | |
| 179064 | + fts5ChunkIterate(p, pSeg, (void*)&sCtx, fts5PoslistFilterCallback); | |
| 179065 | + }else{ | |
| 179066 | + PoslistOffsetsCtx sCtx; | |
| 179067 | + memset(&sCtx, 0, sizeof(sCtx)); | |
| 179068 | + sCtx.pBuf = pBuf; | |
| 179069 | + sCtx.pColset = pColset; | |
| 179070 | + fts5ChunkIterate(p, pSeg, (void*)&sCtx, fts5PoslistOffsetsCallback); | |
| 179071 | + } | |
| 178107 | 179072 | } |
| 178108 | 179073 | } |
| 178109 | 179074 | } |
| 178110 | 179075 | |
| 178111 | 179076 | /* |
| @@ -178144,10 +179109,20 @@ | ||
| 178144 | 179109 | prev = *p++; |
| 178145 | 179110 | } |
| 178146 | 179111 | return p - (*pa); |
| 178147 | 179112 | } |
| 178148 | 179113 | |
| 179114 | +static int fts5AppendRowid( | |
| 179115 | + Fts5Index *p, | |
| 179116 | + i64 iDelta, | |
| 179117 | + Fts5IndexIter *pMulti, | |
| 179118 | + Fts5Colset *pColset, | |
| 179119 | + Fts5Buffer *pBuf | |
| 179120 | +){ | |
| 179121 | + fts5BufferAppendVarint(&p->rc, pBuf, iDelta); | |
| 179122 | + return 0; | |
| 179123 | +} | |
| 178149 | 179124 | |
| 178150 | 179125 | /* |
| 178151 | 179126 | ** Iterator pMulti currently points to a valid entry (not EOF). This |
| 178152 | 179127 | ** function appends the following to buffer pBuf: |
| 178153 | 179128 | ** |
| @@ -178171,12 +179146,12 @@ | ||
| 178171 | 179146 | if( p->rc==SQLITE_OK ){ |
| 178172 | 179147 | Fts5SegIter *pSeg = &pMulti->aSeg[ pMulti->aFirst[1].iFirst ]; |
| 178173 | 179148 | assert( fts5MultiIterEof(p, pMulti)==0 ); |
| 178174 | 179149 | assert( pSeg->nPos>0 ); |
| 178175 | 179150 | if( 0==fts5BufferGrow(&p->rc, pBuf, pSeg->nPos+9+9) ){ |
| 178176 | - | |
| 178177 | - if( pSeg->iLeafOffset+pSeg->nPos<=pSeg->pLeaf->szLeaf | |
| 179151 | + if( p->pConfig->eDetail==FTS5_DETAIL_FULL | |
| 179152 | + && pSeg->iLeafOffset+pSeg->nPos<=pSeg->pLeaf->szLeaf | |
| 178178 | 179153 | && (pColset==0 || pColset->nCol==1) |
| 178179 | 179154 | ){ |
| 178180 | 179155 | const u8 *pPos = &pSeg->pLeaf->p[pSeg->iLeafOffset]; |
| 178181 | 179156 | int nPos; |
| 178182 | 179157 | if( pColset ){ |
| @@ -178217,16 +179192,16 @@ | ||
| 178217 | 179192 | sqlite3Fts5PutVarint(&pBuf->p[iSv2], nActual*2); |
| 178218 | 179193 | } |
| 178219 | 179194 | } |
| 178220 | 179195 | } |
| 178221 | 179196 | } |
| 178222 | - | |
| 178223 | 179197 | } |
| 178224 | 179198 | } |
| 178225 | 179199 | |
| 178226 | 179200 | return 0; |
| 178227 | 179201 | } |
| 179202 | + | |
| 178228 | 179203 | |
| 178229 | 179204 | static void fts5DoclistIterNext(Fts5DoclistIter *pIter){ |
| 178230 | 179205 | u8 *p = pIter->aPoslist + pIter->nSize + pIter->nPoslist; |
| 178231 | 179206 | |
| 178232 | 179207 | assert( pIter->aPoslist ); |
| @@ -178283,10 +179258,73 @@ | ||
| 178283 | 179258 | #define fts5MergeAppendDocid(pBuf, iLastRowid, iRowid) { \ |
| 178284 | 179259 | assert( (pBuf)->n!=0 || (iLastRowid)==0 ); \ |
| 178285 | 179260 | fts5BufferSafeAppendVarint((pBuf), (iRowid) - (iLastRowid)); \ |
| 178286 | 179261 | (iLastRowid) = (iRowid); \ |
| 178287 | 179262 | } |
| 179263 | + | |
| 179264 | +/* | |
| 179265 | +** Swap the contents of buffer *p1 with that of *p2. | |
| 179266 | +*/ | |
| 179267 | +static void fts5BufferSwap(Fts5Buffer *p1, Fts5Buffer *p2){ | |
| 179268 | + Fts5Buffer tmp = *p1; | |
| 179269 | + *p1 = *p2; | |
| 179270 | + *p2 = tmp; | |
| 179271 | +} | |
| 179272 | + | |
| 179273 | +static void fts5NextRowid(Fts5Buffer *pBuf, int *piOff, i64 *piRowid){ | |
| 179274 | + int i = *piOff; | |
| 179275 | + if( i>=pBuf->n ){ | |
| 179276 | + *piOff = -1; | |
| 179277 | + }else{ | |
| 179278 | + u64 iVal; | |
| 179279 | + *piOff = i + sqlite3Fts5GetVarint(&pBuf->p[i], &iVal); | |
| 179280 | + *piRowid += iVal; | |
| 179281 | + } | |
| 179282 | +} | |
| 179283 | + | |
| 179284 | +/* | |
| 179285 | +** This is the equivalent of fts5MergePrefixLists() for detail=none mode. | |
| 179286 | +** In this case the buffers consist of a delta-encoded list of rowids only. | |
| 179287 | +*/ | |
| 179288 | +static void fts5MergeRowidLists( | |
| 179289 | + Fts5Index *p, /* FTS5 backend object */ | |
| 179290 | + Fts5Buffer *p1, /* First list to merge */ | |
| 179291 | + Fts5Buffer *p2 /* Second list to merge */ | |
| 179292 | +){ | |
| 179293 | + int i1 = 0; | |
| 179294 | + int i2 = 0; | |
| 179295 | + i64 iRowid1 = 0; | |
| 179296 | + i64 iRowid2 = 0; | |
| 179297 | + i64 iOut = 0; | |
| 179298 | + | |
| 179299 | + Fts5Buffer out; | |
| 179300 | + memset(&out, 0, sizeof(out)); | |
| 179301 | + sqlite3Fts5BufferSize(&p->rc, &out, p1->n + p2->n); | |
| 179302 | + if( p->rc ) return; | |
| 179303 | + | |
| 179304 | + fts5NextRowid(p1, &i1, &iRowid1); | |
| 179305 | + fts5NextRowid(p2, &i2, &iRowid2); | |
| 179306 | + while( i1>=0 || i2>=0 ){ | |
| 179307 | + if( i1>=0 && (i2<0 || iRowid1<iRowid2) ){ | |
| 179308 | + assert( iOut==0 || iRowid1>iOut ); | |
| 179309 | + fts5BufferSafeAppendVarint(&out, iRowid1 - iOut); | |
| 179310 | + iOut = iRowid1; | |
| 179311 | + fts5NextRowid(p1, &i1, &iRowid1); | |
| 179312 | + }else{ | |
| 179313 | + assert( iOut==0 || iRowid2>iOut ); | |
| 179314 | + fts5BufferSafeAppendVarint(&out, iRowid2 - iOut); | |
| 179315 | + iOut = iRowid2; | |
| 179316 | + if( i1>=0 && iRowid1==iRowid2 ){ | |
| 179317 | + fts5NextRowid(p1, &i1, &iRowid1); | |
| 179318 | + } | |
| 179319 | + fts5NextRowid(p2, &i2, &iRowid2); | |
| 179320 | + } | |
| 179321 | + } | |
| 179322 | + | |
| 179323 | + fts5BufferSwap(&out, p1); | |
| 179324 | + fts5BufferFree(&out); | |
| 179325 | +} | |
| 178288 | 179326 | |
| 178289 | 179327 | /* |
| 178290 | 179328 | ** Buffers p1 and p2 contain doclists. This function merges the content |
| 178291 | 179329 | ** of the two doclists together and sets buffer p1 to the result before |
| 178292 | 179330 | ** returning. |
| @@ -178352,11 +179390,13 @@ | ||
| 178352 | 179390 | sqlite3Fts5PoslistNext64(a2, i2.nPoslist, &iOff2, &iPos2); |
| 178353 | 179391 | if( iPos1==iPos2 ){ |
| 178354 | 179392 | sqlite3Fts5PoslistNext64(a1, i1.nPoslist, &iOff1,&iPos1); |
| 178355 | 179393 | } |
| 178356 | 179394 | } |
| 178357 | - p->rc = sqlite3Fts5PoslistWriterAppend(&tmp, &writer, iNew); | |
| 179395 | + if( iNew!=writer.iPrev || tmp.n==0 ){ | |
| 179396 | + p->rc = sqlite3Fts5PoslistWriterAppend(&tmp, &writer, iNew); | |
| 179397 | + } | |
| 178358 | 179398 | } |
| 178359 | 179399 | |
| 178360 | 179400 | /* WRITEPOSLISTSIZE */ |
| 178361 | 179401 | fts5BufferSafeAppendVarint(&out, tmp.n * 2); |
| 178362 | 179402 | fts5BufferSafeAppendBlob(&out, tmp.p, tmp.n); |
| @@ -178369,16 +179409,10 @@ | ||
| 178369 | 179409 | fts5BufferFree(&tmp); |
| 178370 | 179410 | fts5BufferFree(&out); |
| 178371 | 179411 | } |
| 178372 | 179412 | } |
| 178373 | 179413 | |
| 178374 | -static void fts5BufferSwap(Fts5Buffer *p1, Fts5Buffer *p2){ | |
| 178375 | - Fts5Buffer tmp = *p1; | |
| 178376 | - *p1 = *p2; | |
| 178377 | - *p2 = tmp; | |
| 178378 | -} | |
| 178379 | - | |
| 178380 | 179414 | static void fts5SetupPrefixIter( |
| 178381 | 179415 | Fts5Index *p, /* Index to read from */ |
| 178382 | 179416 | int bDesc, /* True for "ORDER BY rowid DESC" */ |
| 178383 | 179417 | const u8 *pToken, /* Buffer containing prefix to match */ |
| 178384 | 179418 | int nToken, /* Size of buffer pToken in bytes */ |
| @@ -178386,10 +179420,20 @@ | ||
| 178386 | 179420 | Fts5IndexIter **ppIter /* OUT: New iterator */ |
| 178387 | 179421 | ){ |
| 178388 | 179422 | Fts5Structure *pStruct; |
| 178389 | 179423 | Fts5Buffer *aBuf; |
| 178390 | 179424 | const int nBuf = 32; |
| 179425 | + | |
| 179426 | + void (*xMerge)(Fts5Index*, Fts5Buffer*, Fts5Buffer*); | |
| 179427 | + int (*xAppend)(Fts5Index*, i64, Fts5IndexIter*, Fts5Colset*, Fts5Buffer*); | |
| 179428 | + if( p->pConfig->eDetail==FTS5_DETAIL_NONE ){ | |
| 179429 | + xMerge = fts5MergeRowidLists; | |
| 179430 | + xAppend = fts5AppendRowid; | |
| 179431 | + }else{ | |
| 179432 | + xMerge = fts5MergePrefixLists; | |
| 179433 | + xAppend = fts5AppendPoslist; | |
| 179434 | + } | |
| 178391 | 179435 | |
| 178392 | 179436 | aBuf = (Fts5Buffer*)fts5IdxMalloc(p, sizeof(Fts5Buffer)*nBuf); |
| 178393 | 179437 | pStruct = fts5StructureRead(p); |
| 178394 | 179438 | |
| 178395 | 179439 | if( aBuf && pStruct ){ |
| @@ -178419,25 +179463,25 @@ | ||
| 178419 | 179463 | assert( i<nBuf ); |
| 178420 | 179464 | if( aBuf[i].n==0 ){ |
| 178421 | 179465 | fts5BufferSwap(&doclist, &aBuf[i]); |
| 178422 | 179466 | fts5BufferZero(&doclist); |
| 178423 | 179467 | }else{ |
| 178424 | - fts5MergePrefixLists(p, &doclist, &aBuf[i]); | |
| 179468 | + xMerge(p, &doclist, &aBuf[i]); | |
| 178425 | 179469 | fts5BufferZero(&aBuf[i]); |
| 178426 | 179470 | } |
| 178427 | 179471 | } |
| 178428 | 179472 | iLastRowid = 0; |
| 178429 | 179473 | } |
| 178430 | 179474 | |
| 178431 | - if( !fts5AppendPoslist(p, iRowid-iLastRowid, p1, pColset, &doclist) ){ | |
| 179475 | + if( !xAppend(p, iRowid-iLastRowid, p1, pColset, &doclist) ){ | |
| 178432 | 179476 | iLastRowid = iRowid; |
| 178433 | 179477 | } |
| 178434 | 179478 | } |
| 178435 | 179479 | |
| 178436 | 179480 | for(i=0; i<nBuf; i++){ |
| 178437 | 179481 | if( p->rc==SQLITE_OK ){ |
| 178438 | - fts5MergePrefixLists(p, &doclist, &aBuf[i]); | |
| 179482 | + xMerge(p, &doclist, &aBuf[i]); | |
| 178439 | 179483 | } |
| 178440 | 179484 | fts5BufferFree(&aBuf[i]); |
| 178441 | 179485 | } |
| 178442 | 179486 | fts5MultiIterFree(p, p1); |
| 178443 | 179487 | |
| @@ -178463,11 +179507,11 @@ | ||
| 178463 | 179507 | static int sqlite3Fts5IndexBeginWrite(Fts5Index *p, int bDelete, i64 iRowid){ |
| 178464 | 179508 | assert( p->rc==SQLITE_OK ); |
| 178465 | 179509 | |
| 178466 | 179510 | /* Allocate the hash table if it has not already been allocated */ |
| 178467 | 179511 | if( p->pHash==0 ){ |
| 178468 | - p->rc = sqlite3Fts5HashNew(&p->pHash, &p->nPendingData); | |
| 179512 | + p->rc = sqlite3Fts5HashNew(p->pConfig, &p->pHash, &p->nPendingData); | |
| 178469 | 179513 | } |
| 178470 | 179514 | |
| 178471 | 179515 | /* Flush the hash table to disk if required */ |
| 178472 | 179516 | if( iRowid<p->iWriteRowid |
| 178473 | 179517 | || (iRowid==p->iWriteRowid && p->bDelete==0) |
| @@ -178584,11 +179628,15 @@ | ||
| 178584 | 179628 | /* |
| 178585 | 179629 | ** Argument p points to a buffer containing utf-8 text that is n bytes in |
| 178586 | 179630 | ** size. Return the number of bytes in the nChar character prefix of the |
| 178587 | 179631 | ** buffer, or 0 if there are less than nChar characters in total. |
| 178588 | 179632 | */ |
| 178589 | -static int fts5IndexCharlenToBytelen(const char *p, int nByte, int nChar){ | |
| 179633 | +static int sqlite3Fts5IndexCharlenToBytelen( | |
| 179634 | + const char *p, | |
| 179635 | + int nByte, | |
| 179636 | + int nChar | |
| 179637 | +){ | |
| 178590 | 179638 | int n = 0; |
| 178591 | 179639 | int i; |
| 178592 | 179640 | for(i=0; i<nChar; i++){ |
| 178593 | 179641 | if( n>=nByte ) return 0; /* Input contains fewer than nChar chars */ |
| 178594 | 179642 | if( (unsigned char)p[n++]>=0xc0 ){ |
| @@ -178641,11 +179689,12 @@ | ||
| 178641 | 179689 | rc = sqlite3Fts5HashWrite( |
| 178642 | 179690 | p->pHash, p->iWriteRowid, iCol, iPos, FTS5_MAIN_PREFIX, pToken, nToken |
| 178643 | 179691 | ); |
| 178644 | 179692 | |
| 178645 | 179693 | for(i=0; i<pConfig->nPrefix && rc==SQLITE_OK; i++){ |
| 178646 | - int nByte = fts5IndexCharlenToBytelen(pToken, nToken, pConfig->aPrefix[i]); | |
| 179694 | + const int nChar = pConfig->aPrefix[i]; | |
| 179695 | + int nByte = sqlite3Fts5IndexCharlenToBytelen(pToken, nToken, nChar); | |
| 178647 | 179696 | if( nByte ){ |
| 178648 | 179697 | rc = sqlite3Fts5HashWrite(p->pHash, |
| 178649 | 179698 | p->iWriteRowid, iCol, iPos, (char)(FTS5_MAIN_PREFIX+i+1), pToken, |
| 178650 | 179699 | nByte |
| 178651 | 179700 | ); |
| @@ -178819,13 +179868,20 @@ | ||
| 178819 | 179868 | const u8 **pp, /* OUT: Pointer to position-list data */ |
| 178820 | 179869 | int *pn, /* OUT: Size of position-list in bytes */ |
| 178821 | 179870 | i64 *piRowid /* OUT: Current rowid */ |
| 178822 | 179871 | ){ |
| 178823 | 179872 | Fts5SegIter *pSeg = &pIter->aSeg[ pIter->aFirst[1].iFirst ]; |
| 179873 | + int eDetail = pIter->pIndex->pConfig->eDetail; | |
| 179874 | + | |
| 178824 | 179875 | assert( pIter->pIndex->rc==SQLITE_OK ); |
| 178825 | 179876 | *piRowid = pSeg->iRowid; |
| 178826 | - if( pSeg->iLeafOffset+pSeg->nPos<=pSeg->pLeaf->szLeaf ){ | |
| 179877 | + if( eDetail==FTS5_DETAIL_NONE ){ | |
| 179878 | + *pn = pSeg->nPos; | |
| 179879 | + }else | |
| 179880 | + if( eDetail==FTS5_DETAIL_FULL | |
| 179881 | + && pSeg->iLeafOffset+pSeg->nPos<=pSeg->pLeaf->szLeaf | |
| 179882 | + ){ | |
| 178827 | 179883 | u8 *pPos = &pSeg->pLeaf->p[pSeg->iLeafOffset]; |
| 178828 | 179884 | if( pColset==0 || pIter->bFiltered ){ |
| 178829 | 179885 | *pn = pSeg->nPos; |
| 178830 | 179886 | *pp = pPos; |
| 178831 | 179887 | }else if( pColset->nCol==1 ){ |
| @@ -178838,15 +179894,28 @@ | ||
| 178838 | 179894 | *pn = pIter->poslist.n; |
| 178839 | 179895 | } |
| 178840 | 179896 | }else{ |
| 178841 | 179897 | fts5BufferZero(&pIter->poslist); |
| 178842 | 179898 | fts5SegiterPoslist(pIter->pIndex, pSeg, pColset, &pIter->poslist); |
| 178843 | - *pp = pIter->poslist.p; | |
| 179899 | + if( eDetail==FTS5_DETAIL_FULL ){ | |
| 179900 | + *pp = pIter->poslist.p; | |
| 179901 | + } | |
| 178844 | 179902 | *pn = pIter->poslist.n; |
| 178845 | 179903 | } |
| 178846 | 179904 | return fts5IndexReturn(pIter->pIndex); |
| 178847 | 179905 | } |
| 179906 | + | |
| 179907 | +static int sqlite3Fts5IterCollist( | |
| 179908 | + Fts5IndexIter *pIter, | |
| 179909 | + const u8 **pp, /* OUT: Pointer to position-list data */ | |
| 179910 | + int *pn /* OUT: Size of position-list in bytes */ | |
| 179911 | +){ | |
| 179912 | + assert( pIter->pIndex->pConfig->eDetail==FTS5_DETAIL_COLUMNS ); | |
| 179913 | + *pp = pIter->poslist.p; | |
| 179914 | + *pn = pIter->poslist.n; | |
| 179915 | + return SQLITE_OK; | |
| 179916 | +} | |
| 178848 | 179917 | |
| 178849 | 179918 | /* |
| 178850 | 179919 | ** This function is similar to sqlite3Fts5IterPoslist(), except that it |
| 178851 | 179920 | ** copies the position list into the buffer supplied as the second |
| 178852 | 179921 | ** argument. |
| @@ -178957,11 +180026,11 @@ | ||
| 178957 | 180026 | */ |
| 178958 | 180027 | |
| 178959 | 180028 | /* |
| 178960 | 180029 | ** Return a simple checksum value based on the arguments. |
| 178961 | 180030 | */ |
| 178962 | -static u64 fts5IndexEntryCksum( | |
| 180031 | +static u64 sqlite3Fts5IndexEntryCksum( | |
| 178963 | 180032 | i64 iRowid, |
| 178964 | 180033 | int iCol, |
| 178965 | 180034 | int iPos, |
| 178966 | 180035 | int iIdx, |
| 178967 | 180036 | const char *pTerm, |
| @@ -179027,34 +180096,41 @@ | ||
| 179027 | 180096 | const char *z, /* Index key to query for */ |
| 179028 | 180097 | int n, /* Size of index key in bytes */ |
| 179029 | 180098 | int flags, /* Flags for Fts5IndexQuery */ |
| 179030 | 180099 | u64 *pCksum /* IN/OUT: Checksum value */ |
| 179031 | 180100 | ){ |
| 180101 | + int eDetail = p->pConfig->eDetail; | |
| 179032 | 180102 | u64 cksum = *pCksum; |
| 179033 | 180103 | Fts5IndexIter *pIdxIter = 0; |
| 180104 | + Fts5Buffer buf = {0, 0, 0}; | |
| 179034 | 180105 | int rc = sqlite3Fts5IndexQuery(p, z, n, flags, 0, &pIdxIter); |
| 179035 | 180106 | |
| 179036 | 180107 | while( rc==SQLITE_OK && 0==sqlite3Fts5IterEof(pIdxIter) ){ |
| 179037 | - i64 dummy; | |
| 179038 | - const u8 *pPos; | |
| 179039 | - int nPos; | |
| 179040 | 180108 | i64 rowid = sqlite3Fts5IterRowid(pIdxIter); |
| 179041 | - rc = sqlite3Fts5IterPoslist(pIdxIter, 0, &pPos, &nPos, &dummy); | |
| 180109 | + | |
| 180110 | + if( eDetail==FTS5_DETAIL_NONE ){ | |
| 180111 | + cksum ^= sqlite3Fts5IndexEntryCksum(rowid, 0, 0, iIdx, z, n); | |
| 180112 | + }else{ | |
| 180113 | + rc = sqlite3Fts5IterPoslistBuffer(pIdxIter, &buf); | |
| 180114 | + if( rc==SQLITE_OK ){ | |
| 180115 | + Fts5PoslistReader sReader; | |
| 180116 | + for(sqlite3Fts5PoslistReaderInit(buf.p, buf.n, &sReader); | |
| 180117 | + sReader.bEof==0; | |
| 180118 | + sqlite3Fts5PoslistReaderNext(&sReader) | |
| 180119 | + ){ | |
| 180120 | + int iCol = FTS5_POS2COLUMN(sReader.iPos); | |
| 180121 | + int iOff = FTS5_POS2OFFSET(sReader.iPos); | |
| 180122 | + cksum ^= sqlite3Fts5IndexEntryCksum(rowid, iCol, iOff, iIdx, z, n); | |
| 180123 | + } | |
| 180124 | + } | |
| 180125 | + } | |
| 179042 | 180126 | if( rc==SQLITE_OK ){ |
| 179043 | - Fts5PoslistReader sReader; | |
| 179044 | - for(sqlite3Fts5PoslistReaderInit(pPos, nPos, &sReader); | |
| 179045 | - sReader.bEof==0; | |
| 179046 | - sqlite3Fts5PoslistReaderNext(&sReader) | |
| 179047 | - ){ | |
| 179048 | - int iCol = FTS5_POS2COLUMN(sReader.iPos); | |
| 179049 | - int iOff = FTS5_POS2OFFSET(sReader.iPos); | |
| 179050 | - cksum ^= fts5IndexEntryCksum(rowid, iCol, iOff, iIdx, z, n); | |
| 179051 | - } | |
| 179052 | 180127 | rc = sqlite3Fts5IterNext(pIdxIter); |
| 179053 | 180128 | } |
| 179054 | 180129 | } |
| 179055 | 180130 | sqlite3Fts5IterClose(pIdxIter); |
| 180131 | + fts5BufferFree(&buf); | |
| 179056 | 180132 | |
| 179057 | 180133 | *pCksum = cksum; |
| 179058 | 180134 | return rc; |
| 179059 | 180135 | } |
| 179060 | 180136 | |
| @@ -179344,18 +180420,19 @@ | ||
| 179344 | 180420 | |
| 179345 | 180421 | |
| 179346 | 180422 | /* |
| 179347 | 180423 | ** Run internal checks to ensure that the FTS index (a) is internally |
| 179348 | 180424 | ** consistent and (b) contains entries for which the XOR of the checksums |
| 179349 | -** as calculated by fts5IndexEntryCksum() is cksum. | |
| 180425 | +** as calculated by sqlite3Fts5IndexEntryCksum() is cksum. | |
| 179350 | 180426 | ** |
| 179351 | 180427 | ** Return SQLITE_CORRUPT if any of the internal checks fail, or if the |
| 179352 | 180428 | ** checksum does not match. Return SQLITE_OK if all checks pass without |
| 179353 | 180429 | ** error, or some other SQLite error code if another error (e.g. OOM) |
| 179354 | 180430 | ** occurs. |
| 179355 | 180431 | */ |
| 179356 | 180432 | static int sqlite3Fts5IndexIntegrityCheck(Fts5Index *p, u64 cksum){ |
| 180433 | + int eDetail = p->pConfig->eDetail; | |
| 179357 | 180434 | u64 cksum2 = 0; /* Checksum based on contents of indexes */ |
| 179358 | 180435 | Fts5Buffer poslist = {0,0,0}; /* Buffer used to hold a poslist */ |
| 179359 | 180436 | Fts5IndexIter *pIter; /* Used to iterate through entire index */ |
| 179360 | 180437 | Fts5Structure *pStruct; /* Index structure */ |
| 179361 | 180438 | |
| @@ -179403,16 +180480,22 @@ | ||
| 179403 | 180480 | char *z = (char*)fts5MultiIterTerm(pIter, &n); |
| 179404 | 180481 | |
| 179405 | 180482 | /* If this is a new term, query for it. Update cksum3 with the results. */ |
| 179406 | 180483 | fts5TestTerm(p, &term, z, n, cksum2, &cksum3); |
| 179407 | 180484 | |
| 179408 | - poslist.n = 0; | |
| 179409 | - fts5SegiterPoslist(p, &pIter->aSeg[pIter->aFirst[1].iFirst] , 0, &poslist); | |
| 179410 | - while( 0==sqlite3Fts5PoslistNext64(poslist.p, poslist.n, &iOff, &iPos) ){ | |
| 179411 | - int iCol = FTS5_POS2COLUMN(iPos); | |
| 179412 | - int iTokOff = FTS5_POS2OFFSET(iPos); | |
| 179413 | - cksum2 ^= fts5IndexEntryCksum(iRowid, iCol, iTokOff, -1, z, n); | |
| 180485 | + if( eDetail==FTS5_DETAIL_NONE ){ | |
| 180486 | + if( 0==fts5MultiIterIsEmpty(p, pIter) ){ | |
| 180487 | + cksum2 ^= sqlite3Fts5IndexEntryCksum(iRowid, 0, 0, -1, z, n); | |
| 180488 | + } | |
| 180489 | + }else{ | |
| 180490 | + poslist.n = 0; | |
| 180491 | + fts5SegiterPoslist(p, &pIter->aSeg[pIter->aFirst[1].iFirst], 0, &poslist); | |
| 180492 | + while( 0==sqlite3Fts5PoslistNext64(poslist.p, poslist.n, &iOff, &iPos) ){ | |
| 180493 | + int iCol = FTS5_POS2COLUMN(iPos); | |
| 180494 | + int iTokOff = FTS5_POS2OFFSET(iPos); | |
| 180495 | + cksum2 ^= sqlite3Fts5IndexEntryCksum(iRowid, iCol, iTokOff, -1, z, n); | |
| 180496 | + } | |
| 179414 | 180497 | } |
| 179415 | 180498 | } |
| 179416 | 180499 | fts5TestTerm(p, &term, 0, 0, cksum2, &cksum3); |
| 179417 | 180500 | |
| 179418 | 180501 | fts5MultiIterFree(p, pIter); |
| @@ -179424,38 +180507,10 @@ | ||
| 179424 | 180507 | #endif |
| 179425 | 180508 | fts5BufferFree(&poslist); |
| 179426 | 180509 | return fts5IndexReturn(p); |
| 179427 | 180510 | } |
| 179428 | 180511 | |
| 179429 | - | |
| 179430 | -/* | |
| 179431 | -** Calculate and return a checksum that is the XOR of the index entry | |
| 179432 | -** checksum of all entries that would be generated by the token specified | |
| 179433 | -** by the final 5 arguments. | |
| 179434 | -*/ | |
| 179435 | -static u64 sqlite3Fts5IndexCksum( | |
| 179436 | - Fts5Config *pConfig, /* Configuration object */ | |
| 179437 | - i64 iRowid, /* Document term appears in */ | |
| 179438 | - int iCol, /* Column term appears in */ | |
| 179439 | - int iPos, /* Position term appears in */ | |
| 179440 | - const char *pTerm, int nTerm /* Term at iPos */ | |
| 179441 | -){ | |
| 179442 | - u64 ret = 0; /* Return value */ | |
| 179443 | - int iIdx; /* For iterating through indexes */ | |
| 179444 | - | |
| 179445 | - ret = fts5IndexEntryCksum(iRowid, iCol, iPos, 0, pTerm, nTerm); | |
| 179446 | - | |
| 179447 | - for(iIdx=0; iIdx<pConfig->nPrefix; iIdx++){ | |
| 179448 | - int nByte = fts5IndexCharlenToBytelen(pTerm, nTerm, pConfig->aPrefix[iIdx]); | |
| 179449 | - if( nByte ){ | |
| 179450 | - ret ^= fts5IndexEntryCksum(iRowid, iCol, iPos, iIdx+1, pTerm, nByte); | |
| 179451 | - } | |
| 179452 | - } | |
| 179453 | - | |
| 179454 | - return ret; | |
| 179455 | -} | |
| 179456 | - | |
| 179457 | 180512 | /************************************************************************* |
| 179458 | 180513 | ************************************************************************** |
| 179459 | 180514 | ** Below this point is the implementation of the fts5_decode() scalar |
| 179460 | 180515 | ** function only. |
| 179461 | 180516 | */ |
| @@ -180039,10 +181094,11 @@ | ||
| 180039 | 181094 | #define FTS5CSR_REQUIRE_DOCSIZE 0x02 |
| 180040 | 181095 | #define FTS5CSR_REQUIRE_INST 0x04 |
| 180041 | 181096 | #define FTS5CSR_EOF 0x08 |
| 180042 | 181097 | #define FTS5CSR_FREE_ZRANK 0x10 |
| 180043 | 181098 | #define FTS5CSR_REQUIRE_RESEEK 0x20 |
| 181099 | +#define FTS5CSR_REQUIRE_POSLIST 0x40 | |
| 180044 | 181100 | |
| 180045 | 181101 | #define BitFlagAllTest(x,y) (((x) & (y))==(y)) |
| 180046 | 181102 | #define BitFlagTest(x,y) (((x) & (y))!=0) |
| 180047 | 181103 | |
| 180048 | 181104 | |
| @@ -180452,10 +181508,11 @@ | ||
| 180452 | 181508 | static void fts5CsrNewrow(Fts5Cursor *pCsr){ |
| 180453 | 181509 | CsrFlagSet(pCsr, |
| 180454 | 181510 | FTS5CSR_REQUIRE_CONTENT |
| 180455 | 181511 | | FTS5CSR_REQUIRE_DOCSIZE |
| 180456 | 181512 | | FTS5CSR_REQUIRE_INST |
| 181513 | + | FTS5CSR_REQUIRE_POSLIST | |
| 180457 | 181514 | ); |
| 180458 | 181515 | } |
| 180459 | 181516 | |
| 180460 | 181517 | static void fts5FreeCursorComponents(Fts5Cursor *pCsr){ |
| 180461 | 181518 | Fts5Table *pTab = (Fts5Table*)(pCsr->base.pVtab); |
| @@ -180534,19 +181591,22 @@ | ||
| 180534 | 181591 | |
| 180535 | 181592 | pSorter->iRowid = sqlite3_column_int64(pSorter->pStmt, 0); |
| 180536 | 181593 | nBlob = sqlite3_column_bytes(pSorter->pStmt, 1); |
| 180537 | 181594 | aBlob = a = sqlite3_column_blob(pSorter->pStmt, 1); |
| 180538 | 181595 | |
| 180539 | - for(i=0; i<(pSorter->nIdx-1); i++){ | |
| 180540 | - int iVal; | |
| 180541 | - a += fts5GetVarint32(a, iVal); | |
| 180542 | - iOff += iVal; | |
| 180543 | - pSorter->aIdx[i] = iOff; | |
| 180544 | - } | |
| 180545 | - pSorter->aIdx[i] = &aBlob[nBlob] - a; | |
| 180546 | - | |
| 180547 | - pSorter->aPoslist = a; | |
| 181596 | + /* nBlob==0 in detail=none mode. */ | |
| 181597 | + if( nBlob>0 ){ | |
| 181598 | + for(i=0; i<(pSorter->nIdx-1); i++){ | |
| 181599 | + int iVal; | |
| 181600 | + a += fts5GetVarint32(a, iVal); | |
| 181601 | + iOff += iVal; | |
| 181602 | + pSorter->aIdx[i] = iOff; | |
| 181603 | + } | |
| 181604 | + pSorter->aIdx[i] = &aBlob[nBlob] - a; | |
| 181605 | + pSorter->aPoslist = a; | |
| 181606 | + } | |
| 181607 | + | |
| 180548 | 181608 | fts5CsrNewrow(pCsr); |
| 180549 | 181609 | } |
| 180550 | 181610 | |
| 180551 | 181611 | return rc; |
| 180552 | 181612 | } |
| @@ -180980,10 +182040,11 @@ | ||
| 180980 | 182040 | assert( pCsr->iLastRowid==LARGEST_INT64 ); |
| 180981 | 182041 | assert( pCsr->iFirstRowid==SMALLEST_INT64 ); |
| 180982 | 182042 | pCsr->ePlan = FTS5_PLAN_SOURCE; |
| 180983 | 182043 | pCsr->pExpr = pTab->pSortCsr->pExpr; |
| 180984 | 182044 | rc = fts5CursorFirst(pTab, pCsr, bDesc); |
| 182045 | + sqlite3Fts5ExprClearEof(pCsr->pExpr); | |
| 180985 | 182046 | }else if( pMatch ){ |
| 180986 | 182047 | const char *zExpr = (const char*)sqlite3_value_text(apVal[0]); |
| 180987 | 182048 | if( zExpr==0 ) zExpr = ""; |
| 180988 | 182049 | |
| 180989 | 182050 | rc = fts5CursorParseRank(pConfig, pCsr, pRank); |
| @@ -181409,10 +182470,12 @@ | ||
| 181409 | 182470 | fts5CheckTransactionState(pTab, FTS5_ROLLBACK, 0); |
| 181410 | 182471 | rc = sqlite3Fts5StorageRollback(pTab->pStorage); |
| 181411 | 182472 | return rc; |
| 181412 | 182473 | } |
| 181413 | 182474 | |
| 182475 | +static int fts5CsrPoslist(Fts5Cursor*, int, const u8**, int*); | |
| 182476 | + | |
| 181414 | 182477 | static void *fts5ApiUserData(Fts5Context *pCtx){ |
| 181415 | 182478 | Fts5Cursor *pCsr = (Fts5Cursor*)pCtx; |
| 181416 | 182479 | return pCsr->pAux->pUserData; |
| 181417 | 182480 | } |
| 181418 | 182481 | |
| @@ -181458,21 +182521,76 @@ | ||
| 181458 | 182521 | static int fts5ApiPhraseSize(Fts5Context *pCtx, int iPhrase){ |
| 181459 | 182522 | Fts5Cursor *pCsr = (Fts5Cursor*)pCtx; |
| 181460 | 182523 | return sqlite3Fts5ExprPhraseSize(pCsr->pExpr, iPhrase); |
| 181461 | 182524 | } |
| 181462 | 182525 | |
| 181463 | -static int fts5CsrPoslist(Fts5Cursor *pCsr, int iPhrase, const u8 **pa){ | |
| 181464 | - int n; | |
| 181465 | - if( pCsr->pSorter ){ | |
| 182526 | +static int fts5ApiColumnText( | |
| 182527 | + Fts5Context *pCtx, | |
| 182528 | + int iCol, | |
| 182529 | + const char **pz, | |
| 182530 | + int *pn | |
| 182531 | +){ | |
| 182532 | + int rc = SQLITE_OK; | |
| 182533 | + Fts5Cursor *pCsr = (Fts5Cursor*)pCtx; | |
| 182534 | + if( fts5IsContentless((Fts5Table*)(pCsr->base.pVtab)) ){ | |
| 182535 | + *pz = 0; | |
| 182536 | + *pn = 0; | |
| 182537 | + }else{ | |
| 182538 | + rc = fts5SeekCursor(pCsr, 0); | |
| 182539 | + if( rc==SQLITE_OK ){ | |
| 182540 | + *pz = (const char*)sqlite3_column_text(pCsr->pStmt, iCol+1); | |
| 182541 | + *pn = sqlite3_column_bytes(pCsr->pStmt, iCol+1); | |
| 182542 | + } | |
| 182543 | + } | |
| 182544 | + return rc; | |
| 182545 | +} | |
| 182546 | + | |
| 182547 | +static int fts5CsrPoslist( | |
| 182548 | + Fts5Cursor *pCsr, | |
| 182549 | + int iPhrase, | |
| 182550 | + const u8 **pa, | |
| 182551 | + int *pn | |
| 182552 | +){ | |
| 182553 | + Fts5Config *pConfig = ((Fts5Table*)(pCsr->base.pVtab))->pConfig; | |
| 182554 | + int rc = SQLITE_OK; | |
| 182555 | + int bLive = (pCsr->pSorter==0); | |
| 182556 | + | |
| 182557 | + if( CsrFlagTest(pCsr, FTS5CSR_REQUIRE_POSLIST) ){ | |
| 182558 | + | |
| 182559 | + if( pConfig->eDetail!=FTS5_DETAIL_FULL ){ | |
| 182560 | + Fts5PoslistPopulator *aPopulator; | |
| 182561 | + int i; | |
| 182562 | + aPopulator = sqlite3Fts5ExprClearPoslists(pCsr->pExpr, bLive); | |
| 182563 | + if( aPopulator==0 ) rc = SQLITE_NOMEM; | |
| 182564 | + for(i=0; i<pConfig->nCol && rc==SQLITE_OK; i++){ | |
| 182565 | + int n; const char *z; | |
| 182566 | + rc = fts5ApiColumnText((Fts5Context*)pCsr, i, &z, &n); | |
| 182567 | + if( rc==SQLITE_OK ){ | |
| 182568 | + rc = sqlite3Fts5ExprPopulatePoslists( | |
| 182569 | + pConfig, pCsr->pExpr, aPopulator, i, z, n | |
| 182570 | + ); | |
| 182571 | + } | |
| 182572 | + } | |
| 182573 | + sqlite3_free(aPopulator); | |
| 182574 | + | |
| 182575 | + if( pCsr->pSorter ){ | |
| 182576 | + sqlite3Fts5ExprCheckPoslists(pCsr->pExpr, pCsr->pSorter->iRowid); | |
| 182577 | + } | |
| 182578 | + } | |
| 182579 | + CsrFlagClear(pCsr, FTS5CSR_REQUIRE_POSLIST); | |
| 182580 | + } | |
| 182581 | + | |
| 182582 | + if( pCsr->pSorter && pConfig->eDetail==FTS5_DETAIL_FULL ){ | |
| 181466 | 182583 | Fts5Sorter *pSorter = pCsr->pSorter; |
| 181467 | 182584 | int i1 = (iPhrase==0 ? 0 : pSorter->aIdx[iPhrase-1]); |
| 181468 | - n = pSorter->aIdx[iPhrase] - i1; | |
| 182585 | + *pn = pSorter->aIdx[iPhrase] - i1; | |
| 181469 | 182586 | *pa = &pSorter->aPoslist[i1]; |
| 181470 | 182587 | }else{ |
| 181471 | - n = sqlite3Fts5ExprPoslist(pCsr->pExpr, iPhrase, pa); | |
| 182588 | + *pn = sqlite3Fts5ExprPoslist(pCsr->pExpr, iPhrase, pa); | |
| 181472 | 182589 | } |
| 181473 | - return n; | |
| 182590 | + | |
| 182591 | + return rc; | |
| 181474 | 182592 | } |
| 181475 | 182593 | |
| 181476 | 182594 | /* |
| 181477 | 182595 | ** Ensure that the Fts5Cursor.nInstCount and aInst[] variables are populated |
| 181478 | 182596 | ** correctly for the current view. Return SQLITE_OK if successful, or an |
| @@ -181493,47 +182611,50 @@ | ||
| 181493 | 182611 | if( aIter ){ |
| 181494 | 182612 | int nInst = 0; /* Number instances seen so far */ |
| 181495 | 182613 | int i; |
| 181496 | 182614 | |
| 181497 | 182615 | /* Initialize all iterators */ |
| 181498 | - for(i=0; i<nIter; i++){ | |
| 182616 | + for(i=0; i<nIter && rc==SQLITE_OK; i++){ | |
| 181499 | 182617 | const u8 *a; |
| 181500 | - int n = fts5CsrPoslist(pCsr, i, &a); | |
| 182618 | + int n; | |
| 182619 | + rc = fts5CsrPoslist(pCsr, i, &a, &n); | |
| 181501 | 182620 | sqlite3Fts5PoslistReaderInit(a, n, &aIter[i]); |
| 181502 | 182621 | } |
| 181503 | 182622 | |
| 181504 | - while( 1 ){ | |
| 181505 | - int *aInst; | |
| 181506 | - int iBest = -1; | |
| 181507 | - for(i=0; i<nIter; i++){ | |
| 181508 | - if( (aIter[i].bEof==0) | |
| 181509 | - && (iBest<0 || aIter[i].iPos<aIter[iBest].iPos) | |
| 181510 | - ){ | |
| 181511 | - iBest = i; | |
| 181512 | - } | |
| 181513 | - } | |
| 181514 | - if( iBest<0 ) break; | |
| 181515 | - | |
| 181516 | - nInst++; | |
| 181517 | - if( nInst>=pCsr->nInstAlloc ){ | |
| 181518 | - pCsr->nInstAlloc = pCsr->nInstAlloc ? pCsr->nInstAlloc*2 : 32; | |
| 181519 | - aInst = (int*)sqlite3_realloc( | |
| 181520 | - pCsr->aInst, pCsr->nInstAlloc*sizeof(int)*3 | |
| 181521 | - ); | |
| 181522 | - if( aInst ){ | |
| 181523 | - pCsr->aInst = aInst; | |
| 181524 | - }else{ | |
| 181525 | - rc = SQLITE_NOMEM; | |
| 181526 | - break; | |
| 181527 | - } | |
| 181528 | - } | |
| 181529 | - | |
| 181530 | - aInst = &pCsr->aInst[3 * (nInst-1)]; | |
| 181531 | - aInst[0] = iBest; | |
| 181532 | - aInst[1] = FTS5_POS2COLUMN(aIter[iBest].iPos); | |
| 181533 | - aInst[2] = FTS5_POS2OFFSET(aIter[iBest].iPos); | |
| 181534 | - sqlite3Fts5PoslistReaderNext(&aIter[iBest]); | |
| 182623 | + if( rc==SQLITE_OK ){ | |
| 182624 | + while( 1 ){ | |
| 182625 | + int *aInst; | |
| 182626 | + int iBest = -1; | |
| 182627 | + for(i=0; i<nIter; i++){ | |
| 182628 | + if( (aIter[i].bEof==0) | |
| 182629 | + && (iBest<0 || aIter[i].iPos<aIter[iBest].iPos) | |
| 182630 | + ){ | |
| 182631 | + iBest = i; | |
| 182632 | + } | |
| 182633 | + } | |
| 182634 | + if( iBest<0 ) break; | |
| 182635 | + | |
| 182636 | + nInst++; | |
| 182637 | + if( nInst>=pCsr->nInstAlloc ){ | |
| 182638 | + pCsr->nInstAlloc = pCsr->nInstAlloc ? pCsr->nInstAlloc*2 : 32; | |
| 182639 | + aInst = (int*)sqlite3_realloc( | |
| 182640 | + pCsr->aInst, pCsr->nInstAlloc*sizeof(int)*3 | |
| 182641 | + ); | |
| 182642 | + if( aInst ){ | |
| 182643 | + pCsr->aInst = aInst; | |
| 182644 | + }else{ | |
| 182645 | + rc = SQLITE_NOMEM; | |
| 182646 | + break; | |
| 182647 | + } | |
| 182648 | + } | |
| 182649 | + | |
| 182650 | + aInst = &pCsr->aInst[3 * (nInst-1)]; | |
| 182651 | + aInst[0] = iBest; | |
| 182652 | + aInst[1] = FTS5_POS2COLUMN(aIter[iBest].iPos); | |
| 182653 | + aInst[2] = FTS5_POS2OFFSET(aIter[iBest].iPos); | |
| 182654 | + sqlite3Fts5PoslistReaderNext(&aIter[iBest]); | |
| 182655 | + } | |
| 181535 | 182656 | } |
| 181536 | 182657 | |
| 181537 | 182658 | pCsr->nInstCount = nInst; |
| 181538 | 182659 | CsrFlagClear(pCsr, FTS5CSR_REQUIRE_INST); |
| 181539 | 182660 | } |
| @@ -181562,10 +182683,16 @@ | ||
| 181562 | 182683 | if( CsrFlagTest(pCsr, FTS5CSR_REQUIRE_INST)==0 |
| 181563 | 182684 | || SQLITE_OK==(rc = fts5CacheInstArray(pCsr)) |
| 181564 | 182685 | ){ |
| 181565 | 182686 | if( iIdx<0 || iIdx>=pCsr->nInstCount ){ |
| 181566 | 182687 | rc = SQLITE_RANGE; |
| 182688 | +#if 0 | |
| 182689 | + }else if( fts5IsOffsetless((Fts5Table*)pCsr->base.pVtab) ){ | |
| 182690 | + *piPhrase = pCsr->aInst[iIdx*3]; | |
| 182691 | + *piCol = pCsr->aInst[iIdx*3 + 2]; | |
| 182692 | + *piOff = -1; | |
| 182693 | +#endif | |
| 181567 | 182694 | }else{ |
| 181568 | 182695 | *piPhrase = pCsr->aInst[iIdx*3]; |
| 181569 | 182696 | *piCol = pCsr->aInst[iIdx*3 + 1]; |
| 181570 | 182697 | *piOff = pCsr->aInst[iIdx*3 + 2]; |
| 181571 | 182698 | } |
| @@ -181575,31 +182702,10 @@ | ||
| 181575 | 182702 | |
| 181576 | 182703 | static sqlite3_int64 fts5ApiRowid(Fts5Context *pCtx){ |
| 181577 | 182704 | return fts5CursorRowid((Fts5Cursor*)pCtx); |
| 181578 | 182705 | } |
| 181579 | 182706 | |
| 181580 | -static int fts5ApiColumnText( | |
| 181581 | - Fts5Context *pCtx, | |
| 181582 | - int iCol, | |
| 181583 | - const char **pz, | |
| 181584 | - int *pn | |
| 181585 | -){ | |
| 181586 | - int rc = SQLITE_OK; | |
| 181587 | - Fts5Cursor *pCsr = (Fts5Cursor*)pCtx; | |
| 181588 | - if( fts5IsContentless((Fts5Table*)(pCsr->base.pVtab)) ){ | |
| 181589 | - *pz = 0; | |
| 181590 | - *pn = 0; | |
| 181591 | - }else{ | |
| 181592 | - rc = fts5SeekCursor(pCsr, 0); | |
| 181593 | - if( rc==SQLITE_OK ){ | |
| 181594 | - *pz = (const char*)sqlite3_column_text(pCsr->pStmt, iCol+1); | |
| 181595 | - *pn = sqlite3_column_bytes(pCsr->pStmt, iCol+1); | |
| 181596 | - } | |
| 181597 | - } | |
| 181598 | - return rc; | |
| 181599 | -} | |
| 181600 | - | |
| 181601 | 182707 | static int fts5ColumnSizeCb( |
| 181602 | 182708 | void *pContext, /* Pointer to int */ |
| 181603 | 182709 | int tflags, |
| 181604 | 182710 | const char *pToken, /* Buffer containing token */ |
| 181605 | 182711 | int nToken, /* Size of token in bytes */ |
| @@ -181740,23 +182846,94 @@ | ||
| 181740 | 182846 | } |
| 181741 | 182847 | *piOff += (iVal-2); |
| 181742 | 182848 | } |
| 181743 | 182849 | } |
| 181744 | 182850 | |
| 181745 | -static void fts5ApiPhraseFirst( | |
| 182851 | +static int fts5ApiPhraseFirst( | |
| 181746 | 182852 | Fts5Context *pCtx, |
| 181747 | 182853 | int iPhrase, |
| 181748 | 182854 | Fts5PhraseIter *pIter, |
| 181749 | 182855 | int *piCol, int *piOff |
| 181750 | 182856 | ){ |
| 181751 | 182857 | Fts5Cursor *pCsr = (Fts5Cursor*)pCtx; |
| 181752 | - int n = fts5CsrPoslist(pCsr, iPhrase, &pIter->a); | |
| 181753 | - pIter->b = &pIter->a[n]; | |
| 181754 | - *piCol = 0; | |
| 181755 | - *piOff = 0; | |
| 181756 | - fts5ApiPhraseNext(pCtx, pIter, piCol, piOff); | |
| 182858 | + int n; | |
| 182859 | + int rc = fts5CsrPoslist(pCsr, iPhrase, &pIter->a, &n); | |
| 182860 | + if( rc==SQLITE_OK ){ | |
| 182861 | + pIter->b = &pIter->a[n]; | |
| 182862 | + *piCol = 0; | |
| 182863 | + *piOff = 0; | |
| 182864 | + fts5ApiPhraseNext(pCtx, pIter, piCol, piOff); | |
| 182865 | + } | |
| 182866 | + return rc; | |
| 181757 | 182867 | } |
| 182868 | + | |
| 182869 | +static void fts5ApiPhraseNextColumn( | |
| 182870 | + Fts5Context *pCtx, | |
| 182871 | + Fts5PhraseIter *pIter, | |
| 182872 | + int *piCol | |
| 182873 | +){ | |
| 182874 | + Fts5Cursor *pCsr = (Fts5Cursor*)pCtx; | |
| 182875 | + Fts5Config *pConfig = ((Fts5Table*)(pCsr->base.pVtab))->pConfig; | |
| 182876 | + | |
| 182877 | + if( pConfig->eDetail==FTS5_DETAIL_COLUMNS ){ | |
| 182878 | + if( pIter->a>=pIter->b ){ | |
| 182879 | + *piCol = -1; | |
| 182880 | + }else{ | |
| 182881 | + int iIncr; | |
| 182882 | + pIter->a += fts5GetVarint32(&pIter->a[0], iIncr); | |
| 182883 | + *piCol += (iIncr-2); | |
| 182884 | + } | |
| 182885 | + }else{ | |
| 182886 | + while( 1 ){ | |
| 182887 | + int dummy; | |
| 182888 | + if( pIter->a>=pIter->b ){ | |
| 182889 | + *piCol = -1; | |
| 182890 | + return; | |
| 182891 | + } | |
| 182892 | + if( pIter->a[0]==0x01 ) break; | |
| 182893 | + pIter->a += fts5GetVarint32(pIter->a, dummy); | |
| 182894 | + } | |
| 182895 | + pIter->a += 1 + fts5GetVarint32(&pIter->a[1], *piCol); | |
| 182896 | + } | |
| 182897 | +} | |
| 182898 | + | |
| 182899 | +static int fts5ApiPhraseFirstColumn( | |
| 182900 | + Fts5Context *pCtx, | |
| 182901 | + int iPhrase, | |
| 182902 | + Fts5PhraseIter *pIter, | |
| 182903 | + int *piCol | |
| 182904 | +){ | |
| 182905 | + int rc = SQLITE_OK; | |
| 182906 | + Fts5Cursor *pCsr = (Fts5Cursor*)pCtx; | |
| 182907 | + Fts5Config *pConfig = ((Fts5Table*)(pCsr->base.pVtab))->pConfig; | |
| 182908 | + | |
| 182909 | + if( pConfig->eDetail==FTS5_DETAIL_COLUMNS ){ | |
| 182910 | + int n; | |
| 182911 | + rc = sqlite3Fts5ExprPhraseCollist(pCsr->pExpr, iPhrase, &pIter->a, &n); | |
| 182912 | + if( rc==SQLITE_OK ){ | |
| 182913 | + pIter->b = &pIter->a[n]; | |
| 182914 | + *piCol = 0; | |
| 182915 | + fts5ApiPhraseNextColumn(pCtx, pIter, piCol); | |
| 182916 | + } | |
| 182917 | + }else{ | |
| 182918 | + int n; | |
| 182919 | + rc = fts5CsrPoslist(pCsr, iPhrase, &pIter->a, &n); | |
| 182920 | + if( rc==SQLITE_OK ){ | |
| 182921 | + pIter->b = &pIter->a[n]; | |
| 182922 | + if( n<=0 ){ | |
| 182923 | + *piCol = -1; | |
| 182924 | + }else if( pIter->a[0]==0x01 ){ | |
| 182925 | + pIter->a += 1 + fts5GetVarint32(&pIter->a[1], *piCol); | |
| 182926 | + }else{ | |
| 182927 | + *piCol = 0; | |
| 182928 | + } | |
| 182929 | + } | |
| 182930 | + } | |
| 182931 | + | |
| 182932 | + return rc; | |
| 182933 | +} | |
| 182934 | + | |
| 181758 | 182935 | |
| 181759 | 182936 | static int fts5ApiQueryPhrase(Fts5Context*, int, void*, |
| 181760 | 182937 | int(*)(const Fts5ExtensionApi*, Fts5Context*, void*) |
| 181761 | 182938 | ); |
| 181762 | 182939 | |
| @@ -181777,12 +182954,13 @@ | ||
| 181777 | 182954 | fts5ApiQueryPhrase, |
| 181778 | 182955 | fts5ApiSetAuxdata, |
| 181779 | 182956 | fts5ApiGetAuxdata, |
| 181780 | 182957 | fts5ApiPhraseFirst, |
| 181781 | 182958 | fts5ApiPhraseNext, |
| 182959 | + fts5ApiPhraseFirstColumn, | |
| 182960 | + fts5ApiPhraseNextColumn, | |
| 181782 | 182961 | }; |
| 181783 | - | |
| 181784 | 182962 | |
| 181785 | 182963 | /* |
| 181786 | 182964 | ** Implementation of API function xQueryPhrase(). |
| 181787 | 182965 | */ |
| 181788 | 182966 | static int fts5ApiQueryPhrase( |
| @@ -181911,24 +183089,50 @@ | ||
| 181911 | 183089 | int rc = SQLITE_OK; |
| 181912 | 183090 | int nPhrase = sqlite3Fts5ExprPhraseCount(pCsr->pExpr); |
| 181913 | 183091 | Fts5Buffer val; |
| 181914 | 183092 | |
| 181915 | 183093 | memset(&val, 0, sizeof(Fts5Buffer)); |
| 181916 | - | |
| 181917 | - /* Append the varints */ | |
| 181918 | - for(i=0; i<(nPhrase-1); i++){ | |
| 181919 | - const u8 *dummy; | |
| 181920 | - int nByte = sqlite3Fts5ExprPoslist(pCsr->pExpr, i, &dummy); | |
| 181921 | - sqlite3Fts5BufferAppendVarint(&rc, &val, nByte); | |
| 181922 | - } | |
| 181923 | - | |
| 181924 | - /* Append the position lists */ | |
| 181925 | - for(i=0; i<nPhrase; i++){ | |
| 181926 | - const u8 *pPoslist; | |
| 181927 | - int nPoslist; | |
| 181928 | - nPoslist = sqlite3Fts5ExprPoslist(pCsr->pExpr, i, &pPoslist); | |
| 181929 | - sqlite3Fts5BufferAppendBlob(&rc, &val, nPoslist, pPoslist); | |
| 183094 | + switch( ((Fts5Table*)(pCsr->base.pVtab))->pConfig->eDetail ){ | |
| 183095 | + case FTS5_DETAIL_FULL: | |
| 183096 | + | |
| 183097 | + /* Append the varints */ | |
| 183098 | + for(i=0; i<(nPhrase-1); i++){ | |
| 183099 | + const u8 *dummy; | |
| 183100 | + int nByte = sqlite3Fts5ExprPoslist(pCsr->pExpr, i, &dummy); | |
| 183101 | + sqlite3Fts5BufferAppendVarint(&rc, &val, nByte); | |
| 183102 | + } | |
| 183103 | + | |
| 183104 | + /* Append the position lists */ | |
| 183105 | + for(i=0; i<nPhrase; i++){ | |
| 183106 | + const u8 *pPoslist; | |
| 183107 | + int nPoslist; | |
| 183108 | + nPoslist = sqlite3Fts5ExprPoslist(pCsr->pExpr, i, &pPoslist); | |
| 183109 | + sqlite3Fts5BufferAppendBlob(&rc, &val, nPoslist, pPoslist); | |
| 183110 | + } | |
| 183111 | + break; | |
| 183112 | + | |
| 183113 | + case FTS5_DETAIL_COLUMNS: | |
| 183114 | + | |
| 183115 | + /* Append the varints */ | |
| 183116 | + for(i=0; rc==SQLITE_OK && i<(nPhrase-1); i++){ | |
| 183117 | + const u8 *dummy; | |
| 183118 | + int nByte; | |
| 183119 | + rc = sqlite3Fts5ExprPhraseCollist(pCsr->pExpr, i, &dummy, &nByte); | |
| 183120 | + sqlite3Fts5BufferAppendVarint(&rc, &val, nByte); | |
| 183121 | + } | |
| 183122 | + | |
| 183123 | + /* Append the position lists */ | |
| 183124 | + for(i=0; rc==SQLITE_OK && i<nPhrase; i++){ | |
| 183125 | + const u8 *pPoslist; | |
| 183126 | + int nPoslist; | |
| 183127 | + rc = sqlite3Fts5ExprPhraseCollist(pCsr->pExpr, i, &pPoslist, &nPoslist); | |
| 183128 | + sqlite3Fts5BufferAppendBlob(&rc, &val, nPoslist, pPoslist); | |
| 183129 | + } | |
| 183130 | + break; | |
| 183131 | + | |
| 183132 | + default: | |
| 183133 | + break; | |
| 181930 | 183134 | } |
| 181931 | 183135 | |
| 181932 | 183136 | sqlite3_result_blob(pCtx, val.p, val.n, sqlite3_free); |
| 181933 | 183137 | return rc; |
| 181934 | 183138 | } |
| @@ -182247,11 +183451,11 @@ | ||
| 182247 | 183451 | sqlite3_context *pCtx, /* Function call context */ |
| 182248 | 183452 | int nArg, /* Number of args */ |
| 182249 | 183453 | sqlite3_value **apVal /* Function arguments */ |
| 182250 | 183454 | ){ |
| 182251 | 183455 | assert( nArg==0 ); |
| 182252 | - sqlite3_result_text(pCtx, "fts5: 2016-01-06 11:01:07 fd0a50f0797d154fefff724624f00548b5320566", -1, SQLITE_TRANSIENT); | |
| 183456 | + sqlite3_result_text(pCtx, "fts5: 2016-01-14 14:19:50 d17bc2c92f4d086280e49a3cc72993be7fee2da7", -1, SQLITE_TRANSIENT); | |
| 182253 | 183457 | } |
| 182254 | 183458 | |
| 182255 | 183459 | static int fts5Init(sqlite3 *db){ |
| 182256 | 183460 | static const sqlite3_module fts5Mod = { |
| 182257 | 183461 | /* iVersion */ 2, |
| @@ -183179,32 +184383,77 @@ | ||
| 183179 | 184383 | struct Fts5IntegrityCtx { |
| 183180 | 184384 | i64 iRowid; |
| 183181 | 184385 | int iCol; |
| 183182 | 184386 | int szCol; |
| 183183 | 184387 | u64 cksum; |
| 184388 | + Fts5Termset *pTermset; | |
| 183184 | 184389 | Fts5Config *pConfig; |
| 183185 | 184390 | }; |
| 184391 | + | |
| 183186 | 184392 | |
| 183187 | 184393 | /* |
| 183188 | 184394 | ** Tokenization callback used by integrity check. |
| 183189 | 184395 | */ |
| 183190 | 184396 | static int fts5StorageIntegrityCallback( |
| 183191 | - void *pContext, /* Pointer to Fts5InsertCtx object */ | |
| 184397 | + void *pContext, /* Pointer to Fts5IntegrityCtx object */ | |
| 183192 | 184398 | int tflags, |
| 183193 | 184399 | const char *pToken, /* Buffer containing token */ |
| 183194 | 184400 | int nToken, /* Size of token in bytes */ |
| 183195 | 184401 | int iStart, /* Start offset of token */ |
| 183196 | 184402 | int iEnd /* End offset of token */ |
| 183197 | 184403 | ){ |
| 183198 | 184404 | Fts5IntegrityCtx *pCtx = (Fts5IntegrityCtx*)pContext; |
| 184405 | + Fts5Termset *pTermset = pCtx->pTermset; | |
| 184406 | + int bPresent; | |
| 184407 | + int ii; | |
| 184408 | + int rc = SQLITE_OK; | |
| 184409 | + int iPos; | |
| 184410 | + int iCol; | |
| 184411 | + | |
| 183199 | 184412 | if( (tflags & FTS5_TOKEN_COLOCATED)==0 || pCtx->szCol==0 ){ |
| 183200 | 184413 | pCtx->szCol++; |
| 183201 | 184414 | } |
| 183202 | - pCtx->cksum ^= sqlite3Fts5IndexCksum( | |
| 183203 | - pCtx->pConfig, pCtx->iRowid, pCtx->iCol, pCtx->szCol-1, pToken, nToken | |
| 183204 | - ); | |
| 183205 | - return SQLITE_OK; | |
| 184415 | + | |
| 184416 | + switch( pCtx->pConfig->eDetail ){ | |
| 184417 | + case FTS5_DETAIL_FULL: | |
| 184418 | + iPos = pCtx->szCol-1; | |
| 184419 | + iCol = pCtx->iCol; | |
| 184420 | + break; | |
| 184421 | + | |
| 184422 | + case FTS5_DETAIL_COLUMNS: | |
| 184423 | + iPos = pCtx->iCol; | |
| 184424 | + iCol = 0; | |
| 184425 | + break; | |
| 184426 | + | |
| 184427 | + default: | |
| 184428 | + assert( pCtx->pConfig->eDetail==FTS5_DETAIL_NONE ); | |
| 184429 | + iPos = 0; | |
| 184430 | + iCol = 0; | |
| 184431 | + break; | |
| 184432 | + } | |
| 184433 | + | |
| 184434 | + rc = sqlite3Fts5TermsetAdd(pTermset, 0, pToken, nToken, &bPresent); | |
| 184435 | + if( rc==SQLITE_OK && bPresent==0 ){ | |
| 184436 | + pCtx->cksum ^= sqlite3Fts5IndexEntryCksum( | |
| 184437 | + pCtx->iRowid, iCol, iPos, 0, pToken, nToken | |
| 184438 | + ); | |
| 184439 | + } | |
| 184440 | + | |
| 184441 | + for(ii=0; rc==SQLITE_OK && ii<pCtx->pConfig->nPrefix; ii++){ | |
| 184442 | + const int nChar = pCtx->pConfig->aPrefix[ii]; | |
| 184443 | + int nByte = sqlite3Fts5IndexCharlenToBytelen(pToken, nToken, nChar); | |
| 184444 | + if( nByte ){ | |
| 184445 | + rc = sqlite3Fts5TermsetAdd(pTermset, ii+1, pToken, nByte, &bPresent); | |
| 184446 | + if( bPresent==0 ){ | |
| 184447 | + pCtx->cksum ^= sqlite3Fts5IndexEntryCksum( | |
| 184448 | + pCtx->iRowid, iCol, iPos, ii+1, pToken, nByte | |
| 184449 | + ); | |
| 184450 | + } | |
| 184451 | + } | |
| 184452 | + } | |
| 184453 | + | |
| 184454 | + return rc; | |
| 183206 | 184455 | } |
| 183207 | 184456 | |
| 183208 | 184457 | /* |
| 183209 | 184458 | ** Check that the contents of the FTS index match that of the %_content |
| 183210 | 184459 | ** table. Return SQLITE_OK if they do, or SQLITE_CORRUPT if not. Return |
| @@ -183235,27 +184484,42 @@ | ||
| 183235 | 184484 | int i; |
| 183236 | 184485 | ctx.iRowid = sqlite3_column_int64(pScan, 0); |
| 183237 | 184486 | ctx.szCol = 0; |
| 183238 | 184487 | if( pConfig->bColumnsize ){ |
| 183239 | 184488 | rc = sqlite3Fts5StorageDocsize(p, ctx.iRowid, aColSize); |
| 184489 | + } | |
| 184490 | + if( rc==SQLITE_OK && pConfig->eDetail==FTS5_DETAIL_NONE ){ | |
| 184491 | + rc = sqlite3Fts5TermsetNew(&ctx.pTermset); | |
| 183240 | 184492 | } |
| 183241 | 184493 | for(i=0; rc==SQLITE_OK && i<pConfig->nCol; i++){ |
| 183242 | 184494 | if( pConfig->abUnindexed[i] ) continue; |
| 183243 | 184495 | ctx.iCol = i; |
| 183244 | 184496 | ctx.szCol = 0; |
| 183245 | - rc = sqlite3Fts5Tokenize(pConfig, | |
| 183246 | - FTS5_TOKENIZE_DOCUMENT, | |
| 183247 | - (const char*)sqlite3_column_text(pScan, i+1), | |
| 183248 | - sqlite3_column_bytes(pScan, i+1), | |
| 183249 | - (void*)&ctx, | |
| 183250 | - fts5StorageIntegrityCallback | |
| 183251 | - ); | |
| 183252 | - if( pConfig->bColumnsize && ctx.szCol!=aColSize[i] ){ | |
| 184497 | + if( pConfig->eDetail==FTS5_DETAIL_COLUMNS ){ | |
| 184498 | + rc = sqlite3Fts5TermsetNew(&ctx.pTermset); | |
| 184499 | + } | |
| 184500 | + if( rc==SQLITE_OK ){ | |
| 184501 | + rc = sqlite3Fts5Tokenize(pConfig, | |
| 184502 | + FTS5_TOKENIZE_DOCUMENT, | |
| 184503 | + (const char*)sqlite3_column_text(pScan, i+1), | |
| 184504 | + sqlite3_column_bytes(pScan, i+1), | |
| 184505 | + (void*)&ctx, | |
| 184506 | + fts5StorageIntegrityCallback | |
| 184507 | + ); | |
| 184508 | + } | |
| 184509 | + if( rc==SQLITE_OK && pConfig->bColumnsize && ctx.szCol!=aColSize[i] ){ | |
| 183253 | 184510 | rc = FTS5_CORRUPT; |
| 183254 | 184511 | } |
| 183255 | 184512 | aTotalSize[i] += ctx.szCol; |
| 184513 | + if( pConfig->eDetail==FTS5_DETAIL_COLUMNS ){ | |
| 184514 | + sqlite3Fts5TermsetFree(ctx.pTermset); | |
| 184515 | + ctx.pTermset = 0; | |
| 184516 | + } | |
| 183256 | 184517 | } |
| 184518 | + sqlite3Fts5TermsetFree(ctx.pTermset); | |
| 184519 | + ctx.pTermset = 0; | |
| 184520 | + | |
| 183257 | 184521 | if( rc!=SQLITE_OK ) break; |
| 183258 | 184522 | } |
| 183259 | 184523 | rc2 = sqlite3_reset(pScan); |
| 183260 | 184524 | if( rc==SQLITE_OK ) rc = rc2; |
| 183261 | 184525 | } |
| @@ -185777,11 +187041,11 @@ | ||
| 185777 | 187041 | |
| 185778 | 187042 | pCsr->rowid++; |
| 185779 | 187043 | |
| 185780 | 187044 | if( pTab->eType==FTS5_VOCAB_COL ){ |
| 185781 | 187045 | for(pCsr->iCol++; pCsr->iCol<nCol; pCsr->iCol++){ |
| 185782 | - if( pCsr->aCnt[pCsr->iCol] ) break; | |
| 187046 | + if( pCsr->aDoc[pCsr->iCol] ) break; | |
| 185783 | 187047 | } |
| 185784 | 187048 | } |
| 185785 | 187049 | |
| 185786 | 187050 | if( pTab->eType==FTS5_VOCAB_ROW || pCsr->iCol>=nCol ){ |
| 185787 | 187051 | if( sqlite3Fts5IterEof(pCsr->pIter) ){ |
| @@ -185810,28 +187074,56 @@ | ||
| 185810 | 187074 | i64 dummy; |
| 185811 | 187075 | const u8 *pPos; int nPos; /* Position list */ |
| 185812 | 187076 | i64 iPos = 0; /* 64-bit position read from poslist */ |
| 185813 | 187077 | int iOff = 0; /* Current offset within position list */ |
| 185814 | 187078 | |
| 185815 | - rc = sqlite3Fts5IterPoslist(pCsr->pIter, 0, &pPos, &nPos, &dummy); | |
| 185816 | - if( rc==SQLITE_OK ){ | |
| 185817 | - if( pTab->eType==FTS5_VOCAB_ROW ){ | |
| 185818 | - while( 0==sqlite3Fts5PoslistNext64(pPos, nPos, &iOff, &iPos) ){ | |
| 185819 | - pCsr->aCnt[0]++; | |
| 185820 | - } | |
| 185821 | - pCsr->aDoc[0]++; | |
| 185822 | - }else{ | |
| 185823 | - int iCol = -1; | |
| 185824 | - while( 0==sqlite3Fts5PoslistNext64(pPos, nPos, &iOff, &iPos) ){ | |
| 185825 | - int ii = FTS5_POS2COLUMN(iPos); | |
| 185826 | - pCsr->aCnt[ii]++; | |
| 185827 | - if( iCol!=ii ){ | |
| 185828 | - pCsr->aDoc[ii]++; | |
| 185829 | - iCol = ii; | |
| 185830 | - } | |
| 185831 | - } | |
| 185832 | - } | |
| 187079 | + switch( pCsr->pConfig->eDetail ){ | |
| 187080 | + case FTS5_DETAIL_FULL: | |
| 187081 | + rc = sqlite3Fts5IterPoslist(pCsr->pIter, 0, &pPos, &nPos, &dummy); | |
| 187082 | + if( rc==SQLITE_OK ){ | |
| 187083 | + if( pTab->eType==FTS5_VOCAB_ROW ){ | |
| 187084 | + while( 0==sqlite3Fts5PoslistNext64(pPos, nPos, &iOff, &iPos) ){ | |
| 187085 | + pCsr->aCnt[0]++; | |
| 187086 | + } | |
| 187087 | + pCsr->aDoc[0]++; | |
| 187088 | + }else{ | |
| 187089 | + int iCol = -1; | |
| 187090 | + while( 0==sqlite3Fts5PoslistNext64(pPos, nPos, &iOff, &iPos) ){ | |
| 187091 | + int ii = FTS5_POS2COLUMN(iPos); | |
| 187092 | + pCsr->aCnt[ii]++; | |
| 187093 | + if( iCol!=ii ){ | |
| 187094 | + pCsr->aDoc[ii]++; | |
| 187095 | + iCol = ii; | |
| 187096 | + } | |
| 187097 | + } | |
| 187098 | + } | |
| 187099 | + } | |
| 187100 | + break; | |
| 187101 | + | |
| 187102 | + case FTS5_DETAIL_COLUMNS: | |
| 187103 | + if( pTab->eType==FTS5_VOCAB_ROW ){ | |
| 187104 | + pCsr->aDoc[0]++; | |
| 187105 | + }else{ | |
| 187106 | + Fts5Buffer buf = {0, 0, 0}; | |
| 187107 | + rc = sqlite3Fts5IterPoslistBuffer(pCsr->pIter, &buf); | |
| 187108 | + if( rc==SQLITE_OK ){ | |
| 187109 | + while( 0==sqlite3Fts5PoslistNext64(buf.p, buf.n, &iOff,&iPos) ){ | |
| 187110 | + assert_nc( iPos>=0 && iPos<nCol ); | |
| 187111 | + if( iPos<nCol ) pCsr->aDoc[iPos]++; | |
| 187112 | + } | |
| 187113 | + } | |
| 187114 | + sqlite3Fts5BufferFree(&buf); | |
| 187115 | + } | |
| 187116 | + break; | |
| 187117 | + | |
| 187118 | + default: | |
| 187119 | + assert( pCsr->pConfig->eDetail==FTS5_DETAIL_NONE ); | |
| 187120 | + pCsr->aDoc[0]++; | |
| 187121 | + break; | |
| 187122 | + } | |
| 187123 | + | |
| 187124 | + if( rc==SQLITE_OK ){ | |
| 185833 | 187125 | rc = sqlite3Fts5IterNextScan(pCsr->pIter); |
| 185834 | 187126 | } |
| 185835 | 187127 | |
| 185836 | 187128 | if( rc==SQLITE_OK ){ |
| 185837 | 187129 | zTerm = sqlite3Fts5IterTerm(pCsr->pIter, &nTerm); |
| @@ -185843,11 +187135,11 @@ | ||
| 185843 | 187135 | } |
| 185844 | 187136 | } |
| 185845 | 187137 | } |
| 185846 | 187138 | |
| 185847 | 187139 | if( pCsr->bEof==0 && pTab->eType==FTS5_VOCAB_COL ){ |
| 185848 | - while( pCsr->aCnt[pCsr->iCol]==0 ) pCsr->iCol++; | |
| 187140 | + while( pCsr->aDoc[pCsr->iCol]==0 ) pCsr->iCol++; | |
| 185849 | 187141 | assert( pCsr->iCol<pCsr->pConfig->nCol ); |
| 185850 | 187142 | } |
| 185851 | 187143 | return rc; |
| 185852 | 187144 | } |
| 185853 | 187145 | |
| @@ -185923,34 +187215,40 @@ | ||
| 185923 | 187215 | sqlite3_vtab_cursor *pCursor, /* Cursor to retrieve value from */ |
| 185924 | 187216 | sqlite3_context *pCtx, /* Context for sqlite3_result_xxx() calls */ |
| 185925 | 187217 | int iCol /* Index of column to read value from */ |
| 185926 | 187218 | ){ |
| 185927 | 187219 | Fts5VocabCursor *pCsr = (Fts5VocabCursor*)pCursor; |
| 187220 | + int eDetail = pCsr->pConfig->eDetail; | |
| 187221 | + int eType = ((Fts5VocabTable*)(pCursor->pVtab))->eType; | |
| 187222 | + i64 iVal = 0; | |
| 185928 | 187223 | |
| 185929 | 187224 | if( iCol==0 ){ |
| 185930 | 187225 | sqlite3_result_text( |
| 185931 | 187226 | pCtx, (const char*)pCsr->term.p, pCsr->term.n, SQLITE_TRANSIENT |
| 185932 | 187227 | ); |
| 185933 | - } | |
| 185934 | - else if( ((Fts5VocabTable*)(pCursor->pVtab))->eType==FTS5_VOCAB_COL ){ | |
| 187228 | + }else if( eType==FTS5_VOCAB_COL ){ | |
| 185935 | 187229 | assert( iCol==1 || iCol==2 || iCol==3 ); |
| 185936 | 187230 | if( iCol==1 ){ |
| 185937 | - const char *z = pCsr->pConfig->azCol[pCsr->iCol]; | |
| 185938 | - sqlite3_result_text(pCtx, z, -1, SQLITE_STATIC); | |
| 187231 | + if( eDetail!=FTS5_DETAIL_NONE ){ | |
| 187232 | + const char *z = pCsr->pConfig->azCol[pCsr->iCol]; | |
| 187233 | + sqlite3_result_text(pCtx, z, -1, SQLITE_STATIC); | |
| 187234 | + } | |
| 185939 | 187235 | }else if( iCol==2 ){ |
| 185940 | - sqlite3_result_int64(pCtx, pCsr->aDoc[pCsr->iCol]); | |
| 187236 | + iVal = pCsr->aDoc[pCsr->iCol]; | |
| 185941 | 187237 | }else{ |
| 185942 | - sqlite3_result_int64(pCtx, pCsr->aCnt[pCsr->iCol]); | |
| 187238 | + iVal = pCsr->aCnt[pCsr->iCol]; | |
| 185943 | 187239 | } |
| 185944 | 187240 | }else{ |
| 185945 | 187241 | assert( iCol==1 || iCol==2 ); |
| 185946 | 187242 | if( iCol==1 ){ |
| 185947 | - sqlite3_result_int64(pCtx, pCsr->aDoc[0]); | |
| 187243 | + iVal = pCsr->aDoc[0]; | |
| 185948 | 187244 | }else{ |
| 185949 | - sqlite3_result_int64(pCtx, pCsr->aCnt[0]); | |
| 187245 | + iVal = pCsr->aCnt[0]; | |
| 185950 | 187246 | } |
| 185951 | 187247 | } |
| 187248 | + | |
| 187249 | + if( iVal>0 ) sqlite3_result_int64(pCtx, iVal); | |
| 185952 | 187250 | return SQLITE_OK; |
| 185953 | 187251 | } |
| 185954 | 187252 | |
| 185955 | 187253 | /* |
| 185956 | 187254 | ** This is the xRowid method. The SQLite core calls this routine to |
| 185957 | 187255 |
| --- src/sqlite3.c | |
| +++ src/sqlite3.c | |
| @@ -1,8 +1,8 @@ | |
| 1 | /****************************************************************************** |
| 2 | ** This file is an amalgamation of many separate C source files from SQLite |
| 3 | ** version 3.10.0. By combining all the individual C code files into this |
| 4 | ** single large file, the entire code can be compiled as a single translation |
| 5 | ** unit. This allows many compilers to do optimizations that would not be |
| 6 | ** possible if the files were compiled separately. Performance improvements |
| 7 | ** of 5% or more are commonly seen when SQLite is compiled as a single |
| 8 | ** translation unit. |
| @@ -119,10 +119,12 @@ | |
| 119 | #define SQLITE_ENABLE_LOCKING_STYLE 0 |
| 120 | #define HAVE_UTIME 1 |
| 121 | #else |
| 122 | /* This is not VxWorks. */ |
| 123 | #define OS_VXWORKS 0 |
| 124 | #endif /* defined(_WRS_KERNEL) */ |
| 125 | |
| 126 | /************** End of vxworks.h *********************************************/ |
| 127 | /************** Continuing where we left off in sqliteInt.h ******************/ |
| 128 | |
| @@ -323,13 +325,13 @@ | |
| 323 | ** |
| 324 | ** See also: [sqlite3_libversion()], |
| 325 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 326 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 327 | */ |
| 328 | #define SQLITE_VERSION "3.10.0" |
| 329 | #define SQLITE_VERSION_NUMBER 3010000 |
| 330 | #define SQLITE_SOURCE_ID "2016-01-06 11:01:07 fd0a50f0797d154fefff724624f00548b5320566" |
| 331 | |
| 332 | /* |
| 333 | ** CAPI3REF: Run-Time Library Version Numbers |
| 334 | ** KEYWORDS: sqlite3_version, sqlite3_sourceid |
| 335 | ** |
| @@ -1006,12 +1008,17 @@ | |
| 1006 | ** improve performance on some systems. |
| 1007 | ** |
| 1008 | ** <li>[[SQLITE_FCNTL_FILE_POINTER]] |
| 1009 | ** The [SQLITE_FCNTL_FILE_POINTER] opcode is used to obtain a pointer |
| 1010 | ** to the [sqlite3_file] object associated with a particular database |
| 1011 | ** connection. See the [sqlite3_file_control()] documentation for |
| 1012 | ** additional information. |
| 1013 | ** |
| 1014 | ** <li>[[SQLITE_FCNTL_SYNC_OMITTED]] |
| 1015 | ** No longer in use. |
| 1016 | ** |
| 1017 | ** <li>[[SQLITE_FCNTL_SYNC]] |
| @@ -1222,10 +1229,11 @@ | |
| 1222 | #define SQLITE_FCNTL_WIN32_SET_HANDLE 23 |
| 1223 | #define SQLITE_FCNTL_WAL_BLOCK 24 |
| 1224 | #define SQLITE_FCNTL_ZIPVFS 25 |
| 1225 | #define SQLITE_FCNTL_RBU 26 |
| 1226 | #define SQLITE_FCNTL_VFS_POINTER 27 |
| 1227 | |
| 1228 | /* deprecated names */ |
| 1229 | #define SQLITE_GET_LOCKPROXYFILE SQLITE_FCNTL_GET_LOCKPROXYFILE |
| 1230 | #define SQLITE_SET_LOCKPROXYFILE SQLITE_FCNTL_SET_LOCKPROXYFILE |
| 1231 | #define SQLITE_LAST_ERRNO SQLITE_FCNTL_LAST_ERRNO |
| @@ -8399,10 +8407,13 @@ | |
| 8399 | ** If parameter iCol is greater than or equal to the number of columns |
| 8400 | ** in the table, SQLITE_RANGE is returned. Or, if an error occurs (e.g. |
| 8401 | ** an OOM condition or IO error), an appropriate SQLite error code is |
| 8402 | ** returned. |
| 8403 | ** |
| 8404 | ** xColumnText: |
| 8405 | ** This function attempts to retrieve the text of column iCol of the |
| 8406 | ** current document. If successful, (*pz) is set to point to a buffer |
| 8407 | ** containing the text in utf-8 encoding, (*pn) is set to the size in bytes |
| 8408 | ** (not characters) of the buffer and SQLITE_OK is returned. Otherwise, |
| @@ -8419,18 +8430,32 @@ | |
| 8419 | ** xInstCount: |
| 8420 | ** Set *pnInst to the total number of occurrences of all phrases within |
| 8421 | ** the query within the current row. Return SQLITE_OK if successful, or |
| 8422 | ** an error code (i.e. SQLITE_NOMEM) if an error occurs. |
| 8423 | ** |
| 8424 | ** xInst: |
| 8425 | ** Query for the details of phrase match iIdx within the current row. |
| 8426 | ** Phrase matches are numbered starting from zero, so the iIdx argument |
| 8427 | ** should be greater than or equal to zero and smaller than the value |
| 8428 | ** output by xInstCount(). |
| 8429 | ** |
| 8430 | ** Returns SQLITE_OK if successful, or an error code (i.e. SQLITE_NOMEM) |
| 8431 | ** if an error occurs. |
| 8432 | ** |
| 8433 | ** xRowid: |
| 8434 | ** Returns the rowid of the current row. |
| 8435 | ** |
| 8436 | ** xTokenize: |
| @@ -8511,25 +8536,63 @@ | |
| 8511 | ** through instances of phrase iPhrase, use the following code: |
| 8512 | ** |
| 8513 | ** Fts5PhraseIter iter; |
| 8514 | ** int iCol, iOff; |
| 8515 | ** for(pApi->xPhraseFirst(pFts, iPhrase, &iter, &iCol, &iOff); |
| 8516 | ** iOff>=0; |
| 8517 | ** pApi->xPhraseNext(pFts, &iter, &iCol, &iOff) |
| 8518 | ** ){ |
| 8519 | ** // An instance of phrase iPhrase at offset iOff of column iCol |
| 8520 | ** } |
| 8521 | ** |
| 8522 | ** The Fts5PhraseIter structure is defined above. Applications should not |
| 8523 | ** modify this structure directly - it should only be used as shown above |
| 8524 | ** with the xPhraseFirst() and xPhraseNext() API methods. |
| 8525 | ** |
| 8526 | ** xPhraseNext() |
| 8527 | ** See xPhraseFirst above. |
| 8528 | */ |
| 8529 | struct Fts5ExtensionApi { |
| 8530 | int iVersion; /* Currently always set to 1 */ |
| 8531 | |
| 8532 | void *(*xUserData)(Fts5Context*); |
| 8533 | |
| 8534 | int (*xColumnCount)(Fts5Context*); |
| 8535 | int (*xRowCount)(Fts5Context*, sqlite3_int64 *pnRow); |
| @@ -8555,12 +8618,15 @@ | |
| 8555 | int(*)(const Fts5ExtensionApi*,Fts5Context*,void*) |
| 8556 | ); |
| 8557 | int (*xSetAuxdata)(Fts5Context*, void *pAux, void(*xDelete)(void*)); |
| 8558 | void *(*xGetAuxdata)(Fts5Context*, int bClear); |
| 8559 | |
| 8560 | void (*xPhraseFirst)(Fts5Context*, int iPhrase, Fts5PhraseIter*, int*, int*); |
| 8561 | void (*xPhraseNext)(Fts5Context*, Fts5PhraseIter*, int *piCol, int *piOff); |
| 8562 | }; |
| 8563 | |
| 8564 | /* |
| 8565 | ** CUSTOM AUXILIARY FUNCTIONS |
| 8566 | *************************************************************************/ |
| @@ -9976,14 +10042,10 @@ | |
| 9976 | /* |
| 9977 | ** Default maximum size of memory used by memory-mapped I/O in the VFS |
| 9978 | */ |
| 9979 | #ifdef __APPLE__ |
| 9980 | # include <TargetConditionals.h> |
| 9981 | # if TARGET_OS_IPHONE |
| 9982 | # undef SQLITE_MAX_MMAP_SIZE |
| 9983 | # define SQLITE_MAX_MMAP_SIZE 0 |
| 9984 | # endif |
| 9985 | #endif |
| 9986 | #ifndef SQLITE_MAX_MMAP_SIZE |
| 9987 | # if defined(__linux__) \ |
| 9988 | || defined(_WIN32) \ |
| 9989 | || (defined(__APPLE__) && defined(__MACH__)) \ |
| @@ -10479,19 +10541,21 @@ | |
| 10479 | ** Enter and Leave procedures no-ops. |
| 10480 | */ |
| 10481 | #ifndef SQLITE_OMIT_SHARED_CACHE |
| 10482 | SQLITE_PRIVATE void sqlite3BtreeEnter(Btree*); |
| 10483 | SQLITE_PRIVATE void sqlite3BtreeEnterAll(sqlite3*); |
| 10484 | #else |
| 10485 | # define sqlite3BtreeEnter(X) |
| 10486 | # define sqlite3BtreeEnterAll(X) |
| 10487 | #endif |
| 10488 | |
| 10489 | #if !defined(SQLITE_OMIT_SHARED_CACHE) && SQLITE_THREADSAFE |
| 10490 | SQLITE_PRIVATE int sqlite3BtreeSharable(Btree*); |
| 10491 | SQLITE_PRIVATE void sqlite3BtreeLeave(Btree*); |
| 10492 | SQLITE_PRIVATE void sqlite3BtreeEnterCursor(BtCursor*); |
| 10493 | SQLITE_PRIVATE void sqlite3BtreeLeaveCursor(BtCursor*); |
| 10494 | SQLITE_PRIVATE void sqlite3BtreeLeaveAll(sqlite3*); |
| 10495 | #ifndef NDEBUG |
| 10496 | /* These routines are used inside assert() statements only. */ |
| 10497 | SQLITE_PRIVATE int sqlite3BtreeHoldsMutex(Btree*); |
| @@ -10498,13 +10562,11 @@ | |
| 10498 | SQLITE_PRIVATE int sqlite3BtreeHoldsAllMutexes(sqlite3*); |
| 10499 | SQLITE_PRIVATE int sqlite3SchemaMutexHeld(sqlite3*,int,Schema*); |
| 10500 | #endif |
| 10501 | #else |
| 10502 | |
| 10503 | # define sqlite3BtreeSharable(X) 0 |
| 10504 | # define sqlite3BtreeLeave(X) |
| 10505 | # define sqlite3BtreeEnterCursor(X) |
| 10506 | # define sqlite3BtreeLeaveCursor(X) |
| 10507 | # define sqlite3BtreeLeaveAll(X) |
| 10508 | |
| 10509 | # define sqlite3BtreeHoldsMutex(X) 1 |
| 10510 | # define sqlite3BtreeHoldsAllMutexes(X) 1 |
| @@ -11215,10 +11277,11 @@ | |
| 11215 | #endif |
| 11216 | SQLITE_PRIVATE int sqlite3PagerMemUsed(Pager*); |
| 11217 | SQLITE_PRIVATE const char *sqlite3PagerFilename(Pager*, int); |
| 11218 | SQLITE_PRIVATE sqlite3_vfs *sqlite3PagerVfs(Pager*); |
| 11219 | SQLITE_PRIVATE sqlite3_file *sqlite3PagerFile(Pager*); |
| 11220 | SQLITE_PRIVATE const char *sqlite3PagerJournalname(Pager*); |
| 11221 | SQLITE_PRIVATE int sqlite3PagerNosync(Pager*); |
| 11222 | SQLITE_PRIVATE void *sqlite3PagerTempSpace(Pager*); |
| 11223 | SQLITE_PRIVATE int sqlite3PagerIsMemdb(Pager*); |
| 11224 | SQLITE_PRIVATE void sqlite3PagerCacheStat(Pager *, int, int, int *); |
| @@ -11309,10 +11372,12 @@ | |
| 11309 | #define PGHDR_NEED_SYNC 0x008 /* Fsync the rollback journal before |
| 11310 | ** writing this page to the database */ |
| 11311 | #define PGHDR_NEED_READ 0x010 /* Content is unread */ |
| 11312 | #define PGHDR_DONT_WRITE 0x020 /* Do not write content to disk */ |
| 11313 | #define PGHDR_MMAP 0x040 /* This is an mmap page object */ |
| 11314 | |
| 11315 | /* Initialize and shutdown the page cache subsystem */ |
| 11316 | SQLITE_PRIVATE int sqlite3PcacheInitialize(void); |
| 11317 | SQLITE_PRIVATE void sqlite3PcacheShutdown(void); |
| 11318 | |
| @@ -14135,11 +14200,10 @@ | |
| 14135 | SQLITE_PRIVATE int sqlite3InitCallback(void*, int, char**, char**); |
| 14136 | SQLITE_PRIVATE void sqlite3Pragma(Parse*,Token*,Token*,Token*,int); |
| 14137 | SQLITE_PRIVATE void sqlite3ResetAllSchemasOfConnection(sqlite3*); |
| 14138 | SQLITE_PRIVATE void sqlite3ResetOneSchema(sqlite3*,int); |
| 14139 | SQLITE_PRIVATE void sqlite3CollapseDatabaseArray(sqlite3*); |
| 14140 | SQLITE_PRIVATE void sqlite3BeginParse(Parse*,int); |
| 14141 | SQLITE_PRIVATE void sqlite3CommitInternalChanges(sqlite3*); |
| 14142 | SQLITE_PRIVATE void sqlite3DeleteColumnNames(sqlite3*,Table*); |
| 14143 | SQLITE_PRIVATE int sqlite3ColumnsFromExprList(Parse*,ExprList*,i16*,Column**); |
| 14144 | SQLITE_PRIVATE Table *sqlite3ResultSetOfSelect(Parse*,Select*); |
| 14145 | SQLITE_PRIVATE void sqlite3OpenMasterTable(Parse *, int); |
| @@ -16066,15 +16130,19 @@ | |
| 16066 | SQLITE_PRIVATE int sqlite3VdbeSorterNext(sqlite3 *, const VdbeCursor *, int *); |
| 16067 | SQLITE_PRIVATE int sqlite3VdbeSorterRewind(const VdbeCursor *, int *); |
| 16068 | SQLITE_PRIVATE int sqlite3VdbeSorterWrite(const VdbeCursor *, Mem *); |
| 16069 | SQLITE_PRIVATE int sqlite3VdbeSorterCompare(const VdbeCursor *, Mem *, int, int *); |
| 16070 | |
| 16071 | #if !defined(SQLITE_OMIT_SHARED_CACHE) && SQLITE_THREADSAFE>0 |
| 16072 | SQLITE_PRIVATE void sqlite3VdbeEnter(Vdbe*); |
| 16073 | SQLITE_PRIVATE void sqlite3VdbeLeave(Vdbe*); |
| 16074 | #else |
| 16075 | # define sqlite3VdbeEnter(X) |
| 16076 | # define sqlite3VdbeLeave(X) |
| 16077 | #endif |
| 16078 | |
| 16079 | #ifdef SQLITE_DEBUG |
| 16080 | SQLITE_PRIVATE void sqlite3VdbeMemAboutToChange(Vdbe*,Mem*); |
| @@ -19758,10 +19826,11 @@ | |
| 19758 | /* |
| 19759 | ** Mutex to control access to the memory allocation subsystem. |
| 19760 | */ |
| 19761 | sqlite3_mutex *mutex; |
| 19762 | |
| 19763 | /* |
| 19764 | ** Performance statistics |
| 19765 | */ |
| 19766 | u64 nAlloc; /* Total number of calls to malloc */ |
| 19767 | u64 totalAlloc; /* Total of all malloc calls - includes internal frag */ |
| @@ -19769,10 +19838,11 @@ | |
| 19769 | u32 currentOut; /* Current checkout, including internal fragmentation */ |
| 19770 | u32 currentCount; /* Current number of distinct checkouts */ |
| 19771 | u32 maxOut; /* Maximum instantaneous currentOut */ |
| 19772 | u32 maxCount; /* Maximum instantaneous currentCount */ |
| 19773 | u32 maxRequest; /* Largest allocation (exclusive of internal frag) */ |
| 19774 | |
| 19775 | /* |
| 19776 | ** Lists of free blocks. aiFreelist[0] is a list of free blocks of |
| 19777 | ** size mem5.szAtom. aiFreelist[1] holds blocks of size szAtom*2. |
| 19778 | ** aiFreelist[2] holds free blocks of size szAtom*4. And so forth. |
| @@ -19880,18 +19950,21 @@ | |
| 19880 | int iLogsize; /* Log2 of iFullSz/POW2_MIN */ |
| 19881 | |
| 19882 | /* nByte must be a positive */ |
| 19883 | assert( nByte>0 ); |
| 19884 | |
| 19885 | /* Keep track of the maximum allocation request. Even unfulfilled |
| 19886 | ** requests are counted */ |
| 19887 | if( (u32)nByte>mem5.maxRequest ){ |
| 19888 | /* Abort if the requested allocation size is larger than the largest |
| 19889 | ** power of two that we can represent using 32-bit signed integers. */ |
| 19890 | if( nByte > 0x40000000 ) return 0; |
| 19891 | mem5.maxRequest = nByte; |
| 19892 | } |
| 19893 | |
| 19894 | /* Round nByte up to the next valid power of two */ |
| 19895 | for(iFullSz=mem5.szAtom,iLogsize=0; iFullSz<nByte; iFullSz*=2,iLogsize++){} |
| 19896 | |
| 19897 | /* Make sure mem5.aiFreelist[iLogsize] contains at least one free |
| @@ -19914,18 +19987,20 @@ | |
| 19914 | mem5.aCtrl[i+newSize] = CTRL_FREE | iBin; |
| 19915 | memsys5Link(i+newSize, iBin); |
| 19916 | } |
| 19917 | mem5.aCtrl[i] = iLogsize; |
| 19918 | |
| 19919 | /* Update allocator performance statistics. */ |
| 19920 | mem5.nAlloc++; |
| 19921 | mem5.totalAlloc += iFullSz; |
| 19922 | mem5.totalExcess += iFullSz - nByte; |
| 19923 | mem5.currentCount++; |
| 19924 | mem5.currentOut += iFullSz; |
| 19925 | if( mem5.maxCount<mem5.currentCount ) mem5.maxCount = mem5.currentCount; |
| 19926 | if( mem5.maxOut<mem5.currentOut ) mem5.maxOut = mem5.currentOut; |
| 19927 | |
| 19928 | #ifdef SQLITE_DEBUG |
| 19929 | /* Make sure the allocated memory does not assume that it is set to zero |
| 19930 | ** or retains a value from a previous allocation */ |
| 19931 | memset(&mem5.zPool[i*mem5.szAtom], 0xAA, iFullSz); |
| @@ -19956,16 +20031,19 @@ | |
| 19956 | size = 1<<iLogsize; |
| 19957 | assert( iBlock+size-1<(u32)mem5.nBlock ); |
| 19958 | |
| 19959 | mem5.aCtrl[iBlock] |= CTRL_FREE; |
| 19960 | mem5.aCtrl[iBlock+size-1] |= CTRL_FREE; |
| 19961 | assert( mem5.currentCount>0 ); |
| 19962 | assert( mem5.currentOut>=(size*mem5.szAtom) ); |
| 19963 | mem5.currentCount--; |
| 19964 | mem5.currentOut -= size*mem5.szAtom; |
| 19965 | assert( mem5.currentOut>0 || mem5.currentCount==0 ); |
| 19966 | assert( mem5.currentCount>0 || mem5.currentOut==0 ); |
| 19967 | |
| 19968 | mem5.aCtrl[iBlock] = CTRL_FREE | iLogsize; |
| 19969 | while( ALWAYS(iLogsize<LOGMAX) ){ |
| 19970 | int iBuddy; |
| 19971 | if( (iBlock>>iLogsize) & 1 ){ |
| @@ -27479,37 +27557,55 @@ | |
| 27479 | #define osMkdir ((int(*)(const char*,mode_t))aSyscall[18].pCurrent) |
| 27480 | |
| 27481 | { "rmdir", (sqlite3_syscall_ptr)rmdir, 0 }, |
| 27482 | #define osRmdir ((int(*)(const char*))aSyscall[19].pCurrent) |
| 27483 | |
| 27484 | { "fchown", (sqlite3_syscall_ptr)fchown, 0 }, |
| 27485 | #define osFchown ((int(*)(int,uid_t,gid_t))aSyscall[20].pCurrent) |
| 27486 | |
| 27487 | { "geteuid", (sqlite3_syscall_ptr)geteuid, 0 }, |
| 27488 | #define osGeteuid ((uid_t(*)(void))aSyscall[21].pCurrent) |
| 27489 | |
| 27490 | #if !defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0 |
| 27491 | { "mmap", (sqlite3_syscall_ptr)mmap, 0 }, |
| 27492 | #define osMmap ((void*(*)(void*,size_t,int,int,int,off_t))aSyscall[22].pCurrent) |
| 27493 | |
| 27494 | { "munmap", (sqlite3_syscall_ptr)munmap, 0 }, |
| 27495 | #define osMunmap ((void*(*)(void*,size_t))aSyscall[23].pCurrent) |
| 27496 | |
| 27497 | #if HAVE_MREMAP |
| 27498 | { "mremap", (sqlite3_syscall_ptr)mremap, 0 }, |
| 27499 | #else |
| 27500 | { "mremap", (sqlite3_syscall_ptr)0, 0 }, |
| 27501 | #endif |
| 27502 | #define osMremap ((void*(*)(void*,size_t,size_t,int,...))aSyscall[24].pCurrent) |
| 27503 | |
| 27504 | { "getpagesize", (sqlite3_syscall_ptr)unixGetpagesize, 0 }, |
| 27505 | #define osGetpagesize ((int(*)(void))aSyscall[25].pCurrent) |
| 27506 | |
| 27507 | { "readlink", (sqlite3_syscall_ptr)readlink, 0 }, |
| 27508 | #define osReadlink ((ssize_t(*)(const char*,char*,size_t))aSyscall[26].pCurrent) |
| 27509 | |
| 27510 | #endif |
| 27511 | |
| 27512 | }; /* End of the overrideable system calls */ |
| 27513 | |
| 27514 | |
| 27515 | /* |
| @@ -27516,14 +27612,14 @@ | |
| 27516 | ** On some systems, calls to fchown() will trigger a message in a security |
| 27517 | ** log if they come from non-root processes. So avoid calling fchown() if |
| 27518 | ** we are not running as root. |
| 27519 | */ |
| 27520 | static int robustFchown(int fd, uid_t uid, gid_t gid){ |
| 27521 | #if OS_VXWORKS |
| 27522 | return 0; |
| 27523 | #else |
| 27524 | return osGeteuid() ? 0 : osFchown(fd,uid,gid); |
| 27525 | #endif |
| 27526 | } |
| 27527 | |
| 27528 | /* |
| 27529 | ** This is the xSetSystemCall() method of sqlite3_vfs for all of the |
| @@ -32986,10 +33082,11 @@ | |
| 32986 | SimulateIOError( return SQLITE_ERROR ); |
| 32987 | |
| 32988 | assert( pVfs->mxPathname==MAX_PATHNAME ); |
| 32989 | UNUSED_PARAMETER(pVfs); |
| 32990 | |
| 32991 | /* Attempt to resolve the path as if it were a symbolic link. If it is |
| 32992 | ** a symbolic link, the resolved path is stored in buffer zOut[]. Or, if |
| 32993 | ** the identified file is not a symbolic link or does not exist, then |
| 32994 | ** zPath is copied directly into zOut. Either way, nByte is left set to |
| 32995 | ** the size of the string copied into zOut[] in bytes. */ |
| @@ -33001,10 +33098,11 @@ | |
| 33001 | sqlite3_snprintf(nOut, zOut, "%s", zPath); |
| 33002 | nByte = sqlite3Strlen30(zOut); |
| 33003 | }else{ |
| 33004 | zOut[nByte] = '\0'; |
| 33005 | } |
| 33006 | |
| 33007 | /* If buffer zOut[] now contains an absolute path there is nothing more |
| 33008 | ** to do. If it contains a relative path, do the following: |
| 33009 | ** |
| 33010 | ** * move the relative path string so that it is at the end of th |
| @@ -43327,10 +43425,11 @@ | |
| 43327 | # define sqlite3WalCallback(z) 0 |
| 43328 | # define sqlite3WalExclusiveMode(y,z) 0 |
| 43329 | # define sqlite3WalHeapMemory(z) 0 |
| 43330 | # define sqlite3WalFramesize(z) 0 |
| 43331 | # define sqlite3WalFindFrame(x,y,z) 0 |
| 43332 | #else |
| 43333 | |
| 43334 | #define WAL_SAVEPOINT_NDATA 4 |
| 43335 | |
| 43336 | /* Connection to a write-ahead log (WAL) file. |
| @@ -43421,10 +43520,13 @@ | |
| 43421 | ** stored in each frame (i.e. the db page-size when the WAL was created). |
| 43422 | */ |
| 43423 | SQLITE_PRIVATE int sqlite3WalFramesize(Wal *pWal); |
| 43424 | #endif |
| 43425 | |
| 43426 | #endif /* ifndef SQLITE_OMIT_WAL */ |
| 43427 | #endif /* _WAL_H_ */ |
| 43428 | |
| 43429 | /************** End of wal.h *************************************************/ |
| 43430 | /************** Continuing where we left off in pager.c **********************/ |
| @@ -49032,11 +49134,11 @@ | |
| 49032 | if( pPager->exclusiveMode && sqlite3WalExclusiveMode(pPager->pWal, -1) ){ |
| 49033 | rc = pagerLockDb(pPager, EXCLUSIVE_LOCK); |
| 49034 | if( rc!=SQLITE_OK ){ |
| 49035 | return rc; |
| 49036 | } |
| 49037 | sqlite3WalExclusiveMode(pPager->pWal, 1); |
| 49038 | } |
| 49039 | |
| 49040 | /* Grab the write lock on the log file. If successful, upgrade to |
| 49041 | ** PAGER_RESERVED state. Otherwise, return an error code to the caller. |
| 49042 | ** The busy-handler is not invoked if another connection already |
| @@ -50096,10 +50198,22 @@ | |
| 50096 | ** not yet been opened. |
| 50097 | */ |
| 50098 | SQLITE_PRIVATE sqlite3_file *sqlite3PagerFile(Pager *pPager){ |
| 50099 | return pPager->fd; |
| 50100 | } |
| 50101 | |
| 50102 | /* |
| 50103 | ** Return the full pathname of the journal file. |
| 50104 | */ |
| 50105 | SQLITE_PRIVATE const char *sqlite3PagerJournalname(Pager *pPager){ |
| @@ -51202,10 +51316,11 @@ | |
| 51202 | u8 truncateOnCommit; /* True to truncate WAL file on commit */ |
| 51203 | u8 syncHeader; /* Fsync the WAL header if true */ |
| 51204 | u8 padToSectorBoundary; /* Pad transactions out to the next sector */ |
| 51205 | WalIndexHdr hdr; /* Wal-index header for current transaction */ |
| 51206 | u32 minFrame; /* Ignore wal frames before this one */ |
| 51207 | const char *zWalName; /* Name of WAL file */ |
| 51208 | u32 nCkpt; /* Checkpoint sequence counter in the wal-header */ |
| 51209 | #ifdef SQLITE_DEBUG |
| 51210 | u8 lockError; /* True if a locking error has occurred */ |
| 51211 | #endif |
| @@ -51455,18 +51570,22 @@ | |
| 51455 | int nativeCksum; /* True for native byte-order checksums */ |
| 51456 | u32 *aCksum = pWal->hdr.aFrameCksum; |
| 51457 | assert( WAL_FRAME_HDRSIZE==24 ); |
| 51458 | sqlite3Put4byte(&aFrame[0], iPage); |
| 51459 | sqlite3Put4byte(&aFrame[4], nTruncate); |
| 51460 | memcpy(&aFrame[8], pWal->hdr.aSalt, 8); |
| 51461 | |
| 51462 | nativeCksum = (pWal->hdr.bigEndCksum==SQLITE_BIGENDIAN); |
| 51463 | walChecksumBytes(nativeCksum, aFrame, 8, aCksum, aCksum); |
| 51464 | walChecksumBytes(nativeCksum, aData, pWal->szPage, aCksum, aCksum); |
| 51465 | |
| 51466 | sqlite3Put4byte(&aFrame[16], aCksum[0]); |
| 51467 | sqlite3Put4byte(&aFrame[20], aCksum[1]); |
| 51468 | } |
| 51469 | |
| 51470 | /* |
| 51471 | ** Check to see if the frame with header in aFrame[] and content |
| 51472 | ** in aData[] is valid. If it is a valid frame, fill *piPage and |
| @@ -53389,10 +53508,11 @@ | |
| 53389 | int rc; |
| 53390 | |
| 53391 | /* Cannot start a write transaction without first holding a read |
| 53392 | ** transaction. */ |
| 53393 | assert( pWal->readLock>=0 ); |
| 53394 | |
| 53395 | if( pWal->readOnly ){ |
| 53396 | return SQLITE_READONLY; |
| 53397 | } |
| 53398 | |
| @@ -53424,10 +53544,11 @@ | |
| 53424 | */ |
| 53425 | SQLITE_PRIVATE int sqlite3WalEndWriteTransaction(Wal *pWal){ |
| 53426 | if( pWal->writeLock ){ |
| 53427 | walUnlockExclusive(pWal, WAL_WRITE_LOCK, 1); |
| 53428 | pWal->writeLock = 0; |
| 53429 | pWal->truncateOnCommit = 0; |
| 53430 | } |
| 53431 | return SQLITE_OK; |
| 53432 | } |
| 53433 | |
| @@ -53641,10 +53762,63 @@ | |
| 53641 | if( rc ) return rc; |
| 53642 | /* Write the page data */ |
| 53643 | rc = walWriteToLog(p, pData, p->szPage, iOffset+sizeof(aFrame)); |
| 53644 | return rc; |
| 53645 | } |
| 53646 | |
| 53647 | /* |
| 53648 | ** Write a set of frames to the log. The caller must hold the write-lock |
| 53649 | ** on the log file (obtained using sqlite3WalBeginWriteTransaction()). |
| 53650 | */ |
| @@ -53662,10 +53836,12 @@ | |
| 53662 | PgHdr *pLast = 0; /* Last frame in list */ |
| 53663 | int nExtra = 0; /* Number of extra copies of last page */ |
| 53664 | int szFrame; /* The size of a single frame */ |
| 53665 | i64 iOffset; /* Next byte to write in WAL file */ |
| 53666 | WalWriter w; /* The writer */ |
| 53667 | |
| 53668 | assert( pList ); |
| 53669 | assert( pWal->writeLock ); |
| 53670 | |
| 53671 | /* If this frame set completes a transaction, then nTruncate>0. If |
| @@ -53676,10 +53852,15 @@ | |
| 53676 | { int cnt; for(cnt=0, p=pList; p; p=p->pDirty, cnt++){} |
| 53677 | WALTRACE(("WAL%p: frame write begin. %d frames. mxFrame=%d. %s\n", |
| 53678 | pWal, cnt, pWal->hdr.mxFrame, isCommit ? "Commit" : "Spill")); |
| 53679 | } |
| 53680 | #endif |
| 53681 | |
| 53682 | /* See if it is possible to write these frames into the start of the |
| 53683 | ** log file, instead of appending to it at pWal->hdr.mxFrame. |
| 53684 | */ |
| 53685 | if( SQLITE_OK!=(rc = walRestartLog(pWal)) ){ |
| @@ -53741,17 +53922,45 @@ | |
| 53741 | szFrame = szPage + WAL_FRAME_HDRSIZE; |
| 53742 | |
| 53743 | /* Write all frames into the log file exactly once */ |
| 53744 | for(p=pList; p; p=p->pDirty){ |
| 53745 | int nDbSize; /* 0 normally. Positive == commit flag */ |
| 53746 | iFrame++; |
| 53747 | assert( iOffset==walFrameOffset(iFrame, szPage) ); |
| 53748 | nDbSize = (isCommit && p->pDirty==0) ? nTruncate : 0; |
| 53749 | rc = walWriteOneFrame(&w, p, nDbSize, iOffset); |
| 53750 | if( rc ) return rc; |
| 53751 | pLast = p; |
| 53752 | iOffset += szFrame; |
| 53753 | } |
| 53754 | |
| 53755 | /* If this is the end of a transaction, then we might need to pad |
| 53756 | ** the transaction and/or sync the WAL file. |
| 53757 | ** |
| @@ -53799,10 +54008,11 @@ | |
| 53799 | ** guarantees that there are no other writers, and no data that may |
| 53800 | ** be in use by existing readers is being overwritten. |
| 53801 | */ |
| 53802 | iFrame = pWal->hdr.mxFrame; |
| 53803 | for(p=pList; p && rc==SQLITE_OK; p=p->pDirty){ |
| 53804 | iFrame++; |
| 53805 | rc = walIndexAppend(pWal, iFrame, p->pgno); |
| 53806 | } |
| 53807 | while( rc==SQLITE_OK && nExtra>0 ){ |
| 53808 | iFrame++; |
| @@ -53911,10 +54121,11 @@ | |
| 53911 | } |
| 53912 | } |
| 53913 | |
| 53914 | /* Copy data from the log to the database file. */ |
| 53915 | if( rc==SQLITE_OK ){ |
| 53916 | if( pWal->hdr.mxFrame && walPagesize(pWal)!=nBuf ){ |
| 53917 | rc = SQLITE_CORRUPT_BKPT; |
| 53918 | }else{ |
| 53919 | rc = walCheckpoint(pWal, eMode2, xBusy2, pBusyArg, sync_flags, zBuf); |
| 53920 | } |
| @@ -54066,10 +54277,16 @@ | |
| 54066 | SQLITE_PRIVATE int sqlite3WalFramesize(Wal *pWal){ |
| 54067 | assert( pWal==0 || pWal->readLock>=0 ); |
| 54068 | return (pWal ? pWal->szPage : 0); |
| 54069 | } |
| 54070 | #endif |
| 54071 | |
| 54072 | #endif /* #ifndef SQLITE_OMIT_WAL */ |
| 54073 | |
| 54074 | /************** End of wal.c *************************************************/ |
| 54075 | /************** Begin file btmutex.c *****************************************/ |
| @@ -54368,11 +54585,10 @@ | |
| 54368 | struct MemPage { |
| 54369 | u8 isInit; /* True if previously initialized. MUST BE FIRST! */ |
| 54370 | u8 nOverflow; /* Number of overflow cell bodies in aCell[] */ |
| 54371 | u8 intKey; /* True if table b-trees. False for index b-trees */ |
| 54372 | u8 intKeyLeaf; /* True if the leaf of an intKey table */ |
| 54373 | u8 noPayload; /* True if internal intKey page (thus w/o data) */ |
| 54374 | u8 leaf; /* True if a leaf page */ |
| 54375 | u8 hdrOffset; /* 100 for page 1. 0 otherwise */ |
| 54376 | u8 childPtrSize; /* 0 if leaf==1. 4 if leaf==0 */ |
| 54377 | u8 max1bytePayload; /* min(maxLocal,127) */ |
| 54378 | u8 bBusy; /* Prevent endless loops on corrupt database files */ |
| @@ -54955,25 +55171,10 @@ | |
| 54955 | |
| 54956 | return (p->sharable==0 || p->locked); |
| 54957 | } |
| 54958 | #endif |
| 54959 | |
| 54960 | |
| 54961 | #ifndef SQLITE_OMIT_INCRBLOB |
| 54962 | /* |
| 54963 | ** Enter and leave a mutex on a Btree given a cursor owned by that |
| 54964 | ** Btree. These entry points are used by incremental I/O and can be |
| 54965 | ** omitted if that module is not used. |
| 54966 | */ |
| 54967 | SQLITE_PRIVATE void sqlite3BtreeEnterCursor(BtCursor *pCur){ |
| 54968 | sqlite3BtreeEnter(pCur->pBtree); |
| 54969 | } |
| 54970 | SQLITE_PRIVATE void sqlite3BtreeLeaveCursor(BtCursor *pCur){ |
| 54971 | sqlite3BtreeLeave(pCur->pBtree); |
| 54972 | } |
| 54973 | #endif /* SQLITE_OMIT_INCRBLOB */ |
| 54974 | |
| 54975 | |
| 54976 | /* |
| 54977 | ** Enter the mutex on every Btree associated with a database |
| 54978 | ** connection. This is needed (for example) prior to parsing |
| 54979 | ** a statement since we will be comparing table and column names |
| @@ -55004,18 +55205,10 @@ | |
| 55004 | p = db->aDb[i].pBt; |
| 55005 | if( p ) sqlite3BtreeLeave(p); |
| 55006 | } |
| 55007 | } |
| 55008 | |
| 55009 | /* |
| 55010 | ** Return true if a particular Btree requires a lock. Return FALSE if |
| 55011 | ** no lock is ever required since it is not sharable. |
| 55012 | */ |
| 55013 | SQLITE_PRIVATE int sqlite3BtreeSharable(Btree *p){ |
| 55014 | return p->sharable; |
| 55015 | } |
| 55016 | |
| 55017 | #ifndef NDEBUG |
| 55018 | /* |
| 55019 | ** Return true if the current thread holds the database connection |
| 55020 | ** mutex and all required BtShared mutexes. |
| 55021 | ** |
| @@ -55085,10 +55278,29 @@ | |
| 55085 | p->pBt->db = p->db; |
| 55086 | } |
| 55087 | } |
| 55088 | } |
| 55089 | #endif /* if SQLITE_THREADSAFE */ |
| 55090 | #endif /* ifndef SQLITE_OMIT_SHARED_CACHE */ |
| 55091 | |
| 55092 | /************** End of btmutex.c *********************************************/ |
| 55093 | /************** Begin file btree.c *******************************************/ |
| 55094 | /* |
| @@ -55541,10 +55753,14 @@ | |
| 55541 | */ |
| 55542 | #ifdef SQLITE_DEBUG |
| 55543 | static int cursorHoldsMutex(BtCursor *p){ |
| 55544 | return sqlite3_mutex_held(p->pBt->mutex); |
| 55545 | } |
| 55546 | #endif |
| 55547 | |
| 55548 | /* |
| 55549 | ** Invalidate the overflow cache of the cursor passed as the first argument. |
| 55550 | ** on the shared btree structure pBt. |
| @@ -55877,11 +56093,11 @@ | |
| 55877 | ** saveCursorPosition(). |
| 55878 | */ |
| 55879 | static int btreeRestoreCursorPosition(BtCursor *pCur){ |
| 55880 | int rc; |
| 55881 | int skipNext; |
| 55882 | assert( cursorHoldsMutex(pCur) ); |
| 55883 | assert( pCur->eState>=CURSOR_REQUIRESEEK ); |
| 55884 | if( pCur->eState==CURSOR_FAULT ){ |
| 55885 | return pCur->skipNext; |
| 55886 | } |
| 55887 | pCur->eState = CURSOR_INVALID; |
| @@ -56166,11 +56382,10 @@ | |
| 56166 | u8 *pCell, /* Pointer to the cell text. */ |
| 56167 | CellInfo *pInfo /* Fill in this structure */ |
| 56168 | ){ |
| 56169 | assert( sqlite3_mutex_held(pPage->pBt->mutex) ); |
| 56170 | assert( pPage->leaf==0 ); |
| 56171 | assert( pPage->noPayload ); |
| 56172 | assert( pPage->childPtrSize==4 ); |
| 56173 | #ifndef SQLITE_DEBUG |
| 56174 | UNUSED_PARAMETER(pPage); |
| 56175 | #endif |
| 56176 | pInfo->nSize = 4 + getVarint(&pCell[4], (u64*)&pInfo->nKey); |
| @@ -56188,12 +56403,10 @@ | |
| 56188 | u32 nPayload; /* Number of bytes of cell payload */ |
| 56189 | u64 iKey; /* Extracted Key value */ |
| 56190 | |
| 56191 | assert( sqlite3_mutex_held(pPage->pBt->mutex) ); |
| 56192 | assert( pPage->leaf==0 || pPage->leaf==1 ); |
| 56193 | assert( pPage->intKeyLeaf || pPage->noPayload ); |
| 56194 | assert( pPage->noPayload==0 ); |
| 56195 | assert( pPage->intKeyLeaf ); |
| 56196 | assert( pPage->childPtrSize==0 ); |
| 56197 | pIter = pCell; |
| 56198 | |
| 56199 | /* The next block of code is equivalent to: |
| @@ -56258,11 +56471,10 @@ | |
| 56258 | u32 nPayload; /* Number of bytes of cell payload */ |
| 56259 | |
| 56260 | assert( sqlite3_mutex_held(pPage->pBt->mutex) ); |
| 56261 | assert( pPage->leaf==0 || pPage->leaf==1 ); |
| 56262 | assert( pPage->intKeyLeaf==0 ); |
| 56263 | assert( pPage->noPayload==0 ); |
| 56264 | pIter = pCell + pPage->childPtrSize; |
| 56265 | nPayload = *pIter; |
| 56266 | if( nPayload>=0x80 ){ |
| 56267 | u8 *pEnd = &pIter[8]; |
| 56268 | nPayload &= 0x7f; |
| @@ -56319,11 +56531,10 @@ | |
| 56319 | ** this function verifies that this invariant is not violated. */ |
| 56320 | CellInfo debuginfo; |
| 56321 | pPage->xParseCell(pPage, pCell, &debuginfo); |
| 56322 | #endif |
| 56323 | |
| 56324 | assert( pPage->noPayload==0 ); |
| 56325 | nSize = *pIter; |
| 56326 | if( nSize>=0x80 ){ |
| 56327 | pEnd = &pIter[8]; |
| 56328 | nSize &= 0x7f; |
| 56329 | do{ |
| @@ -56777,15 +56988,13 @@ | |
| 56777 | ** table b-tree page. */ |
| 56778 | assert( (PTF_LEAFDATA|PTF_INTKEY|PTF_LEAF)==13 ); |
| 56779 | pPage->intKey = 1; |
| 56780 | if( pPage->leaf ){ |
| 56781 | pPage->intKeyLeaf = 1; |
| 56782 | pPage->noPayload = 0; |
| 56783 | pPage->xParseCell = btreeParseCellPtr; |
| 56784 | }else{ |
| 56785 | pPage->intKeyLeaf = 0; |
| 56786 | pPage->noPayload = 1; |
| 56787 | pPage->xCellSize = cellSizePtrNoPayload; |
| 56788 | pPage->xParseCell = btreeParseCellPtrNoPayload; |
| 56789 | } |
| 56790 | pPage->maxLocal = pBt->maxLeaf; |
| 56791 | pPage->minLocal = pBt->minLeaf; |
| @@ -56796,11 +57005,10 @@ | |
| 56796 | /* EVIDENCE-OF: R-16571-11615 A value of 10 means the page is a leaf |
| 56797 | ** index b-tree page. */ |
| 56798 | assert( (PTF_ZERODATA|PTF_LEAF)==10 ); |
| 56799 | pPage->intKey = 0; |
| 56800 | pPage->intKeyLeaf = 0; |
| 56801 | pPage->noPayload = 0; |
| 56802 | pPage->xParseCell = btreeParseCellPtrIndex; |
| 56803 | pPage->maxLocal = pBt->maxLocal; |
| 56804 | pPage->minLocal = pBt->minLocal; |
| 56805 | }else{ |
| 56806 | /* EVIDENCE-OF: R-47608-56469 Any other value for the b-tree page type is |
| @@ -58217,11 +58425,10 @@ | |
| 58217 | ** no progress. By returning SQLITE_BUSY and not invoking the busy callback |
| 58218 | ** when A already has a read lock, we encourage A to give up and let B |
| 58219 | ** proceed. |
| 58220 | */ |
| 58221 | SQLITE_PRIVATE int sqlite3BtreeBeginTrans(Btree *p, int wrflag){ |
| 58222 | sqlite3 *pBlock = 0; |
| 58223 | BtShared *pBt = p->pBt; |
| 58224 | int rc = SQLITE_OK; |
| 58225 | |
| 58226 | sqlite3BtreeEnter(p); |
| 58227 | btreeIntegrity(p); |
| @@ -58240,31 +58447,34 @@ | |
| 58240 | rc = SQLITE_READONLY; |
| 58241 | goto trans_begun; |
| 58242 | } |
| 58243 | |
| 58244 | #ifndef SQLITE_OMIT_SHARED_CACHE |
| 58245 | /* If another database handle has already opened a write transaction |
| 58246 | ** on this shared-btree structure and a second write transaction is |
| 58247 | ** requested, return SQLITE_LOCKED. |
| 58248 | */ |
| 58249 | if( (wrflag && pBt->inTransaction==TRANS_WRITE) |
| 58250 | || (pBt->btsFlags & BTS_PENDING)!=0 |
| 58251 | ){ |
| 58252 | pBlock = pBt->pWriter->db; |
| 58253 | }else if( wrflag>1 ){ |
| 58254 | BtLock *pIter; |
| 58255 | for(pIter=pBt->pLock; pIter; pIter=pIter->pNext){ |
| 58256 | if( pIter->pBtree!=p ){ |
| 58257 | pBlock = pIter->pBtree->db; |
| 58258 | break; |
| 58259 | } |
| 58260 | } |
| 58261 | } |
| 58262 | if( pBlock ){ |
| 58263 | sqlite3ConnectionBlocked(p->db, pBlock); |
| 58264 | rc = SQLITE_LOCKED_SHAREDCACHE; |
| 58265 | goto trans_begun; |
| 58266 | } |
| 58267 | #endif |
| 58268 | |
| 58269 | /* Any read-only or read-write transaction implies a read-lock on |
| 58270 | ** page 1. So if some other shared-cache client already has a write-lock |
| @@ -59377,11 +59587,11 @@ | |
| 59377 | ** Failure is not possible. This function always returns SQLITE_OK. |
| 59378 | ** It might just as well be a procedure (returning void) but we continue |
| 59379 | ** to return an integer result code for historical reasons. |
| 59380 | */ |
| 59381 | SQLITE_PRIVATE int sqlite3BtreeDataSize(BtCursor *pCur, u32 *pSize){ |
| 59382 | assert( cursorHoldsMutex(pCur) ); |
| 59383 | assert( pCur->eState==CURSOR_VALID ); |
| 59384 | assert( pCur->iPage>=0 ); |
| 59385 | assert( pCur->iPage<BTCURSOR_MAX_DEPTH ); |
| 59386 | assert( pCur->apPage[pCur->iPage]->intKeyLeaf==1 ); |
| 59387 | getCellInfo(pCur); |
| @@ -59757,11 +59967,11 @@ | |
| 59757 | if ( pCur->eState==CURSOR_INVALID ){ |
| 59758 | return SQLITE_ABORT; |
| 59759 | } |
| 59760 | #endif |
| 59761 | |
| 59762 | assert( cursorHoldsMutex(pCur) ); |
| 59763 | rc = restoreCursorPosition(pCur); |
| 59764 | if( rc==SQLITE_OK ){ |
| 59765 | assert( pCur->eState==CURSOR_VALID ); |
| 59766 | assert( pCur->iPage>=0 && pCur->apPage[pCur->iPage] ); |
| 59767 | assert( pCur->aiIdx[pCur->iPage]<pCur->apPage[pCur->iPage]->nCell ); |
| @@ -59795,11 +60005,11 @@ | |
| 59795 | ){ |
| 59796 | u32 amt; |
| 59797 | assert( pCur!=0 && pCur->iPage>=0 && pCur->apPage[pCur->iPage]); |
| 59798 | assert( pCur->eState==CURSOR_VALID ); |
| 59799 | assert( sqlite3_mutex_held(pCur->pBtree->db->mutex) ); |
| 59800 | assert( cursorHoldsMutex(pCur) ); |
| 59801 | assert( pCur->aiIdx[pCur->iPage]<pCur->apPage[pCur->iPage]->nCell ); |
| 59802 | assert( pCur->info.nSize>0 ); |
| 59803 | assert( pCur->info.pPayload>pCur->apPage[pCur->iPage]->aData || CORRUPT_DB ); |
| 59804 | assert( pCur->info.pPayload<pCur->apPage[pCur->iPage]->aDataEnd ||CORRUPT_DB); |
| 59805 | amt = (int)(pCur->apPage[pCur->iPage]->aDataEnd - pCur->info.pPayload); |
| @@ -59841,11 +60051,11 @@ | |
| 59841 | ** vice-versa). |
| 59842 | */ |
| 59843 | static int moveToChild(BtCursor *pCur, u32 newPgno){ |
| 59844 | BtShared *pBt = pCur->pBt; |
| 59845 | |
| 59846 | assert( cursorHoldsMutex(pCur) ); |
| 59847 | assert( pCur->eState==CURSOR_VALID ); |
| 59848 | assert( pCur->iPage<BTCURSOR_MAX_DEPTH ); |
| 59849 | assert( pCur->iPage>=0 ); |
| 59850 | if( pCur->iPage>=(BTCURSOR_MAX_DEPTH-1) ){ |
| 59851 | return SQLITE_CORRUPT_BKPT; |
| @@ -59887,11 +60097,11 @@ | |
| 59887 | ** to the page we are coming from. If we are coming from the |
| 59888 | ** right-most child page then pCur->idx is set to one more than |
| 59889 | ** the largest cell index. |
| 59890 | */ |
| 59891 | static void moveToParent(BtCursor *pCur){ |
| 59892 | assert( cursorHoldsMutex(pCur) ); |
| 59893 | assert( pCur->eState==CURSOR_VALID ); |
| 59894 | assert( pCur->iPage>0 ); |
| 59895 | assert( pCur->apPage[pCur->iPage] ); |
| 59896 | assertParentIndex( |
| 59897 | pCur->apPage[pCur->iPage-1], |
| @@ -59927,11 +60137,11 @@ | |
| 59927 | */ |
| 59928 | static int moveToRoot(BtCursor *pCur){ |
| 59929 | MemPage *pRoot; |
| 59930 | int rc = SQLITE_OK; |
| 59931 | |
| 59932 | assert( cursorHoldsMutex(pCur) ); |
| 59933 | assert( CURSOR_INVALID < CURSOR_REQUIRESEEK ); |
| 59934 | assert( CURSOR_VALID < CURSOR_REQUIRESEEK ); |
| 59935 | assert( CURSOR_FAULT > CURSOR_REQUIRESEEK ); |
| 59936 | if( pCur->eState>=CURSOR_REQUIRESEEK ){ |
| 59937 | if( pCur->eState==CURSOR_FAULT ){ |
| @@ -60006,11 +60216,11 @@ | |
| 60006 | static int moveToLeftmost(BtCursor *pCur){ |
| 60007 | Pgno pgno; |
| 60008 | int rc = SQLITE_OK; |
| 60009 | MemPage *pPage; |
| 60010 | |
| 60011 | assert( cursorHoldsMutex(pCur) ); |
| 60012 | assert( pCur->eState==CURSOR_VALID ); |
| 60013 | while( rc==SQLITE_OK && !(pPage = pCur->apPage[pCur->iPage])->leaf ){ |
| 60014 | assert( pCur->aiIdx[pCur->iPage]<pPage->nCell ); |
| 60015 | pgno = get4byte(findCell(pPage, pCur->aiIdx[pCur->iPage])); |
| 60016 | rc = moveToChild(pCur, pgno); |
| @@ -60031,11 +60241,11 @@ | |
| 60031 | static int moveToRightmost(BtCursor *pCur){ |
| 60032 | Pgno pgno; |
| 60033 | int rc = SQLITE_OK; |
| 60034 | MemPage *pPage = 0; |
| 60035 | |
| 60036 | assert( cursorHoldsMutex(pCur) ); |
| 60037 | assert( pCur->eState==CURSOR_VALID ); |
| 60038 | while( !(pPage = pCur->apPage[pCur->iPage])->leaf ){ |
| 60039 | pgno = get4byte(&pPage->aData[pPage->hdrOffset+8]); |
| 60040 | pCur->aiIdx[pCur->iPage] = pPage->nCell; |
| 60041 | rc = moveToChild(pCur, pgno); |
| @@ -60052,11 +60262,11 @@ | |
| 60052 | ** or set *pRes to 1 if the table is empty. |
| 60053 | */ |
| 60054 | SQLITE_PRIVATE int sqlite3BtreeFirst(BtCursor *pCur, int *pRes){ |
| 60055 | int rc; |
| 60056 | |
| 60057 | assert( cursorHoldsMutex(pCur) ); |
| 60058 | assert( sqlite3_mutex_held(pCur->pBtree->db->mutex) ); |
| 60059 | rc = moveToRoot(pCur); |
| 60060 | if( rc==SQLITE_OK ){ |
| 60061 | if( pCur->eState==CURSOR_INVALID ){ |
| 60062 | assert( pCur->pgnoRoot==0 || pCur->apPage[pCur->iPage]->nCell==0 ); |
| @@ -60075,11 +60285,11 @@ | |
| 60075 | ** or set *pRes to 1 if the table is empty. |
| 60076 | */ |
| 60077 | SQLITE_PRIVATE int sqlite3BtreeLast(BtCursor *pCur, int *pRes){ |
| 60078 | int rc; |
| 60079 | |
| 60080 | assert( cursorHoldsMutex(pCur) ); |
| 60081 | assert( sqlite3_mutex_held(pCur->pBtree->db->mutex) ); |
| 60082 | |
| 60083 | /* If the cursor already points to the last entry, this is a no-op. */ |
| 60084 | if( CURSOR_VALID==pCur->eState && (pCur->curFlags & BTCF_AtLast)!=0 ){ |
| 60085 | #ifdef SQLITE_DEBUG |
| @@ -60153,11 +60363,11 @@ | |
| 60153 | int *pRes /* Write search results here */ |
| 60154 | ){ |
| 60155 | int rc; |
| 60156 | RecordCompare xRecordCompare; |
| 60157 | |
| 60158 | assert( cursorHoldsMutex(pCur) ); |
| 60159 | assert( sqlite3_mutex_held(pCur->pBtree->db->mutex) ); |
| 60160 | assert( pRes ); |
| 60161 | assert( (pIdxKey==0)==(pCur->pKeyInfo==0) ); |
| 60162 | |
| 60163 | /* If the cursor is already positioned at the point we are trying |
| @@ -60401,11 +60611,11 @@ | |
| 60401 | static SQLITE_NOINLINE int btreeNext(BtCursor *pCur, int *pRes){ |
| 60402 | int rc; |
| 60403 | int idx; |
| 60404 | MemPage *pPage; |
| 60405 | |
| 60406 | assert( cursorHoldsMutex(pCur) ); |
| 60407 | assert( pCur->skipNext==0 || pCur->eState!=CURSOR_VALID ); |
| 60408 | assert( *pRes==0 ); |
| 60409 | if( pCur->eState!=CURSOR_VALID ){ |
| 60410 | assert( (pCur->curFlags & BTCF_ValidOvfl)==0 ); |
| 60411 | rc = restoreCursorPosition(pCur); |
| @@ -60465,11 +60675,11 @@ | |
| 60465 | return moveToLeftmost(pCur); |
| 60466 | } |
| 60467 | } |
| 60468 | SQLITE_PRIVATE int sqlite3BtreeNext(BtCursor *pCur, int *pRes){ |
| 60469 | MemPage *pPage; |
| 60470 | assert( cursorHoldsMutex(pCur) ); |
| 60471 | assert( pRes!=0 ); |
| 60472 | assert( *pRes==0 || *pRes==1 ); |
| 60473 | assert( pCur->skipNext==0 || pCur->eState!=CURSOR_VALID ); |
| 60474 | pCur->info.nSize = 0; |
| 60475 | pCur->curFlags &= ~(BTCF_ValidNKey|BTCF_ValidOvfl); |
| @@ -60510,11 +60720,11 @@ | |
| 60510 | */ |
| 60511 | static SQLITE_NOINLINE int btreePrevious(BtCursor *pCur, int *pRes){ |
| 60512 | int rc; |
| 60513 | MemPage *pPage; |
| 60514 | |
| 60515 | assert( cursorHoldsMutex(pCur) ); |
| 60516 | assert( pRes!=0 ); |
| 60517 | assert( *pRes==0 ); |
| 60518 | assert( pCur->skipNext==0 || pCur->eState!=CURSOR_VALID ); |
| 60519 | assert( (pCur->curFlags & (BTCF_AtLast|BTCF_ValidOvfl|BTCF_ValidNKey))==0 ); |
| 60520 | assert( pCur->info.nSize==0 ); |
| @@ -60566,11 +60776,11 @@ | |
| 60566 | } |
| 60567 | } |
| 60568 | return rc; |
| 60569 | } |
| 60570 | SQLITE_PRIVATE int sqlite3BtreePrevious(BtCursor *pCur, int *pRes){ |
| 60571 | assert( cursorHoldsMutex(pCur) ); |
| 60572 | assert( pRes!=0 ); |
| 60573 | assert( *pRes==0 || *pRes==1 ); |
| 60574 | assert( pCur->skipNext==0 || pCur->eState!=CURSOR_VALID ); |
| 60575 | *pRes = 0; |
| 60576 | pCur->curFlags &= ~(BTCF_AtLast|BTCF_ValidOvfl|BTCF_ValidNKey); |
| @@ -63046,11 +63256,11 @@ | |
| 63046 | if( pCur->eState==CURSOR_FAULT ){ |
| 63047 | assert( pCur->skipNext!=SQLITE_OK ); |
| 63048 | return pCur->skipNext; |
| 63049 | } |
| 63050 | |
| 63051 | assert( cursorHoldsMutex(pCur) ); |
| 63052 | assert( (pCur->curFlags & BTCF_WriteFlag)!=0 |
| 63053 | && pBt->inTransaction==TRANS_WRITE |
| 63054 | && (pBt->btsFlags & BTS_READ_ONLY)==0 ); |
| 63055 | assert( hasSharedCacheTableLock(p, pCur->pgnoRoot, pCur->pKeyInfo!=0, 2) ); |
| 63056 | |
| @@ -63193,11 +63403,11 @@ | |
| 63193 | int iCellIdx; /* Index of cell to delete */ |
| 63194 | int iCellDepth; /* Depth of node containing pCell */ |
| 63195 | u16 szCell; /* Size of the cell being deleted */ |
| 63196 | int bSkipnext = 0; /* Leaf cursor in SKIPNEXT state */ |
| 63197 | |
| 63198 | assert( cursorHoldsMutex(pCur) ); |
| 63199 | assert( pBt->inTransaction==TRANS_WRITE ); |
| 63200 | assert( (pBt->btsFlags & BTS_READ_ONLY)==0 ); |
| 63201 | assert( pCur->curFlags & BTCF_WriteFlag ); |
| 63202 | assert( hasSharedCacheTableLock(p, pCur->pgnoRoot, pCur->pKeyInfo!=0, 2) ); |
| 63203 | assert( !hasReadConflicts(p, pCur->pgnoRoot) ); |
| @@ -64655,11 +64865,11 @@ | |
| 64655 | ** parameters that attempt to write past the end of the existing data, |
| 64656 | ** no modifications are made and SQLITE_CORRUPT is returned. |
| 64657 | */ |
| 64658 | SQLITE_PRIVATE int sqlite3BtreePutData(BtCursor *pCsr, u32 offset, u32 amt, void *z){ |
| 64659 | int rc; |
| 64660 | assert( cursorHoldsMutex(pCsr) ); |
| 64661 | assert( sqlite3_mutex_held(pCsr->pBtree->db->mutex) ); |
| 64662 | assert( pCsr->curFlags & BTCF_Incrblob ); |
| 64663 | |
| 64664 | rc = restoreCursorPosition(pCsr); |
| 64665 | if( rc!=SQLITE_OK ){ |
| @@ -64762,10 +64972,19 @@ | |
| 64762 | |
| 64763 | /* |
| 64764 | ** Return the size of the header added to each page by this module. |
| 64765 | */ |
| 64766 | SQLITE_PRIVATE int sqlite3HeaderSizeBtree(void){ return ROUND8(sizeof(MemPage)); } |
| 64767 | |
| 64768 | /************** End of btree.c ***********************************************/ |
| 64769 | /************** Begin file backup.c ******************************************/ |
| 64770 | /* |
| 64771 | ** 2009 January 28 |
| @@ -67593,12 +67812,11 @@ | |
| 67593 | ** The zWhere string must have been obtained from sqlite3_malloc(). |
| 67594 | ** This routine will take ownership of the allocated memory. |
| 67595 | */ |
| 67596 | SQLITE_PRIVATE void sqlite3VdbeAddParseSchemaOp(Vdbe *p, int iDb, char *zWhere){ |
| 67597 | int j; |
| 67598 | int addr = sqlite3VdbeAddOp3(p, OP_ParseSchema, iDb, 0, 0); |
| 67599 | sqlite3VdbeChangeP4(p, addr, zWhere, P4_DYNAMIC); |
| 67600 | for(j=0; j<p->db->nDb; j++) sqlite3VdbeUsesBtree(p, j); |
| 67601 | } |
| 67602 | |
| 67603 | /* |
| 67604 | ** Add an opcode that includes the p4 value as an integer. |
| @@ -68093,11 +68311,11 @@ | |
| 68093 | */ |
| 68094 | static void vdbeFreeOpArray(sqlite3 *db, Op *aOp, int nOp){ |
| 68095 | if( aOp ){ |
| 68096 | Op *pOp; |
| 68097 | for(pOp=aOp; pOp<&aOp[nOp]; pOp++){ |
| 68098 | freeP4(db, pOp->p4type, pOp->p4.p); |
| 68099 | #ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS |
| 68100 | sqlite3DbFree(db, pOp->zComment); |
| 68101 | #endif |
| 68102 | } |
| 68103 | } |
| @@ -68155,65 +68373,60 @@ | |
| 68155 | ** to a string or structure that is guaranteed to exist for the lifetime of |
| 68156 | ** the Vdbe. In these cases we can just copy the pointer. |
| 68157 | ** |
| 68158 | ** If addr<0 then change P4 on the most recently inserted instruction. |
| 68159 | */ |
| 68160 | SQLITE_PRIVATE void sqlite3VdbeChangeP4(Vdbe *p, int addr, const char *zP4, int n){ |
| 68161 | Op *pOp; |
| 68162 | sqlite3 *db; |
| 68163 | assert( p!=0 ); |
| 68164 | db = p->db; |
| 68165 | assert( p->magic==VDBE_MAGIC_INIT ); |
| 68166 | if( p->aOp==0 || db->mallocFailed ){ |
| 68167 | if( n!=P4_VTAB ){ |
| 68168 | freeP4(db, n, (void*)*(char**)&zP4); |
| 68169 | } |
| 68170 | return; |
| 68171 | } |
| 68172 | assert( p->nOp>0 ); |
| 68173 | assert( addr<p->nOp ); |
| 68174 | if( addr<0 ){ |
| 68175 | addr = p->nOp - 1; |
| 68176 | } |
| 68177 | pOp = &p->aOp[addr]; |
| 68178 | assert( pOp->p4type==P4_NOTUSED |
| 68179 | || pOp->p4type==P4_INT32 |
| 68180 | || pOp->p4type==P4_KEYINFO ); |
| 68181 | freeP4(db, pOp->p4type, pOp->p4.p); |
| 68182 | pOp->p4.p = 0; |
| 68183 | if( n==P4_INT32 ){ |
| 68184 | /* Note: this cast is safe, because the origin data point was an int |
| 68185 | ** that was cast to a (const char *). */ |
| 68186 | pOp->p4.i = SQLITE_PTR_TO_INT(zP4); |
| 68187 | pOp->p4type = P4_INT32; |
| 68188 | }else if( zP4==0 ){ |
| 68189 | pOp->p4.p = 0; |
| 68190 | pOp->p4type = P4_NOTUSED; |
| 68191 | }else if( n==P4_KEYINFO ){ |
| 68192 | pOp->p4.p = (void*)zP4; |
| 68193 | pOp->p4type = P4_KEYINFO; |
| 68194 | #ifdef SQLITE_ENABLE_CURSOR_HINTS |
| 68195 | }else if( n==P4_EXPR ){ |
| 68196 | /* Responsibility for deleting the Expr tree is handed over to the |
| 68197 | ** VDBE by this operation. The caller should have already invoked |
| 68198 | ** sqlite3ExprDup() or whatever other routine is needed to make a |
| 68199 | ** private copy of the tree. */ |
| 68200 | pOp->p4.pExpr = (Expr*)zP4; |
| 68201 | pOp->p4type = P4_EXPR; |
| 68202 | #endif |
| 68203 | }else if( n==P4_VTAB ){ |
| 68204 | pOp->p4.p = (void*)zP4; |
| 68205 | pOp->p4type = P4_VTAB; |
| 68206 | sqlite3VtabLock((VTable *)zP4); |
| 68207 | assert( ((VTable *)zP4)->db==p->db ); |
| 68208 | }else if( n<0 ){ |
| 68209 | pOp->p4.p = (void*)zP4; |
| 68210 | pOp->p4type = (signed char)n; |
| 68211 | }else{ |
| 68212 | if( n==0 ) n = sqlite3Strlen30(zP4); |
| 68213 | pOp->p4.z = sqlite3DbStrNDup(p->db, zP4, n); |
| 68214 | pOp->p4type = P4_DYNAMIC; |
| 68215 | } |
| 68216 | } |
| 68217 | |
| 68218 | /* |
| 68219 | ** Set the P4 on the most recently added opcode to the KeyInfo for the |
| @@ -68605,11 +68818,11 @@ | |
| 68605 | if( i!=1 && sqlite3BtreeSharable(p->db->aDb[i].pBt) ){ |
| 68606 | DbMaskSet(p->lockMask, i); |
| 68607 | } |
| 68608 | } |
| 68609 | |
| 68610 | #if !defined(SQLITE_OMIT_SHARED_CACHE) && SQLITE_THREADSAFE>0 |
| 68611 | /* |
| 68612 | ** If SQLite is compiled to support shared-cache mode and to be threadsafe, |
| 68613 | ** this routine obtains the mutex associated with each BtShared structure |
| 68614 | ** that may be accessed by the VM passed as an argument. In doing so it also |
| 68615 | ** sets the BtShared.db member of each of the BtShared structures, ensuring |
| @@ -76059,11 +76272,10 @@ | |
| 76059 | const u8 *zEndHdr; /* Pointer to first byte after the header */ |
| 76060 | u32 offset; /* Offset into the data */ |
| 76061 | u64 offset64; /* 64-bit offset */ |
| 76062 | u32 avail; /* Number of bytes of available data */ |
| 76063 | u32 t; /* A type code from the record header */ |
| 76064 | u16 fx; /* pDest->flags value */ |
| 76065 | Mem *pReg; /* PseudoTable input register */ |
| 76066 | |
| 76067 | p2 = pOp->p2; |
| 76068 | assert( pOp->p3>0 && pOp->p3<=(p->nMem-p->nCursor) ); |
| 76069 | pDest = &aMem[pOp->p3]; |
| @@ -76237,14 +76449,35 @@ | |
| 76237 | assert( p2<pC->nHdrParsed ); |
| 76238 | assert( rc==SQLITE_OK ); |
| 76239 | assert( sqlite3VdbeCheckMemInvariants(pDest) ); |
| 76240 | if( VdbeMemDynamic(pDest) ) sqlite3VdbeMemSetNull(pDest); |
| 76241 | assert( t==pC->aType[p2] ); |
| 76242 | if( pC->szRow>=aOffset[p2+1] ){ |
| 76243 | /* This is the common case where the desired content fits on the original |
| 76244 | ** page - where the content is not on an overflow page */ |
| 76245 | sqlite3VdbeSerialGet(pC->aRow+aOffset[p2], t, pDest); |
| 76246 | }else{ |
| 76247 | /* This branch happens only when content is on overflow pages */ |
| 76248 | if( ((pOp->p5 & (OPFLAG_LENGTHARG|OPFLAG_TYPEOFARG))!=0 |
| 76249 | && ((t>=12 && (t&1)==0) || (pOp->p5 & OPFLAG_TYPEOFARG)!=0)) |
| 76250 | || (len = sqlite3VdbeSerialTypeLen(t))==0 |
| @@ -76252,42 +76485,24 @@ | |
| 76252 | /* Content is irrelevant for |
| 76253 | ** 1. the typeof() function, |
| 76254 | ** 2. the length(X) function if X is a blob, and |
| 76255 | ** 3. if the content length is zero. |
| 76256 | ** So we might as well use bogus content rather than reading |
| 76257 | ** content from disk. NULL will work for the value for strings |
| 76258 | ** and blobs and whatever is in the payloadSize64 variable |
| 76259 | ** will work for everything else. */ |
| 76260 | sqlite3VdbeSerialGet(t<=13 ? (u8*)&payloadSize64 : 0, t, pDest); |
| 76261 | }else{ |
| 76262 | rc = sqlite3VdbeMemFromBtree(pCrsr, aOffset[p2], len, !pC->isTable, |
| 76263 | pDest); |
| 76264 | if( rc!=SQLITE_OK ){ |
| 76265 | goto op_column_error; |
| 76266 | } |
| 76267 | sqlite3VdbeSerialGet((const u8*)pDest->z, t, pDest); |
| 76268 | pDest->flags &= ~MEM_Ephem; |
| 76269 | } |
| 76270 | } |
| 76271 | pDest->enc = encoding; |
| 76272 | |
| 76273 | op_column_out: |
| 76274 | /* If the column value is an ephemeral string, go ahead and persist |
| 76275 | ** that string in case the cursor moves before the column value is |
| 76276 | ** used. The following code does the equivalent of Deephemeralize() |
| 76277 | ** but does it faster. */ |
| 76278 | if( (pDest->flags & MEM_Ephem)!=0 && pDest->z ){ |
| 76279 | fx = pDest->flags & (MEM_Str|MEM_Blob); |
| 76280 | assert( fx!=0 ); |
| 76281 | zData = (const u8*)pDest->z; |
| 76282 | len = pDest->n; |
| 76283 | if( sqlite3VdbeMemClearAndResize(pDest, len+2) ) goto no_mem; |
| 76284 | memcpy(pDest->z, zData, len); |
| 76285 | pDest->z[len] = 0; |
| 76286 | pDest->z[len+1] = 0; |
| 76287 | pDest->flags = fx|MEM_Term; |
| 76288 | } |
| 76289 | op_column_error: |
| 76290 | UPDATE_MAX_BLOBSIZE(pDest); |
| 76291 | REGISTER_TRACE(pOp->p3, pDest); |
| 76292 | break; |
| 76293 | } |
| @@ -81685,11 +81900,11 @@ | |
| 81685 | assert( pReadr->aBuffer==0 ); |
| 81686 | assert( pReadr->aMap==0 ); |
| 81687 | |
| 81688 | rc = vdbePmaReaderSeek(pTask, pReadr, pFile, iStart); |
| 81689 | if( rc==SQLITE_OK ){ |
| 81690 | u64 nByte; /* Size of PMA in bytes */ |
| 81691 | rc = vdbePmaReadVarint(pReadr, &nByte); |
| 81692 | pReadr->iEof = pReadr->iReadOff + nByte; |
| 81693 | *pnByte += nByte; |
| 81694 | } |
| 81695 | |
| @@ -84243,13 +84458,12 @@ | |
| 84243 | ** return the top-level walk call. |
| 84244 | ** |
| 84245 | ** The return value from this routine is WRC_Abort to abandon the tree walk |
| 84246 | ** and WRC_Continue to continue. |
| 84247 | */ |
| 84248 | SQLITE_PRIVATE int sqlite3WalkExpr(Walker *pWalker, Expr *pExpr){ |
| 84249 | int rc; |
| 84250 | if( pExpr==0 ) return WRC_Continue; |
| 84251 | testcase( ExprHasProperty(pExpr, EP_TokenOnly) ); |
| 84252 | testcase( ExprHasProperty(pExpr, EP_Reduced) ); |
| 84253 | rc = pWalker->xExprCallback(pWalker, pExpr); |
| 84254 | if( rc==WRC_Continue |
| 84255 | && !ExprHasProperty(pExpr,EP_TokenOnly) ){ |
| @@ -84260,10 +84474,13 @@ | |
| 84260 | }else{ |
| 84261 | if( sqlite3WalkExprList(pWalker, pExpr->x.pList) ) return WRC_Abort; |
| 84262 | } |
| 84263 | } |
| 84264 | return rc & WRC_Abort; |
| 84265 | } |
| 84266 | |
| 84267 | /* |
| 84268 | ** Call sqlite3WalkExpr() for every expression in list p or until |
| 84269 | ** an abort request is seen. |
| @@ -86327,12 +86544,13 @@ | |
| 86327 | || sqlite3GetInt32(pToken->z, &iValue)==0 ){ |
| 86328 | nExtra = pToken->n+1; |
| 86329 | assert( iValue>=0 ); |
| 86330 | } |
| 86331 | } |
| 86332 | pNew = sqlite3DbMallocZero(db, sizeof(Expr)+nExtra); |
| 86333 | if( pNew ){ |
| 86334 | pNew->op = (u8)op; |
| 86335 | pNew->iAgg = -1; |
| 86336 | if( pToken ){ |
| 86337 | if( nExtra==0 ){ |
| 86338 | pNew->flags |= EP_IntValue; |
| @@ -93723,19 +93941,10 @@ | |
| 93723 | ** COMMIT |
| 93724 | ** ROLLBACK |
| 93725 | */ |
| 93726 | /* #include "sqliteInt.h" */ |
| 93727 | |
| 93728 | /* |
| 93729 | ** This routine is called when a new SQL statement is beginning to |
| 93730 | ** be parsed. Initialize the pParse structure as needed. |
| 93731 | */ |
| 93732 | SQLITE_PRIVATE void sqlite3BeginParse(Parse *pParse, int explainFlag){ |
| 93733 | pParse->explain = (u8)explainFlag; |
| 93734 | pParse->nVar = 0; |
| 93735 | } |
| 93736 | |
| 93737 | #ifndef SQLITE_OMIT_SHARED_CACHE |
| 93738 | /* |
| 93739 | ** The TableLock structure is only used by the sqlite3TableLock() and |
| 93740 | ** codeTableLocks() functions. |
| 93741 | */ |
| @@ -100072,14 +100281,14 @@ | |
| 100072 | |
| 100073 | /* |
| 100074 | ** A structure defining how to do GLOB-style comparisons. |
| 100075 | */ |
| 100076 | struct compareInfo { |
| 100077 | u8 matchAll; |
| 100078 | u8 matchOne; |
| 100079 | u8 matchSet; |
| 100080 | u8 noCase; |
| 100081 | }; |
| 100082 | |
| 100083 | /* |
| 100084 | ** For LIKE and GLOB matching on EBCDIC machines, assume that every |
| 100085 | ** character is exactly one byte in size. Also, provde the Utf8Read() |
| @@ -100138,26 +100347,18 @@ | |
| 100138 | */ |
| 100139 | static int patternCompare( |
| 100140 | const u8 *zPattern, /* The glob pattern */ |
| 100141 | const u8 *zString, /* The string to compare against the glob */ |
| 100142 | const struct compareInfo *pInfo, /* Information about how to do the compare */ |
| 100143 | u32 esc /* The escape character */ |
| 100144 | ){ |
| 100145 | u32 c, c2; /* Next pattern and input string chars */ |
| 100146 | u32 matchOne = pInfo->matchOne; /* "?" or "_" */ |
| 100147 | u32 matchAll = pInfo->matchAll; /* "*" or "%" */ |
| 100148 | u32 matchOther; /* "[" or the escape character */ |
| 100149 | u8 noCase = pInfo->noCase; /* True if uppercase==lowercase */ |
| 100150 | const u8 *zEscaped = 0; /* One past the last escaped input char */ |
| 100151 | |
| 100152 | /* The GLOB operator does not have an ESCAPE clause. And LIKE does not |
| 100153 | ** have the matchSet operator. So we either have to look for one or |
| 100154 | ** the other, never both. Hence the single variable matchOther is used |
| 100155 | ** to store the one we have to look for. |
| 100156 | */ |
| 100157 | matchOther = esc ? esc : pInfo->matchSet; |
| 100158 | |
| 100159 | while( (c = Utf8Read(zPattern))!=0 ){ |
| 100160 | if( c==matchAll ){ /* Match "*" */ |
| 100161 | /* Skip over multiple "*" characters in the pattern. If there |
| 100162 | ** are also "?" characters, skip those as well, but consume a |
| 100163 | ** single character of the input string for each "?" skipped */ |
| @@ -100167,19 +100368,19 @@ | |
| 100167 | } |
| 100168 | } |
| 100169 | if( c==0 ){ |
| 100170 | return 1; /* "*" at the end of the pattern matches */ |
| 100171 | }else if( c==matchOther ){ |
| 100172 | if( esc ){ |
| 100173 | c = sqlite3Utf8Read(&zPattern); |
| 100174 | if( c==0 ) return 0; |
| 100175 | }else{ |
| 100176 | /* "[...]" immediately follows the "*". We have to do a slow |
| 100177 | ** recursive search in this case, but it is an unusual case. */ |
| 100178 | assert( matchOther<0x80 ); /* '[' is a single-byte character */ |
| 100179 | while( *zString |
| 100180 | && patternCompare(&zPattern[-1],zString,pInfo,esc)==0 ){ |
| 100181 | SQLITE_SKIP_UTF8(zString); |
| 100182 | } |
| 100183 | return *zString!=0; |
| 100184 | } |
| 100185 | } |
| @@ -100201,22 +100402,22 @@ | |
| 100201 | }else{ |
| 100202 | cx = c; |
| 100203 | } |
| 100204 | while( (c2 = *(zString++))!=0 ){ |
| 100205 | if( c2!=c && c2!=cx ) continue; |
| 100206 | if( patternCompare(zPattern,zString,pInfo,esc) ) return 1; |
| 100207 | } |
| 100208 | }else{ |
| 100209 | while( (c2 = Utf8Read(zString))!=0 ){ |
| 100210 | if( c2!=c ) continue; |
| 100211 | if( patternCompare(zPattern,zString,pInfo,esc) ) return 1; |
| 100212 | } |
| 100213 | } |
| 100214 | return 0; |
| 100215 | } |
| 100216 | if( c==matchOther ){ |
| 100217 | if( esc ){ |
| 100218 | c = sqlite3Utf8Read(&zPattern); |
| 100219 | if( c==0 ) return 0; |
| 100220 | zEscaped = zPattern; |
| 100221 | }else{ |
| 100222 | u32 prior_c = 0; |
| @@ -100265,11 +100466,11 @@ | |
| 100265 | |
| 100266 | /* |
| 100267 | ** The sqlite3_strglob() interface. |
| 100268 | */ |
| 100269 | SQLITE_API int SQLITE_STDCALL sqlite3_strglob(const char *zGlobPattern, const char *zString){ |
| 100270 | return patternCompare((u8*)zGlobPattern, (u8*)zString, &globInfo, 0)==0; |
| 100271 | } |
| 100272 | |
| 100273 | /* |
| 100274 | ** The sqlite3_strlike() interface. |
| 100275 | */ |
| @@ -100303,13 +100504,14 @@ | |
| 100303 | sqlite3_context *context, |
| 100304 | int argc, |
| 100305 | sqlite3_value **argv |
| 100306 | ){ |
| 100307 | const unsigned char *zA, *zB; |
| 100308 | u32 escape = 0; |
| 100309 | int nPat; |
| 100310 | sqlite3 *db = sqlite3_context_db_handle(context); |
| 100311 | |
| 100312 | #ifdef SQLITE_LIKE_DOESNT_MATCH_BLOBS |
| 100313 | if( sqlite3_value_type(argv[0])==SQLITE_BLOB |
| 100314 | || sqlite3_value_type(argv[1])==SQLITE_BLOB |
| 100315 | ){ |
| @@ -100345,17 +100547,17 @@ | |
| 100345 | sqlite3_result_error(context, |
| 100346 | "ESCAPE expression must be a single character", -1); |
| 100347 | return; |
| 100348 | } |
| 100349 | escape = sqlite3Utf8Read(&zEsc); |
| 100350 | } |
| 100351 | if( zA && zB ){ |
| 100352 | struct compareInfo *pInfo = sqlite3_user_data(context); |
| 100353 | #ifdef SQLITE_TEST |
| 100354 | sqlite3_like_count++; |
| 100355 | #endif |
| 100356 | |
| 100357 | sqlite3_result_int(context, patternCompare(zB, zA, pInfo, escape)); |
| 100358 | } |
| 100359 | } |
| 100360 | |
| 100361 | /* |
| @@ -109688,10 +109890,11 @@ | |
| 109688 | int nOBSat; /* Number of ORDER BY terms satisfied by indices */ |
| 109689 | int iECursor; /* Cursor number for the sorter */ |
| 109690 | int regReturn; /* Register holding block-output return address */ |
| 109691 | int labelBkOut; /* Start label for the block-output subroutine */ |
| 109692 | int addrSortIndex; /* Address of the OP_SorterOpen or OP_OpenEphemeral */ |
| 109693 | u8 sortFlags; /* Zero or more SORTFLAG_* bits */ |
| 109694 | }; |
| 109695 | #define SORTFLAG_UseSorter 0x01 /* Use SorterOpen instead of OpenEphemeral */ |
| 109696 | |
| 109697 | /* |
| @@ -109745,33 +109948,41 @@ | |
| 109745 | Expr *pOffset /* OFFSET value. NULL means no offset */ |
| 109746 | ){ |
| 109747 | Select *pNew; |
| 109748 | Select standin; |
| 109749 | sqlite3 *db = pParse->db; |
| 109750 | pNew = sqlite3DbMallocZero(db, sizeof(*pNew) ); |
| 109751 | if( pNew==0 ){ |
| 109752 | assert( db->mallocFailed ); |
| 109753 | pNew = &standin; |
| 109754 | memset(pNew, 0, sizeof(*pNew)); |
| 109755 | } |
| 109756 | if( pEList==0 ){ |
| 109757 | pEList = sqlite3ExprListAppend(pParse, 0, sqlite3Expr(db,TK_ASTERISK,0)); |
| 109758 | } |
| 109759 | pNew->pEList = pEList; |
| 109760 | if( pSrc==0 ) pSrc = sqlite3DbMallocZero(db, sizeof(*pSrc)); |
| 109761 | pNew->pSrc = pSrc; |
| 109762 | pNew->pWhere = pWhere; |
| 109763 | pNew->pGroupBy = pGroupBy; |
| 109764 | pNew->pHaving = pHaving; |
| 109765 | pNew->pOrderBy = pOrderBy; |
| 109766 | pNew->selFlags = selFlags; |
| 109767 | pNew->op = TK_SELECT; |
| 109768 | pNew->pLimit = pLimit; |
| 109769 | pNew->pOffset = pOffset; |
| 109770 | assert( pOffset==0 || pLimit!=0 || pParse->nErr>0 || db->mallocFailed!=0 ); |
| 109771 | pNew->addrOpenEphm[0] = -1; |
| 109772 | pNew->addrOpenEphm[1] = -1; |
| 109773 | if( db->mallocFailed ) { |
| 109774 | clearSelect(db, pNew, pNew!=&standin); |
| 109775 | pNew = 0; |
| 109776 | }else{ |
| 109777 | assert( pNew->pSrc!=0 || pParse->nErr>0 ); |
| @@ -110142,10 +110353,11 @@ | |
| 110142 | int nBase = nExpr + bSeq + nData; /* Fields in sorter record */ |
| 110143 | int regBase; /* Regs for sorter record */ |
| 110144 | int regRecord = ++pParse->nMem; /* Assembled sorter record */ |
| 110145 | int nOBSat = pSort->nOBSat; /* ORDER BY terms to skip */ |
| 110146 | int op; /* Opcode to add sorter record to sorter */ |
| 110147 | |
| 110148 | assert( bSeq==0 || bSeq==1 ); |
| 110149 | assert( nData==1 || regData==regOrigData ); |
| 110150 | if( nPrefixReg ){ |
| 110151 | assert( nPrefixReg==nExpr+bSeq ); |
| @@ -110152,19 +110364,21 @@ | |
| 110152 | regBase = regData - nExpr - bSeq; |
| 110153 | }else{ |
| 110154 | regBase = pParse->nMem + 1; |
| 110155 | pParse->nMem += nBase; |
| 110156 | } |
| 110157 | sqlite3ExprCodeExprList(pParse, pSort->pOrderBy, regBase, regOrigData, |
| 110158 | SQLITE_ECEL_DUP|SQLITE_ECEL_REF); |
| 110159 | if( bSeq ){ |
| 110160 | sqlite3VdbeAddOp2(v, OP_Sequence, pSort->iECursor, regBase+nExpr); |
| 110161 | } |
| 110162 | if( nPrefixReg==0 ){ |
| 110163 | sqlite3ExprCodeMove(pParse, regData, regBase+nExpr+bSeq, nData); |
| 110164 | } |
| 110165 | |
| 110166 | sqlite3VdbeAddOp3(v, OP_MakeRecord, regBase+nOBSat, nBase-nOBSat, regRecord); |
| 110167 | if( nOBSat>0 ){ |
| 110168 | int regPrevKey; /* The first nOBSat columns of the previous row */ |
| 110169 | int addrFirst; /* Address of the OP_IfNot opcode */ |
| 110170 | int addrJmp; /* Address of the OP_Jump opcode */ |
| @@ -110195,10 +110409,14 @@ | |
| 110195 | sqlite3VdbeAddOp3(v, OP_Jump, addrJmp+1, 0, addrJmp+1); VdbeCoverage(v); |
| 110196 | pSort->labelBkOut = sqlite3VdbeMakeLabel(v); |
| 110197 | pSort->regReturn = ++pParse->nMem; |
| 110198 | sqlite3VdbeAddOp2(v, OP_Gosub, pSort->regReturn, pSort->labelBkOut); |
| 110199 | sqlite3VdbeAddOp1(v, OP_ResetSorter, pSort->iECursor); |
| 110200 | sqlite3VdbeJumpHere(v, addrFirst); |
| 110201 | sqlite3ExprCodeMove(pParse, regBase, regPrevKey, pSort->nOBSat); |
| 110202 | sqlite3VdbeJumpHere(v, addrJmp); |
| 110203 | } |
| 110204 | if( pSort->sortFlags & SORTFLAG_UseSorter ){ |
| @@ -110205,18 +110423,12 @@ | |
| 110205 | op = OP_SorterInsert; |
| 110206 | }else{ |
| 110207 | op = OP_IdxInsert; |
| 110208 | } |
| 110209 | sqlite3VdbeAddOp2(v, op, pSort->iECursor, regRecord); |
| 110210 | if( pSelect->iLimit ){ |
| 110211 | int addr; |
| 110212 | int iLimit; |
| 110213 | if( pSelect->iOffset ){ |
| 110214 | iLimit = pSelect->iOffset+1; |
| 110215 | }else{ |
| 110216 | iLimit = pSelect->iLimit; |
| 110217 | } |
| 110218 | addr = sqlite3VdbeAddOp3(v, OP_IfNotZero, iLimit, 0, 1); VdbeCoverage(v); |
| 110219 | sqlite3VdbeAddOp1(v, OP_Last, pSort->iECursor); |
| 110220 | sqlite3VdbeAddOp1(v, OP_Delete, pSort->iECursor); |
| 110221 | sqlite3VdbeJumpHere(v, addr); |
| 110222 | } |
| @@ -110816,11 +111028,11 @@ | |
| 110816 | SortCtx *pSort, /* Information on the ORDER BY clause */ |
| 110817 | int nColumn, /* Number of columns of data */ |
| 110818 | SelectDest *pDest /* Write the sorted results here */ |
| 110819 | ){ |
| 110820 | Vdbe *v = pParse->pVdbe; /* The prepared statement */ |
| 110821 | int addrBreak = sqlite3VdbeMakeLabel(v); /* Jump here to exit loop */ |
| 110822 | int addrContinue = sqlite3VdbeMakeLabel(v); /* Jump here for next cycle */ |
| 110823 | int addr; |
| 110824 | int addrOnce = 0; |
| 110825 | int iTab; |
| 110826 | ExprList *pOrderBy = pSort->pOrderBy; |
| @@ -110835,10 +111047,11 @@ | |
| 110835 | int bSeq; /* True if sorter record includes seq. no. */ |
| 110836 | #ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS |
| 110837 | struct ExprList_item *aOutEx = p->pEList->a; |
| 110838 | #endif |
| 110839 | |
| 110840 | if( pSort->labelBkOut ){ |
| 110841 | sqlite3VdbeAddOp2(v, OP_Gosub, pSort->regReturn, pSort->labelBkOut); |
| 110842 | sqlite3VdbeGoto(v, addrBreak); |
| 110843 | sqlite3VdbeResolveLabel(v, pSort->labelBkOut); |
| 110844 | } |
| @@ -126875,12 +127088,11 @@ | |
| 126875 | testcase( pWInfo->eOnePass==ONEPASS_OFF && pTab->nCol==BMS ); |
| 126876 | if( pWInfo->eOnePass==ONEPASS_OFF && pTab->nCol<BMS && HasRowid(pTab) ){ |
| 126877 | Bitmask b = pTabItem->colUsed; |
| 126878 | int n = 0; |
| 126879 | for(; b; b=b>>1, n++){} |
| 126880 | sqlite3VdbeChangeP4(v, sqlite3VdbeCurrentAddr(v)-1, |
| 126881 | SQLITE_INT_TO_PTR(n), P4_INT32); |
| 126882 | assert( n<=pTab->nCol ); |
| 126883 | } |
| 126884 | #ifdef SQLITE_ENABLE_CURSOR_HINTS |
| 126885 | if( pLoop->u.btree.pIndex!=0 ){ |
| 126886 | sqlite3VdbeChangeP5(v, OPFLAG_SEEKEQ|bFordelete); |
| @@ -129343,18 +129555,15 @@ | |
| 129343 | ** { ... } // User supplied code |
| 129344 | ** #line <lineno> <thisfile> |
| 129345 | ** break; |
| 129346 | */ |
| 129347 | /********** Begin reduce actions **********************************************/ |
| 129348 | case 5: /* explain ::= */ |
| 129349 | { sqlite3BeginParse(pParse, 0); } |
| 129350 | break; |
| 129351 | case 6: /* explain ::= EXPLAIN */ |
| 129352 | { sqlite3BeginParse(pParse, 1); } |
| 129353 | break; |
| 129354 | case 7: /* explain ::= EXPLAIN QUERY PLAN */ |
| 129355 | { sqlite3BeginParse(pParse, 2); } |
| 129356 | break; |
| 129357 | case 8: /* cmdx ::= cmd */ |
| 129358 | { sqlite3FinishCoding(pParse); } |
| 129359 | break; |
| 129360 | case 9: /* cmd ::= BEGIN transtype trans_opt */ |
| @@ -130525,10 +130734,11 @@ | |
| 130525 | /* (0) input ::= cmdlist */ yytestcase(yyruleno==0); |
| 130526 | /* (1) cmdlist ::= cmdlist ecmd */ yytestcase(yyruleno==1); |
| 130527 | /* (2) cmdlist ::= ecmd */ yytestcase(yyruleno==2); |
| 130528 | /* (3) ecmd ::= SEMI */ yytestcase(yyruleno==3); |
| 130529 | /* (4) ecmd ::= explain cmdx SEMI */ yytestcase(yyruleno==4); |
| 130530 | /* (10) trans_opt ::= */ yytestcase(yyruleno==10); |
| 130531 | /* (11) trans_opt ::= TRANSACTION */ yytestcase(yyruleno==11); |
| 130532 | /* (12) trans_opt ::= TRANSACTION nm */ yytestcase(yyruleno==12); |
| 130533 | /* (20) savepoint_opt ::= SAVEPOINT */ yytestcase(yyruleno==20); |
| 130534 | /* (21) savepoint_opt ::= */ yytestcase(yyruleno==21); |
| @@ -135450,10 +135660,13 @@ | |
| 135450 | *(sqlite3_file**)pArg = fd; |
| 135451 | rc = SQLITE_OK; |
| 135452 | }else if( op==SQLITE_FCNTL_VFS_POINTER ){ |
| 135453 | *(sqlite3_vfs**)pArg = sqlite3PagerVfs(pPager); |
| 135454 | rc = SQLITE_OK; |
| 135455 | }else if( fd->pMethods ){ |
| 135456 | rc = sqlite3OsFileControl(fd, op, pArg); |
| 135457 | }else{ |
| 135458 | rc = SQLITE_NOTFOUND; |
| 135459 | } |
| @@ -161083,11 +161296,11 @@ | |
| 161083 | */ |
| 161084 | static void *rbuMalloc(sqlite3rbu *p, int nByte){ |
| 161085 | void *pRet = 0; |
| 161086 | if( p->rc==SQLITE_OK ){ |
| 161087 | assert( nByte>0 ); |
| 161088 | pRet = sqlite3_malloc(nByte); |
| 161089 | if( pRet==0 ){ |
| 161090 | p->rc = SQLITE_NOMEM; |
| 161091 | }else{ |
| 161092 | memset(pRet, 0, nByte); |
| 161093 | } |
| @@ -161129,12 +161342,12 @@ | |
| 161129 | static char *rbuStrndup(const char *zStr, int *pRc){ |
| 161130 | char *zRet = 0; |
| 161131 | |
| 161132 | assert( *pRc==SQLITE_OK ); |
| 161133 | if( zStr ){ |
| 161134 | int nCopy = strlen(zStr) + 1; |
| 161135 | zRet = (char*)sqlite3_malloc(nCopy); |
| 161136 | if( zRet ){ |
| 161137 | memcpy(zRet, zStr, nCopy); |
| 161138 | }else{ |
| 161139 | *pRc = SQLITE_NOMEM; |
| 161140 | } |
| @@ -162478,11 +162691,11 @@ | |
| 162478 | |
| 162479 | pRbu->pgsz = iAmt; |
| 162480 | if( pRbu->nFrame==pRbu->nFrameAlloc ){ |
| 162481 | int nNew = (pRbu->nFrameAlloc ? pRbu->nFrameAlloc : 64) * 2; |
| 162482 | RbuFrame *aNew; |
| 162483 | aNew = (RbuFrame*)sqlite3_realloc(pRbu->aFrame, nNew * sizeof(RbuFrame)); |
| 162484 | if( aNew==0 ) return SQLITE_NOMEM; |
| 162485 | pRbu->aFrame = aNew; |
| 162486 | pRbu->nFrameAlloc = nNew; |
| 162487 | } |
| 162488 | |
| @@ -162543,11 +162756,11 @@ | |
| 162543 | |
| 162544 | nChar = MultiByteToWideChar(CP_UTF8, 0, zFilename, -1, NULL, 0); |
| 162545 | if( nChar==0 ){ |
| 162546 | return 0; |
| 162547 | } |
| 162548 | zWideFilename = sqlite3_malloc( nChar*sizeof(zWideFilename[0]) ); |
| 162549 | if( zWideFilename==0 ){ |
| 162550 | return 0; |
| 162551 | } |
| 162552 | memset(zWideFilename, 0, nChar*sizeof(zWideFilename[0])); |
| 162553 | nChar = MultiByteToWideChar(CP_UTF8, 0, zFilename, -1, zWideFilename, |
| @@ -163177,15 +163390,16 @@ | |
| 163177 | const char *zTarget, |
| 163178 | const char *zRbu, |
| 163179 | const char *zState |
| 163180 | ){ |
| 163181 | sqlite3rbu *p; |
| 163182 | int nTarget = strlen(zTarget); |
| 163183 | int nRbu = strlen(zRbu); |
| 163184 | int nState = zState ? strlen(zState) : 0; |
| 163185 | |
| 163186 | p = (sqlite3rbu*)sqlite3_malloc(sizeof(sqlite3rbu)+nTarget+1+nRbu+1+nState+1); |
| 163187 | if( p ){ |
| 163188 | RbuState *pState = 0; |
| 163189 | |
| 163190 | /* Create the custom VFS. */ |
| 163191 | memset(p, 0, sizeof(sqlite3rbu)); |
| @@ -163318,11 +163532,11 @@ | |
| 163318 | ** the pattern "rbu_imp_[0-9]*". |
| 163319 | */ |
| 163320 | static void rbuEditErrmsg(sqlite3rbu *p){ |
| 163321 | if( p->rc==SQLITE_CONSTRAINT && p->zErrmsg ){ |
| 163322 | int i; |
| 163323 | int nErrmsg = strlen(p->zErrmsg); |
| 163324 | for(i=0; i<(nErrmsg-8); i++){ |
| 163325 | if( memcmp(&p->zErrmsg[i], "rbu_imp_", 8)==0 ){ |
| 163326 | int nDel = 8; |
| 163327 | while( p->zErrmsg[i+nDel]>='0' && p->zErrmsg[i+nDel]<='9' ) nDel++; |
| 163328 | memmove(&p->zErrmsg[i], &p->zErrmsg[i+nDel], nErrmsg + 1 - i - nDel); |
| @@ -163782,11 +163996,11 @@ | |
| 163782 | ** instead of a file on disk. */ |
| 163783 | assert( p->openFlags & (SQLITE_OPEN_MAIN_DB|SQLITE_OPEN_TEMP_DB) ); |
| 163784 | if( eStage==RBU_STAGE_OAL || eStage==RBU_STAGE_MOVE ){ |
| 163785 | if( iRegion<=p->nShm ){ |
| 163786 | int nByte = (iRegion+1) * sizeof(char*); |
| 163787 | char **apNew = (char**)sqlite3_realloc(p->apShm, nByte); |
| 163788 | if( apNew==0 ){ |
| 163789 | rc = SQLITE_NOMEM; |
| 163790 | }else{ |
| 163791 | memset(&apNew[p->nShm], 0, sizeof(char*) * (1 + iRegion - p->nShm)); |
| 163792 | p->apShm = apNew; |
| @@ -163793,11 +164007,11 @@ | |
| 163793 | p->nShm = iRegion+1; |
| 163794 | } |
| 163795 | } |
| 163796 | |
| 163797 | if( rc==SQLITE_OK && p->apShm[iRegion]==0 ){ |
| 163798 | char *pNew = (char*)sqlite3_malloc(szRegion); |
| 163799 | if( pNew==0 ){ |
| 163800 | rc = SQLITE_NOMEM; |
| 163801 | }else{ |
| 163802 | memset(pNew, 0, szRegion); |
| 163803 | p->apShm[iRegion] = pNew; |
| @@ -163903,11 +164117,11 @@ | |
| 163903 | /* A main database has just been opened. The following block sets |
| 163904 | ** (pFd->zWal) to point to a buffer owned by SQLite that contains |
| 163905 | ** the name of the *-wal file this db connection will use. SQLite |
| 163906 | ** happens to pass a pointer to this buffer when using xAccess() |
| 163907 | ** or xOpen() to operate on the *-wal file. */ |
| 163908 | int n = strlen(zName); |
| 163909 | const char *z = &zName[n]; |
| 163910 | if( flags & SQLITE_OPEN_URI ){ |
| 163911 | int odd = 0; |
| 163912 | while( 1 ){ |
| 163913 | if( z[0]==0 ){ |
| @@ -163929,12 +164143,12 @@ | |
| 163929 | if( pDb->pRbu && pDb->pRbu->eStage==RBU_STAGE_OAL ){ |
| 163930 | /* This call is to open a *-wal file. Intead, open the *-oal. This |
| 163931 | ** code ensures that the string passed to xOpen() is terminated by a |
| 163932 | ** pair of '\0' bytes in case the VFS attempts to extract a URI |
| 163933 | ** parameter from it. */ |
| 163934 | int nCopy = strlen(zName); |
| 163935 | char *zCopy = sqlite3_malloc(nCopy+2); |
| 163936 | if( zCopy ){ |
| 163937 | memcpy(zCopy, zName, nCopy); |
| 163938 | zCopy[nCopy-3] = 'o'; |
| 163939 | zCopy[nCopy] = '\0'; |
| 163940 | zCopy[nCopy+1] = '\0'; |
| @@ -164159,17 +164373,17 @@ | |
| 164159 | 0, /* xCurrentTimeInt64 (version 2) */ |
| 164160 | 0, 0, 0 /* Unimplemented version 3 methods */ |
| 164161 | }; |
| 164162 | |
| 164163 | rbu_vfs *pNew = 0; /* Newly allocated VFS */ |
| 164164 | int nName; |
| 164165 | int rc = SQLITE_OK; |
| 164166 | |
| 164167 | int nByte; |
| 164168 | nName = strlen(zName); |
| 164169 | nByte = sizeof(rbu_vfs) + nName + 1; |
| 164170 | pNew = (rbu_vfs*)sqlite3_malloc(nByte); |
| 164171 | if( pNew==0 ){ |
| 164172 | rc = SQLITE_NOMEM; |
| 164173 | }else{ |
| 164174 | sqlite3_vfs *pParent; /* Parent VFS */ |
| 164175 | memset(pNew, 0, nByte); |
| @@ -167174,10 +167388,13 @@ | |
| 167174 | ** If parameter iCol is greater than or equal to the number of columns |
| 167175 | ** in the table, SQLITE_RANGE is returned. Or, if an error occurs (e.g. |
| 167176 | ** an OOM condition or IO error), an appropriate SQLite error code is |
| 167177 | ** returned. |
| 167178 | ** |
| 167179 | ** xColumnText: |
| 167180 | ** This function attempts to retrieve the text of column iCol of the |
| 167181 | ** current document. If successful, (*pz) is set to point to a buffer |
| 167182 | ** containing the text in utf-8 encoding, (*pn) is set to the size in bytes |
| 167183 | ** (not characters) of the buffer and SQLITE_OK is returned. Otherwise, |
| @@ -167194,18 +167411,32 @@ | |
| 167194 | ** xInstCount: |
| 167195 | ** Set *pnInst to the total number of occurrences of all phrases within |
| 167196 | ** the query within the current row. Return SQLITE_OK if successful, or |
| 167197 | ** an error code (i.e. SQLITE_NOMEM) if an error occurs. |
| 167198 | ** |
| 167199 | ** xInst: |
| 167200 | ** Query for the details of phrase match iIdx within the current row. |
| 167201 | ** Phrase matches are numbered starting from zero, so the iIdx argument |
| 167202 | ** should be greater than or equal to zero and smaller than the value |
| 167203 | ** output by xInstCount(). |
| 167204 | ** |
| 167205 | ** Returns SQLITE_OK if successful, or an error code (i.e. SQLITE_NOMEM) |
| 167206 | ** if an error occurs. |
| 167207 | ** |
| 167208 | ** xRowid: |
| 167209 | ** Returns the rowid of the current row. |
| 167210 | ** |
| 167211 | ** xTokenize: |
| @@ -167286,25 +167517,63 @@ | |
| 167286 | ** through instances of phrase iPhrase, use the following code: |
| 167287 | ** |
| 167288 | ** Fts5PhraseIter iter; |
| 167289 | ** int iCol, iOff; |
| 167290 | ** for(pApi->xPhraseFirst(pFts, iPhrase, &iter, &iCol, &iOff); |
| 167291 | ** iOff>=0; |
| 167292 | ** pApi->xPhraseNext(pFts, &iter, &iCol, &iOff) |
| 167293 | ** ){ |
| 167294 | ** // An instance of phrase iPhrase at offset iOff of column iCol |
| 167295 | ** } |
| 167296 | ** |
| 167297 | ** The Fts5PhraseIter structure is defined above. Applications should not |
| 167298 | ** modify this structure directly - it should only be used as shown above |
| 167299 | ** with the xPhraseFirst() and xPhraseNext() API methods. |
| 167300 | ** |
| 167301 | ** xPhraseNext() |
| 167302 | ** See xPhraseFirst above. |
| 167303 | */ |
| 167304 | struct Fts5ExtensionApi { |
| 167305 | int iVersion; /* Currently always set to 1 */ |
| 167306 | |
| 167307 | void *(*xUserData)(Fts5Context*); |
| 167308 | |
| 167309 | int (*xColumnCount)(Fts5Context*); |
| 167310 | int (*xRowCount)(Fts5Context*, sqlite3_int64 *pnRow); |
| @@ -167330,12 +167599,15 @@ | |
| 167330 | int(*)(const Fts5ExtensionApi*,Fts5Context*,void*) |
| 167331 | ); |
| 167332 | int (*xSetAuxdata)(Fts5Context*, void *pAux, void(*xDelete)(void*)); |
| 167333 | void *(*xGetAuxdata)(Fts5Context*, int bClear); |
| 167334 | |
| 167335 | void (*xPhraseFirst)(Fts5Context*, int iPhrase, Fts5PhraseIter*, int*, int*); |
| 167336 | void (*xPhraseNext)(Fts5Context*, Fts5PhraseIter*, int *piCol, int *piOff); |
| 167337 | }; |
| 167338 | |
| 167339 | /* |
| 167340 | ** CUSTOM AUXILIARY FUNCTIONS |
| 167341 | *************************************************************************/ |
| @@ -167762,10 +168034,11 @@ | |
| 167762 | int *aPrefix; /* Sizes in bytes of nPrefix prefix indexes */ |
| 167763 | int eContent; /* An FTS5_CONTENT value */ |
| 167764 | char *zContent; /* content table */ |
| 167765 | char *zContentRowid; /* "content_rowid=" option value */ |
| 167766 | int bColumnsize; /* "columnsize=" option value (dflt==1) */ |
| 167767 | char *zContentExprlist; |
| 167768 | Fts5Tokenizer *pTok; |
| 167769 | fts5_tokenizer *pTokApi; |
| 167770 | |
| 167771 | /* Values loaded from the %_config table */ |
| @@ -167790,10 +168063,13 @@ | |
| 167790 | |
| 167791 | #define FTS5_CONTENT_NORMAL 0 |
| 167792 | #define FTS5_CONTENT_NONE 1 |
| 167793 | #define FTS5_CONTENT_EXTERNAL 2 |
| 167794 | |
| 167795 | |
| 167796 | |
| 167797 | |
| 167798 | static int sqlite3Fts5ConfigParse( |
| 167799 | Fts5Global*, sqlite3*, int, const char **, Fts5Config**, char** |
| @@ -167903,10 +168179,17 @@ | |
| 167903 | static char *sqlite3Fts5Strndup(int *pRc, const char *pIn, int nIn); |
| 167904 | |
| 167905 | /* Character set tests (like isspace(), isalpha() etc.) */ |
| 167906 | static int sqlite3Fts5IsBareword(char t); |
| 167907 | |
| 167908 | /* |
| 167909 | ** End of interface to code in fts5_buffer.c. |
| 167910 | **************************************************************************/ |
| 167911 | |
| 167912 | /************************************************************************** |
| @@ -168024,11 +168307,10 @@ | |
| 168024 | static int sqlite3Fts5IndexSetAverages(Fts5Index *p, const u8*, int); |
| 168025 | |
| 168026 | /* |
| 168027 | ** Functions called by the storage module as part of integrity-check. |
| 168028 | */ |
| 168029 | static u64 sqlite3Fts5IndexCksum(Fts5Config*,i64,int,int,const char*,int); |
| 168030 | static int sqlite3Fts5IndexIntegrityCheck(Fts5Index*, u64 cksum); |
| 168031 | |
| 168032 | /* |
| 168033 | ** Called during virtual module initialization to register UDF |
| 168034 | ** fts5_decode() with SQLite |
| @@ -168046,10 +168328,12 @@ | |
| 168046 | static int sqlite3Fts5IndexReinit(Fts5Index *p); |
| 168047 | static int sqlite3Fts5IndexOptimize(Fts5Index *p); |
| 168048 | static int sqlite3Fts5IndexMerge(Fts5Index *p, int nMerge); |
| 168049 | |
| 168050 | static int sqlite3Fts5IndexLoadConfig(Fts5Index *p); |
| 168051 | |
| 168052 | /* |
| 168053 | ** End of interface to code in fts5_index.c. |
| 168054 | **************************************************************************/ |
| 168055 | |
| @@ -168103,11 +168387,11 @@ | |
| 168103 | typedef struct Fts5Hash Fts5Hash; |
| 168104 | |
| 168105 | /* |
| 168106 | ** Create a hash table, free a hash table. |
| 168107 | */ |
| 168108 | static int sqlite3Fts5HashNew(Fts5Hash**, int *pnSize); |
| 168109 | static void sqlite3Fts5HashFree(Fts5Hash*); |
| 168110 | |
| 168111 | static int sqlite3Fts5HashWrite( |
| 168112 | Fts5Hash*, |
| 168113 | i64 iRowid, /* Rowid for this entry */ |
| @@ -168239,12 +168523,22 @@ | |
| 168239 | static int sqlite3Fts5ExprInit(Fts5Global*, sqlite3*); |
| 168240 | |
| 168241 | static int sqlite3Fts5ExprPhraseCount(Fts5Expr*); |
| 168242 | static int sqlite3Fts5ExprPhraseSize(Fts5Expr*, int iPhrase); |
| 168243 | static int sqlite3Fts5ExprPoslist(Fts5Expr*, int, const u8 **); |
| 168244 | |
| 168245 | static int sqlite3Fts5ExprClonePhrase(Fts5Config*, Fts5Expr*, int, Fts5Expr**); |
| 168246 | |
| 168247 | /******************************************* |
| 168248 | ** The fts5_expr.c API above this point is used by the other hand-written |
| 168249 | ** C code in this module. The interfaces below this point are called by |
| 168250 | ** the parser code in fts5parse.y. */ |
| @@ -170397,10 +170691,93 @@ | |
| 170397 | |
| 170398 | return (t & 0x80) || aBareword[(int)t]; |
| 170399 | } |
| 170400 | |
| 170401 | |
| 170402 | |
| 170403 | /* |
| 170404 | ** 2014 Jun 09 |
| 170405 | ** |
| 170406 | ** The author disclaims copyright to this source code. In place of |
| @@ -170412,11 +170789,10 @@ | |
| 170412 | ** |
| 170413 | ****************************************************************************** |
| 170414 | ** |
| 170415 | ** This is an SQLite module implementing full-text search. |
| 170416 | */ |
| 170417 | |
| 170418 | |
| 170419 | |
| 170420 | /* #include "fts5Int.h" */ |
| 170421 | |
| 170422 | #define FTS5_DEFAULT_PAGE_SIZE 4050 |
| @@ -170595,10 +170971,37 @@ | |
| 170595 | if( quote=='[' || quote=='\'' || quote=='"' || quote=='`' ){ |
| 170596 | fts5Dequote(z); |
| 170597 | } |
| 170598 | } |
| 170599 | |
| 170600 | /* |
| 170601 | ** Parse a "special" CREATE VIRTUAL TABLE directive and update |
| 170602 | ** configuration object pConfig as appropriate. |
| 170603 | ** |
| 170604 | ** If successful, object pConfig is updated and SQLITE_OK returned. If |
| @@ -170744,10 +171147,24 @@ | |
| 170744 | }else{ |
| 170745 | pConfig->bColumnsize = (zArg[0]=='1'); |
| 170746 | } |
| 170747 | return rc; |
| 170748 | } |
| 170749 | |
| 170750 | *pzErr = sqlite3_mprintf("unrecognized option: \"%.*s\"", nCmd, zCmd); |
| 170751 | return SQLITE_ERROR; |
| 170752 | } |
| 170753 | |
| @@ -170900,10 +171317,11 @@ | |
| 170900 | pRet->azCol = (char**)sqlite3Fts5MallocZero(&rc, nByte); |
| 170901 | pRet->abUnindexed = (u8*)&pRet->azCol[nArg]; |
| 170902 | pRet->zDb = sqlite3Fts5Strndup(&rc, azArg[1], -1); |
| 170903 | pRet->zName = sqlite3Fts5Strndup(&rc, azArg[2], -1); |
| 170904 | pRet->bColumnsize = 1; |
| 170905 | #ifdef SQLITE_DEBUG |
| 170906 | pRet->bPrefixIndex = 1; |
| 170907 | #endif |
| 170908 | if( rc==SQLITE_OK && sqlite3_stricmp(pRet->zName, FTS5_RANK_NAME)==0 ){ |
| 170909 | *pzErr = sqlite3_mprintf("reserved fts5 table name: %s", pRet->zName); |
| @@ -171346,10 +171764,11 @@ | |
| 171346 | #endif |
| 171347 | |
| 171348 | |
| 171349 | struct Fts5Expr { |
| 171350 | Fts5Index *pIndex; |
| 171351 | Fts5ExprNode *pRoot; |
| 171352 | int bDesc; /* Iterate in descending rowid order */ |
| 171353 | int nPhrase; /* Number of phrases in expression */ |
| 171354 | Fts5ExprPhrase **apExprPhrase; /* Pointers to phrase objects */ |
| 171355 | }; |
| @@ -171541,10 +171960,11 @@ | |
| 171541 | sParse.rc = SQLITE_NOMEM; |
| 171542 | sqlite3Fts5ParseNodeFree(sParse.pExpr); |
| 171543 | }else{ |
| 171544 | pNew->pRoot = sParse.pExpr; |
| 171545 | pNew->pIndex = 0; |
| 171546 | pNew->apExprPhrase = sParse.apPhrase; |
| 171547 | pNew->nPhrase = sParse.nPhrase; |
| 171548 | sParse.apPhrase = 0; |
| 171549 | } |
| 171550 | } |
| @@ -171605,12 +172025,13 @@ | |
| 171605 | } |
| 171606 | |
| 171607 | /* |
| 171608 | ** Argument pTerm must be a synonym iterator. |
| 171609 | */ |
| 171610 | static int fts5ExprSynonymPoslist( |
| 171611 | Fts5ExprTerm *pTerm, |
| 171612 | Fts5Colset *pColset, |
| 171613 | i64 iRowid, |
| 171614 | int *pbDel, /* OUT: Caller should sqlite3_free(*pa) */ |
| 171615 | u8 **pa, int *pn |
| 171616 | ){ |
| @@ -171625,13 +172046,20 @@ | |
| 171625 | for(p=pTerm; p; p=p->pSynonym){ |
| 171626 | Fts5IndexIter *pIter = p->pIter; |
| 171627 | if( sqlite3Fts5IterEof(pIter)==0 && sqlite3Fts5IterRowid(pIter)==iRowid ){ |
| 171628 | const u8 *a; |
| 171629 | int n; |
| 171630 | i64 dummy; |
| 171631 | rc = sqlite3Fts5IterPoslist(pIter, pColset, &a, &n, &dummy); |
| 171632 | if( rc!=SQLITE_OK ) goto synonym_poslist_out; |
| 171633 | if( nIter==nAlloc ){ |
| 171634 | int nByte = sizeof(Fts5PoslistReader) * nAlloc * 2; |
| 171635 | Fts5PoslistReader *aNew = (Fts5PoslistReader*)sqlite3_malloc(nByte); |
| 171636 | if( aNew==0 ){ |
| 171637 | rc = SQLITE_NOMEM; |
| @@ -171728,12 +172156,12 @@ | |
| 171728 | i64 dummy; |
| 171729 | int n = 0; |
| 171730 | int bFlag = 0; |
| 171731 | const u8 *a = 0; |
| 171732 | if( pTerm->pSynonym ){ |
| 171733 | rc = fts5ExprSynonymPoslist( |
| 171734 | pTerm, pColset, pNode->iRowid, &bFlag, (u8**)&a, &n |
| 171735 | ); |
| 171736 | }else{ |
| 171737 | rc = sqlite3Fts5IterPoslist(pTerm->pIter, pColset, &a, &n, &dummy); |
| 171738 | } |
| 171739 | if( rc!=SQLITE_OK ) goto ismatch_out; |
| @@ -172063,34 +172491,55 @@ | |
| 172063 | Fts5Expr *pExpr, /* Expression that pNear is a part of */ |
| 172064 | Fts5ExprNode *pNode /* The "NEAR" node (FTS5_STRING) */ |
| 172065 | ){ |
| 172066 | Fts5ExprNearset *pNear = pNode->pNear; |
| 172067 | int rc = *pRc; |
| 172068 | int i; |
| 172069 | |
| 172070 | /* Check that each phrase in the nearset matches the current row. |
| 172071 | ** Populate the pPhrase->poslist buffers at the same time. If any |
| 172072 | ** phrase is not a match, break out of the loop early. */ |
| 172073 | for(i=0; rc==SQLITE_OK && i<pNear->nPhrase; i++){ |
| 172074 | Fts5ExprPhrase *pPhrase = pNear->apPhrase[i]; |
| 172075 | if( pPhrase->nTerm>1 || pPhrase->aTerm[0].pSynonym || pNear->pColset ){ |
| 172076 | int bMatch = 0; |
| 172077 | rc = fts5ExprPhraseIsMatch(pNode, pNear->pColset, pPhrase, &bMatch); |
| 172078 | if( bMatch==0 ) break; |
| 172079 | }else{ |
| 172080 | rc = sqlite3Fts5IterPoslistBuffer( |
| 172081 | pPhrase->aTerm[0].pIter, &pPhrase->poslist |
| 172082 | ); |
| 172083 | } |
| 172084 | } |
| 172085 | |
| 172086 | *pRc = rc; |
| 172087 | if( i==pNear->nPhrase && (i==1 || fts5ExprNearIsMatch(pRc, pNear)) ){ |
| 172088 | return 1; |
| 172089 | } |
| 172090 | |
| 172091 | return 0; |
| 172092 | } |
| 172093 | |
| 172094 | static int fts5ExprTokenTest( |
| 172095 | Fts5Expr *pExpr, /* Expression that pNear is a part of */ |
| 172096 | Fts5ExprNode *pNode /* The "NEAR" node (FTS5_TERM) */ |
| @@ -172524,10 +172973,13 @@ | |
| 172524 | if( cmp || p2->bNomatch ) break; |
| 172525 | rc = fts5ExprNodeNext(pExpr, p1, 0, 0); |
| 172526 | } |
| 172527 | pNode->bEof = p1->bEof; |
| 172528 | pNode->iRowid = p1->iRowid; |
| 172529 | break; |
| 172530 | } |
| 172531 | } |
| 172532 | } |
| 172533 | return rc; |
| @@ -172909,10 +173361,11 @@ | |
| 172909 | } |
| 172910 | |
| 172911 | if( rc==SQLITE_OK ){ |
| 172912 | /* All the allocations succeeded. Put the expression object together. */ |
| 172913 | pNew->pIndex = pExpr->pIndex; |
| 172914 | pNew->nPhrase = 1; |
| 172915 | pNew->apExprPhrase[0] = sCtx.pPhrase; |
| 172916 | pNew->pRoot->pNear->apPhrase[0] = sCtx.pPhrase; |
| 172917 | pNew->pRoot->pNear->nPhrase = 1; |
| 172918 | sCtx.pPhrase->pNode = pNew->pRoot; |
| @@ -173050,10 +173503,19 @@ | |
| 173050 | static void sqlite3Fts5ParseSetColset( |
| 173051 | Fts5Parse *pParse, |
| 173052 | Fts5ExprNearset *pNear, |
| 173053 | Fts5Colset *pColset |
| 173054 | ){ |
| 173055 | if( pNear ){ |
| 173056 | pNear->pColset = pColset; |
| 173057 | }else{ |
| 173058 | sqlite3_free(pColset); |
| 173059 | } |
| @@ -173111,15 +173573,24 @@ | |
| 173111 | if( eType==FTS5_STRING ){ |
| 173112 | int iPhrase; |
| 173113 | for(iPhrase=0; iPhrase<pNear->nPhrase; iPhrase++){ |
| 173114 | pNear->apPhrase[iPhrase]->pNode = pRet; |
| 173115 | } |
| 173116 | if( pNear->nPhrase==1 |
| 173117 | && pNear->apPhrase[0]->nTerm==1 |
| 173118 | && pNear->apPhrase[0]->aTerm[0].pSynonym==0 |
| 173119 | ){ |
| 173120 | pRet->eType = FTS5_TERM; |
| 173121 | } |
| 173122 | }else{ |
| 173123 | fts5ExprAddChildren(pRet, pLeft); |
| 173124 | fts5ExprAddChildren(pRet, pRight); |
| 173125 | } |
| @@ -173229,10 +173700,13 @@ | |
| 173229 | |
| 173230 | zRet = fts5PrintfAppend(zRet, " {"); |
| 173231 | for(iTerm=0; zRet && iTerm<pPhrase->nTerm; iTerm++){ |
| 173232 | char *zTerm = pPhrase->aTerm[iTerm].zTerm; |
| 173233 | zRet = fts5PrintfAppend(zRet, "%s%s", iTerm==0?"":" ", zTerm); |
| 173234 | } |
| 173235 | |
| 173236 | if( zRet ) zRet = fts5PrintfAppend(zRet, "}"); |
| 173237 | if( zRet==0 ) return 0; |
| 173238 | } |
| @@ -173541,10 +174015,237 @@ | |
| 173541 | *pa = 0; |
| 173542 | nRet = 0; |
| 173543 | } |
| 173544 | return nRet; |
| 173545 | } |
| 173546 | |
| 173547 | /* |
| 173548 | ** 2014 August 11 |
| 173549 | ** |
| 173550 | ** The author disclaims copyright to this source code. In place of |
| @@ -173570,10 +174271,11 @@ | |
| 173570 | ** segment. |
| 173571 | */ |
| 173572 | |
| 173573 | |
| 173574 | struct Fts5Hash { |
| 173575 | int *pnByte; /* Pointer to bytes counter */ |
| 173576 | int nEntry; /* Number of entries currently in hash */ |
| 173577 | int nSlot; /* Size of aSlot[] array */ |
| 173578 | Fts5HashEntry *pScan; /* Current ordered scan item */ |
| 173579 | Fts5HashEntry **aSlot; /* Array of hash slots */ |
| @@ -173606,10 +174308,11 @@ | |
| 173606 | |
| 173607 | int nAlloc; /* Total size of allocation */ |
| 173608 | int iSzPoslist; /* Offset of space for 4-byte poslist size */ |
| 173609 | int nData; /* Total bytes of data (incl. structure) */ |
| 173610 | u8 bDel; /* Set delete-flag @ iSzPoslist */ |
| 173611 | |
| 173612 | int iCol; /* Column of last value written */ |
| 173613 | int iPos; /* Position of last value written */ |
| 173614 | i64 iRowid; /* Rowid of last value written */ |
| 173615 | char zKey[8]; /* Nul-terminated entry key */ |
| @@ -173623,11 +174326,11 @@ | |
| 173623 | |
| 173624 | |
| 173625 | /* |
| 173626 | ** Allocate a new hash table. |
| 173627 | */ |
| 173628 | static int sqlite3Fts5HashNew(Fts5Hash **ppNew, int *pnByte){ |
| 173629 | int rc = SQLITE_OK; |
| 173630 | Fts5Hash *pNew; |
| 173631 | |
| 173632 | *ppNew = pNew = (Fts5Hash*)sqlite3_malloc(sizeof(Fts5Hash)); |
| 173633 | if( pNew==0 ){ |
| @@ -173634,10 +174337,11 @@ | |
| 173634 | rc = SQLITE_NOMEM; |
| 173635 | }else{ |
| 173636 | int nByte; |
| 173637 | memset(pNew, 0, sizeof(Fts5Hash)); |
| 173638 | pNew->pnByte = pnByte; |
| 173639 | |
| 173640 | pNew->nSlot = 1024; |
| 173641 | nByte = sizeof(Fts5HashEntry*) * pNew->nSlot; |
| 173642 | pNew->aSlot = (Fts5HashEntry**)sqlite3_malloc(nByte); |
| 173643 | if( pNew->aSlot==0 ){ |
| @@ -173726,30 +174430,50 @@ | |
| 173726 | pHash->nSlot = nNew; |
| 173727 | pHash->aSlot = apNew; |
| 173728 | return SQLITE_OK; |
| 173729 | } |
| 173730 | |
| 173731 | static void fts5HashAddPoslistSize(Fts5HashEntry *p){ |
| 173732 | if( p->iSzPoslist ){ |
| 173733 | u8 *pPtr = (u8*)p; |
| 173734 | int nSz = (p->nData - p->iSzPoslist - 1); /* Size in bytes */ |
| 173735 | int nPos = nSz*2 + p->bDel; /* Value of nPos field */ |
| 173736 | |
| 173737 | assert( p->bDel==0 || p->bDel==1 ); |
| 173738 | if( nPos<=127 ){ |
| 173739 | pPtr[p->iSzPoslist] = (u8)nPos; |
| 173740 | }else{ |
| 173741 | int nByte = sqlite3Fts5GetVarintLen((u32)nPos); |
| 173742 | memmove(&pPtr[p->iSzPoslist + nByte], &pPtr[p->iSzPoslist + 1], nSz); |
| 173743 | sqlite3Fts5PutVarint(&pPtr[p->iSzPoslist], nPos); |
| 173744 | p->nData += (nByte-1); |
| 173745 | } |
| 173746 | p->bDel = 0; |
| 173747 | p->iSzPoslist = 0; |
| 173748 | } |
| 173749 | } |
| 173750 | |
| 173751 | static int sqlite3Fts5HashWrite( |
| 173752 | Fts5Hash *pHash, |
| 173753 | i64 iRowid, /* Rowid for this entry */ |
| 173754 | int iCol, /* Column token appears in (-ve -> delete) */ |
| 173755 | int iPos, /* Position of token within column */ |
| @@ -173758,10 +174482,13 @@ | |
| 173758 | ){ |
| 173759 | unsigned int iHash; |
| 173760 | Fts5HashEntry *p; |
| 173761 | u8 *pPtr; |
| 173762 | int nIncr = 0; /* Amount to increment (*pHash->pnByte) by */ |
| 173763 | |
| 173764 | /* Attempt to locate an existing hash entry */ |
| 173765 | iHash = fts5HashKey2(pHash->nSlot, (u8)bByte, (const u8*)pToken, nToken); |
| 173766 | for(p=pHash->aSlot[iHash]; p; p=p->pHashNext){ |
| 173767 | if( p->zKey[0]==bByte |
| @@ -173772,92 +174499,120 @@ | |
| 173772 | } |
| 173773 | } |
| 173774 | |
| 173775 | /* If an existing hash entry cannot be found, create a new one. */ |
| 173776 | if( p==0 ){ |
| 173777 | int nByte = FTS5_HASHENTRYSIZE + (nToken+1) + 1 + 64; |
| 173778 | if( nByte<128 ) nByte = 128; |
| 173779 | |
| 173780 | if( (pHash->nEntry*2)>=pHash->nSlot ){ |
| 173781 | int rc = fts5HashResize(pHash); |
| 173782 | if( rc!=SQLITE_OK ) return rc; |
| 173783 | iHash = fts5HashKey2(pHash->nSlot, (u8)bByte, (const u8*)pToken, nToken); |
| 173784 | } |
| 173785 | |
| 173786 | p = (Fts5HashEntry*)sqlite3_malloc(nByte); |
| 173787 | if( !p ) return SQLITE_NOMEM; |
| 173788 | memset(p, 0, FTS5_HASHENTRYSIZE); |
| 173789 | p->nAlloc = nByte; |
| 173790 | p->zKey[0] = bByte; |
| 173791 | memcpy(&p->zKey[1], pToken, nToken); |
| 173792 | assert( iHash==fts5HashKey(pHash->nSlot, (u8*)p->zKey, nToken+1) ); |
| 173793 | p->zKey[nToken+1] = '\0'; |
| 173794 | p->nData = nToken+1 + 1 + FTS5_HASHENTRYSIZE; |
| 173795 | p->nData += sqlite3Fts5PutVarint(&((u8*)p)[p->nData], iRowid); |
| 173796 | p->iSzPoslist = p->nData; |
| 173797 | p->nData += 1; |
| 173798 | p->iRowid = iRowid; |
| 173799 | p->pHashNext = pHash->aSlot[iHash]; |
| 173800 | pHash->aSlot[iHash] = p; |
| 173801 | pHash->nEntry++; |
| 173802 | nIncr += p->nData; |
| 173803 | } |
| 173804 | |
| 173805 | /* Check there is enough space to append a new entry. Worst case scenario |
| 173806 | ** is: |
| 173807 | ** |
| 173808 | ** + 9 bytes for a new rowid, |
| 173809 | ** + 4 byte reserved for the "poslist size" varint. |
| 173810 | ** + 1 byte for a "new column" byte, |
| 173811 | ** + 3 bytes for a new column number (16-bit max) as a varint, |
| 173812 | ** + 5 bytes for the new position offset (32-bit max). |
| 173813 | */ |
| 173814 | if( (p->nAlloc - p->nData) < (9 + 4 + 1 + 3 + 5) ){ |
| 173815 | int nNew = p->nAlloc * 2; |
| 173816 | Fts5HashEntry *pNew; |
| 173817 | Fts5HashEntry **pp; |
| 173818 | pNew = (Fts5HashEntry*)sqlite3_realloc(p, nNew); |
| 173819 | if( pNew==0 ) return SQLITE_NOMEM; |
| 173820 | pNew->nAlloc = nNew; |
| 173821 | for(pp=&pHash->aSlot[iHash]; *pp!=p; pp=&(*pp)->pHashNext); |
| 173822 | *pp = pNew; |
| 173823 | p = pNew; |
| 173824 | } |
| 173825 | pPtr = (u8*)p; |
| 173826 | nIncr -= p->nData; |
| 173827 | |
| 173828 | /* If this is a new rowid, append the 4-byte size field for the previous |
| 173829 | ** entry, and the new rowid for this entry. */ |
| 173830 | if( iRowid!=p->iRowid ){ |
| 173831 | fts5HashAddPoslistSize(p); |
| 173832 | p->nData += sqlite3Fts5PutVarint(&pPtr[p->nData], iRowid - p->iRowid); |
| 173833 | p->iSzPoslist = p->nData; |
| 173834 | p->nData += 1; |
| 173835 | p->iCol = 0; |
| 173836 | p->iPos = 0; |
| 173837 | p->iRowid = iRowid; |
| 173838 | } |
| 173839 | |
| 173840 | if( iCol>=0 ){ |
| 173841 | /* Append a new column value, if necessary */ |
| 173842 | assert( iCol>=p->iCol ); |
| 173843 | if( iCol!=p->iCol ){ |
| 173844 | pPtr[p->nData++] = 0x01; |
| 173845 | p->nData += sqlite3Fts5PutVarint(&pPtr[p->nData], iCol); |
| 173846 | p->iCol = iCol; |
| 173847 | p->iPos = 0; |
| 173848 | } |
| 173849 | |
| 173850 | /* Append the new position offset */ |
| 173851 | p->nData += sqlite3Fts5PutVarint(&pPtr[p->nData], iPos - p->iPos + 2); |
| 173852 | p->iPos = iPos; |
| 173853 | }else{ |
| 173854 | /* This is a delete. Set the delete flag. */ |
| 173855 | p->bDel = 1; |
| 173856 | } |
| 173857 | nIncr += p->nData; |
| 173858 | |
| 173859 | *pHash->pnByte += nIncr; |
| 173860 | return SQLITE_OK; |
| 173861 | } |
| 173862 | |
| 173863 | |
| @@ -173967,11 +174722,11 @@ | |
| 173967 | for(p=pHash->aSlot[iHash]; p; p=p->pHashNext){ |
| 173968 | if( memcmp(p->zKey, pTerm, nTerm)==0 && p->zKey[nTerm]==0 ) break; |
| 173969 | } |
| 173970 | |
| 173971 | if( p ){ |
| 173972 | fts5HashAddPoslistSize(p); |
| 173973 | *ppDoclist = (const u8*)&p->zKey[nTerm+1]; |
| 173974 | *pnDoclist = p->nData - (FTS5_HASHENTRYSIZE + nTerm + 1); |
| 173975 | }else{ |
| 173976 | *ppDoclist = 0; |
| 173977 | *pnDoclist = 0; |
| @@ -174003,11 +174758,11 @@ | |
| 174003 | int *pnDoclist /* OUT: size of doclist in bytes */ |
| 174004 | ){ |
| 174005 | Fts5HashEntry *p; |
| 174006 | if( (p = pHash->pScan) ){ |
| 174007 | int nTerm = (int)strlen(p->zKey); |
| 174008 | fts5HashAddPoslistSize(p); |
| 174009 | *pzTerm = p->zKey; |
| 174010 | *ppDoclist = (const u8*)&p->zKey[nTerm+1]; |
| 174011 | *pnDoclist = p->nData - (FTS5_HASHENTRYSIZE + nTerm + 1); |
| 174012 | }else{ |
| 174013 | *pzTerm = 0; |
| @@ -174450,10 +175205,13 @@ | |
| 174450 | int iLeafPgno; /* Current leaf page number */ |
| 174451 | Fts5Data *pLeaf; /* Current leaf data */ |
| 174452 | Fts5Data *pNextLeaf; /* Leaf page (iLeafPgno+1) */ |
| 174453 | int iLeafOffset; /* Byte offset within current leaf */ |
| 174454 | |
| 174455 | /* The page and offset from which the current term was read. The offset |
| 174456 | ** is the offset of the first rowid in the current doclist. */ |
| 174457 | int iTermLeafPgno; |
| 174458 | int iTermLeafOffset; |
| 174459 | |
| @@ -174469,11 +175227,11 @@ | |
| 174469 | |
| 174470 | /* Variables populated based on current entry. */ |
| 174471 | Fts5Buffer term; /* Current term */ |
| 174472 | i64 iRowid; /* Current rowid */ |
| 174473 | int nPos; /* Number of bytes in current position list */ |
| 174474 | int bDel; /* True if the delete flag is set */ |
| 174475 | }; |
| 174476 | |
| 174477 | /* |
| 174478 | ** Argument is a pointer to an Fts5Data structure that contains a |
| 174479 | ** leaf page. |
| @@ -174482,11 +175240,10 @@ | |
| 174482 | (x)->szLeaf==(x)->nn || (x)->szLeaf==fts5GetU16(&(x)->p[2]) \ |
| 174483 | ) |
| 174484 | |
| 174485 | #define FTS5_SEGITER_ONETERM 0x01 |
| 174486 | #define FTS5_SEGITER_REVERSE 0x02 |
| 174487 | |
| 174488 | |
| 174489 | /* |
| 174490 | ** Argument is a pointer to an Fts5Data structure that contains a leaf |
| 174491 | ** page. This macro evaluates to true if the leaf contains no terms, or |
| 174492 | ** false if it contains at least one term. |
| @@ -175509,17 +176266,33 @@ | |
| 175509 | ** position list content (if any). |
| 175510 | */ |
| 175511 | static void fts5SegIterLoadNPos(Fts5Index *p, Fts5SegIter *pIter){ |
| 175512 | if( p->rc==SQLITE_OK ){ |
| 175513 | int iOff = pIter->iLeafOffset; /* Offset to read at */ |
| 175514 | int nSz; |
| 175515 | ASSERT_SZLEAF_OK(pIter->pLeaf); |
| 175516 | fts5FastGetVarint32(pIter->pLeaf->p, iOff, nSz); |
| 175517 | pIter->bDel = (nSz & 0x0001); |
| 175518 | pIter->nPos = nSz>>1; |
| 175519 | pIter->iLeafOffset = iOff; |
| 175520 | assert_nc( pIter->nPos>=0 ); |
| 175521 | } |
| 175522 | } |
| 175523 | |
| 175524 | static void fts5SegIterLoadRowid(Fts5Index *p, Fts5SegIter *pIter){ |
| 175525 | u8 *a = pIter->pLeaf->p; /* Buffer to read data from */ |
| @@ -175575,10 +176348,24 @@ | |
| 175575 | pIter->iEndofDoclist += nExtra; |
| 175576 | } |
| 175577 | |
| 175578 | fts5SegIterLoadRowid(p, pIter); |
| 175579 | } |
| 175580 | |
| 175581 | /* |
| 175582 | ** Initialize the iterator object pIter to iterate through the entries in |
| 175583 | ** segment pSeg. The iterator is left pointing to the first entry when |
| 175584 | ** this function returns. |
| @@ -175601,10 +176388,11 @@ | |
| 175601 | return; |
| 175602 | } |
| 175603 | |
| 175604 | if( p->rc==SQLITE_OK ){ |
| 175605 | memset(pIter, 0, sizeof(*pIter)); |
| 175606 | pIter->pSeg = pSeg; |
| 175607 | pIter->iLeafPgno = pSeg->pgnoFirst-1; |
| 175608 | fts5SegIterNextPage(p, pIter); |
| 175609 | } |
| 175610 | |
| @@ -175632,10 +176420,11 @@ | |
| 175632 | ** aRowidOffset[] and iRowidOffset variables. At this point the iterator |
| 175633 | ** is in its regular state - Fts5SegIter.iLeafOffset points to the first |
| 175634 | ** byte of the position list content associated with said rowid. |
| 175635 | */ |
| 175636 | static void fts5SegIterReverseInitPage(Fts5Index *p, Fts5SegIter *pIter){ |
| 175637 | int n = pIter->pLeaf->szLeaf; |
| 175638 | int i = pIter->iLeafOffset; |
| 175639 | u8 *a = pIter->pLeaf->p; |
| 175640 | int iRowidOffset = 0; |
| 175641 | |
| @@ -175644,19 +176433,28 @@ | |
| 175644 | } |
| 175645 | |
| 175646 | ASSERT_SZLEAF_OK(pIter->pLeaf); |
| 175647 | while( 1 ){ |
| 175648 | i64 iDelta = 0; |
| 175649 | int nPos; |
| 175650 | int bDummy; |
| 175651 | |
| 175652 | i += fts5GetPoslistSize(&a[i], &nPos, &bDummy); |
| 175653 | i += nPos; |
| 175654 | if( i>=n ) break; |
| 175655 | i += fts5GetVarint(&a[i], (u64*)&iDelta); |
| 175656 | pIter->iRowid += iDelta; |
| 175657 | |
| 175658 | if( iRowidOffset>=pIter->nRowidOffset ){ |
| 175659 | int nNew = pIter->nRowidOffset + 8; |
| 175660 | int *aNew = (int*)sqlite3_realloc(pIter->aRowidOffset, nNew*sizeof(int)); |
| 175661 | if( aNew==0 ){ |
| 175662 | p->rc = SQLITE_NOMEM; |
| @@ -175730,10 +176528,112 @@ | |
| 175730 | */ |
| 175731 | static int fts5MultiIterIsEmpty(Fts5Index *p, Fts5IndexIter *pIter){ |
| 175732 | Fts5SegIter *pSeg = &pIter->aSeg[pIter->aFirst[1].iFirst]; |
| 175733 | return (p->rc==SQLITE_OK && pSeg->pLeaf && pSeg->nPos==0); |
| 175734 | } |
| 175735 | |
| 175736 | /* |
| 175737 | ** Advance iterator pIter to the next entry. |
| 175738 | ** |
| 175739 | ** If an error occurs, Fts5Index.rc is set to an appropriate error code. It |
| @@ -175743,144 +176643,133 @@ | |
| 175743 | static void fts5SegIterNext( |
| 175744 | Fts5Index *p, /* FTS5 backend object */ |
| 175745 | Fts5SegIter *pIter, /* Iterator to advance */ |
| 175746 | int *pbNewTerm /* OUT: Set for new term */ |
| 175747 | ){ |
| 175748 | assert( pbNewTerm==0 || *pbNewTerm==0 ); |
| 175749 | if( p->rc==SQLITE_OK ){ |
| 175750 | if( pIter->flags & FTS5_SEGITER_REVERSE ){ |
| 175751 | assert( pIter->pNextLeaf==0 ); |
| 175752 | if( pIter->iRowidOffset>0 ){ |
| 175753 | u8 *a = pIter->pLeaf->p; |
| 175754 | int iOff; |
| 175755 | int nPos; |
| 175756 | int bDummy; |
| 175757 | i64 iDelta; |
| 175758 | |
| 175759 | pIter->iRowidOffset--; |
| 175760 | pIter->iLeafOffset = iOff = pIter->aRowidOffset[pIter->iRowidOffset]; |
| 175761 | iOff += fts5GetPoslistSize(&a[iOff], &nPos, &bDummy); |
| 175762 | iOff += nPos; |
| 175763 | fts5GetVarint(&a[iOff], (u64*)&iDelta); |
| 175764 | pIter->iRowid -= iDelta; |
| 175765 | fts5SegIterLoadNPos(p, pIter); |
| 175766 | }else{ |
| 175767 | fts5SegIterReverseNewPage(p, pIter); |
| 175768 | } |
| 175769 | }else{ |
| 175770 | Fts5Data *pLeaf = pIter->pLeaf; |
| 175771 | int iOff; |
| 175772 | int bNewTerm = 0; |
| 175773 | int nKeep = 0; |
| 175774 | |
| 175775 | /* Search for the end of the position list within the current page. */ |
| 175776 | u8 *a = pLeaf->p; |
| 175777 | int n = pLeaf->szLeaf; |
| 175778 | |
| 175779 | ASSERT_SZLEAF_OK(pLeaf); |
| 175780 | iOff = pIter->iLeafOffset + pIter->nPos; |
| 175781 | |
| 175782 | if( iOff<n ){ |
| 175783 | /* The next entry is on the current page. */ |
| 175784 | assert_nc( iOff<=pIter->iEndofDoclist ); |
| 175785 | if( iOff>=pIter->iEndofDoclist ){ |
| 175786 | bNewTerm = 1; |
| 175787 | if( iOff!=fts5LeafFirstTermOff(pLeaf) ){ |
| 175788 | iOff += fts5GetVarint32(&a[iOff], nKeep); |
| 175789 | } |
| 175790 | }else{ |
| 175791 | u64 iDelta; |
| 175792 | iOff += sqlite3Fts5GetVarint(&a[iOff], &iDelta); |
| 175793 | pIter->iRowid += iDelta; |
| 175794 | assert_nc( iDelta>0 ); |
| 175795 | } |
| 175796 | pIter->iLeafOffset = iOff; |
| 175797 | |
| 175798 | }else if( pIter->pSeg==0 ){ |
| 175799 | const u8 *pList = 0; |
| 175800 | const char *zTerm = 0; |
| 175801 | int nList = 0; |
| 175802 | assert( (pIter->flags & FTS5_SEGITER_ONETERM) || pbNewTerm ); |
| 175803 | if( 0==(pIter->flags & FTS5_SEGITER_ONETERM) ){ |
| 175804 | sqlite3Fts5HashScanNext(p->pHash); |
| 175805 | sqlite3Fts5HashScanEntry(p->pHash, &zTerm, &pList, &nList); |
| 175806 | } |
| 175807 | if( pList==0 ){ |
| 175808 | fts5DataRelease(pIter->pLeaf); |
| 175809 | pIter->pLeaf = 0; |
| 175810 | }else{ |
| 175811 | pIter->pLeaf->p = (u8*)pList; |
| 175812 | pIter->pLeaf->nn = nList; |
| 175813 | pIter->pLeaf->szLeaf = nList; |
| 175814 | pIter->iEndofDoclist = nList+1; |
| 175815 | sqlite3Fts5BufferSet(&p->rc, &pIter->term, (int)strlen(zTerm), |
| 175816 | (u8*)zTerm); |
| 175817 | pIter->iLeafOffset = fts5GetVarint(pList, (u64*)&pIter->iRowid); |
| 175818 | *pbNewTerm = 1; |
| 175819 | } |
| 175820 | }else{ |
| 175821 | iOff = 0; |
| 175822 | /* Next entry is not on the current page */ |
| 175823 | while( iOff==0 ){ |
| 175824 | fts5SegIterNextPage(p, pIter); |
| 175825 | pLeaf = pIter->pLeaf; |
| 175826 | if( pLeaf==0 ) break; |
| 175827 | ASSERT_SZLEAF_OK(pLeaf); |
| 175828 | if( (iOff = fts5LeafFirstRowidOff(pLeaf)) && iOff<pLeaf->szLeaf ){ |
| 175829 | iOff += sqlite3Fts5GetVarint(&pLeaf->p[iOff], (u64*)&pIter->iRowid); |
| 175830 | pIter->iLeafOffset = iOff; |
| 175831 | |
| 175832 | if( pLeaf->nn>pLeaf->szLeaf ){ |
| 175833 | pIter->iPgidxOff = pLeaf->szLeaf + fts5GetVarint32( |
| 175834 | &pLeaf->p[pLeaf->szLeaf], pIter->iEndofDoclist |
| 175835 | ); |
| 175836 | } |
| 175837 | |
| 175838 | } |
| 175839 | else if( pLeaf->nn>pLeaf->szLeaf ){ |
| 175840 | pIter->iPgidxOff = pLeaf->szLeaf + fts5GetVarint32( |
| 175841 | &pLeaf->p[pLeaf->szLeaf], iOff |
| 175842 | ); |
| 175843 | pIter->iLeafOffset = iOff; |
| 175844 | pIter->iEndofDoclist = iOff; |
| 175845 | bNewTerm = 1; |
| 175846 | } |
| 175847 | if( iOff>=pLeaf->szLeaf ){ |
| 175848 | p->rc = FTS5_CORRUPT; |
| 175849 | return; |
| 175850 | } |
| 175851 | } |
| 175852 | } |
| 175853 | |
| 175854 | /* Check if the iterator is now at EOF. If so, return early. */ |
| 175855 | if( pIter->pLeaf ){ |
| 175856 | if( bNewTerm ){ |
| 175857 | if( pIter->flags & FTS5_SEGITER_ONETERM ){ |
| 175858 | fts5DataRelease(pIter->pLeaf); |
| 175859 | pIter->pLeaf = 0; |
| 175860 | }else{ |
| 175861 | fts5SegIterLoadTerm(p, pIter, nKeep); |
| 175862 | fts5SegIterLoadNPos(p, pIter); |
| 175863 | if( pbNewTerm ) *pbNewTerm = 1; |
| 175864 | } |
| 175865 | }else{ |
| 175866 | /* The following could be done by calling fts5SegIterLoadNPos(). But |
| 175867 | ** this block is particularly performance critical, so equivalent |
| 175868 | ** code is inlined. */ |
| 175869 | int nSz; |
| 175870 | assert( p->rc==SQLITE_OK ); |
| 175871 | fts5FastGetVarint32(pIter->pLeaf->p, pIter->iLeafOffset, nSz); |
| 175872 | pIter->bDel = (nSz & 0x0001); |
| 175873 | pIter->nPos = nSz>>1; |
| 175874 | assert_nc( pIter->nPos>=0 ); |
| 175875 | } |
| 175876 | } |
| 175877 | } |
| 175878 | } |
| 175879 | } |
| 175880 | |
| 175881 | #define SWAPVAL(T, a, b) { T tmp; tmp=a; a=b; b=tmp; } |
| 175882 | |
| 175883 | /* |
| 175884 | ** Iterator pIter currently points to the first rowid in a doclist. This |
| 175885 | ** function sets the iterator up so that iterates in reverse order through |
| 175886 | ** the doclist. |
| @@ -175898,11 +176787,21 @@ | |
| 175898 | Fts5Data *pLeaf = pIter->pLeaf; /* Current leaf data */ |
| 175899 | |
| 175900 | /* Currently, Fts5SegIter.iLeafOffset points to the first byte of |
| 175901 | ** position-list content for the current rowid. Back it up so that it |
| 175902 | ** points to the start of the position-list size field. */ |
| 175903 | pIter->iLeafOffset -= sqlite3Fts5GetVarintLen(pIter->nPos*2+pIter->bDel); |
| 175904 | |
| 175905 | /* If this condition is true then the largest rowid for the current |
| 175906 | ** term may not be stored on the current page. So search forward to |
| 175907 | ** see where said rowid really is. */ |
| 175908 | if( pIter->iEndofDoclist>=pLeaf->szLeaf ){ |
| @@ -175982,15 +176881,10 @@ | |
| 175982 | } |
| 175983 | |
| 175984 | pIter->pDlidx = fts5DlidxIterInit(p, bRev, iSeg, pIter->iTermLeafPgno); |
| 175985 | } |
| 175986 | |
| 175987 | #define fts5IndexSkipVarint(a, iOff) { \ |
| 175988 | int iEnd = iOff+9; \ |
| 175989 | while( (a[iOff++] & 0x80) && iOff<iEnd ); \ |
| 175990 | } |
| 175991 | |
| 175992 | /* |
| 175993 | ** The iterator object passed as the second argument currently contains |
| 175994 | ** no valid values except for the Fts5SegIter.pLeaf member variable. This |
| 175995 | ** function searches the leaf page for a term matching (pTerm/nTerm). |
| 175996 | ** |
| @@ -176189,10 +177083,12 @@ | |
| 176189 | fts5SegIterReverse(p, pIter); |
| 176190 | } |
| 176191 | } |
| 176192 | } |
| 176193 | |
| 176194 | /* Either: |
| 176195 | ** |
| 176196 | ** 1) an error has occurred, or |
| 176197 | ** 2) the iterator points to EOF, or |
| 176198 | ** 3) the iterator points to an entry with term (pTerm/nTerm), or |
| @@ -176246,19 +177142,21 @@ | |
| 176246 | if( pLeaf==0 ) return; |
| 176247 | pLeaf->p = (u8*)pList; |
| 176248 | pLeaf->nn = pLeaf->szLeaf = nList; |
| 176249 | pIter->pLeaf = pLeaf; |
| 176250 | pIter->iLeafOffset = fts5GetVarint(pLeaf->p, (u64*)&pIter->iRowid); |
| 176251 | pIter->iEndofDoclist = pLeaf->nn+1; |
| 176252 | |
| 176253 | if( flags & FTS5INDEX_QUERY_DESC ){ |
| 176254 | pIter->flags |= FTS5_SEGITER_REVERSE; |
| 176255 | fts5SegIterReverseInitPage(p, pIter); |
| 176256 | }else{ |
| 176257 | fts5SegIterLoadNPos(p, pIter); |
| 176258 | } |
| 176259 | } |
| 176260 | } |
| 176261 | |
| 176262 | /* |
| 176263 | ** Zero the iterator passed as the only argument. |
| 176264 | */ |
| @@ -176498,11 +177396,11 @@ | |
| 176498 | bMove = 0; |
| 176499 | } |
| 176500 | } |
| 176501 | |
| 176502 | do{ |
| 176503 | if( bMove ) fts5SegIterNext(p, pIter, 0); |
| 176504 | if( pIter->pLeaf==0 ) break; |
| 176505 | if( bRev==0 && pIter->iRowid>=iMatch ) break; |
| 176506 | if( bRev!=0 && pIter->iRowid<=iMatch ) break; |
| 176507 | bMove = 1; |
| 176508 | }while( p->rc==SQLITE_OK ); |
| @@ -176532,11 +177430,13 @@ | |
| 176532 | ){ |
| 176533 | int i; |
| 176534 | for(i=(pIter->nSeg+iChanged)/2; i>=iMinset && p->rc==SQLITE_OK; i=i/2){ |
| 176535 | int iEq; |
| 176536 | if( (iEq = fts5MultiIterDoCompare(pIter, i)) ){ |
| 176537 | fts5SegIterNext(p, &pIter->aSeg[iEq], 0); |
| 176538 | i = pIter->nSeg + iEq; |
| 176539 | } |
| 176540 | } |
| 176541 | } |
| 176542 | |
| @@ -176619,11 +177519,11 @@ | |
| 176619 | Fts5SegIter *pSeg = &pIter->aSeg[iFirst]; |
| 176620 | assert( p->rc==SQLITE_OK ); |
| 176621 | if( bUseFrom && pSeg->pDlidx ){ |
| 176622 | fts5SegIterNextFrom(p, pSeg, iFrom); |
| 176623 | }else{ |
| 176624 | fts5SegIterNext(p, pSeg, &bNewTerm); |
| 176625 | } |
| 176626 | |
| 176627 | if( pSeg->pLeaf==0 || bNewTerm |
| 176628 | || fts5MultiIterAdvanceRowid(p, pIter, iFirst) |
| 176629 | ){ |
| @@ -176647,11 +177547,12 @@ | |
| 176647 | do { |
| 176648 | int iFirst = pIter->aFirst[1].iFirst; |
| 176649 | Fts5SegIter *pSeg = &pIter->aSeg[iFirst]; |
| 176650 | int bNewTerm = 0; |
| 176651 | |
| 176652 | fts5SegIterNext(p, pSeg, &bNewTerm); |
| 176653 | if( pSeg->pLeaf==0 || bNewTerm |
| 176654 | || fts5MultiIterAdvanceRowid(p, pIter, iFirst) |
| 176655 | ){ |
| 176656 | fts5MultiIterAdvanced(p, pIter, iFirst, 1); |
| 176657 | fts5MultiIterSetEof(pIter); |
| @@ -176767,11 +177668,12 @@ | |
| 176767 | ** object and set the output variable to NULL. */ |
| 176768 | if( p->rc==SQLITE_OK ){ |
| 176769 | for(iIter=pNew->nSeg-1; iIter>0; iIter--){ |
| 176770 | int iEq; |
| 176771 | if( (iEq = fts5MultiIterDoCompare(pNew, iIter)) ){ |
| 176772 | fts5SegIterNext(p, &pNew->aSeg[iEq], 0); |
| 176773 | fts5MultiIterAdvanced(p, pNew, iEq, iIter); |
| 176774 | } |
| 176775 | } |
| 176776 | fts5MultiIterSetEof(pNew); |
| 176777 | fts5AssertMultiIterSetup(p, pNew); |
| @@ -176817,10 +177719,11 @@ | |
| 176817 | } |
| 176818 | pData = 0; |
| 176819 | }else{ |
| 176820 | pNew->bEof = 1; |
| 176821 | } |
| 176822 | |
| 176823 | *ppOut = pNew; |
| 176824 | } |
| 176825 | |
| 176826 | fts5DataRelease(pData); |
| @@ -176885,10 +177788,13 @@ | |
| 176885 | Fts5Data *pData = 0; |
| 176886 | u8 *pChunk = &pSeg->pLeaf->p[pSeg->iLeafOffset]; |
| 176887 | int nChunk = MIN(nRem, pSeg->pLeaf->szLeaf - pSeg->iLeafOffset); |
| 176888 | int pgno = pSeg->iLeafPgno; |
| 176889 | int pgnoSave = 0; |
| 176890 | |
| 176891 | if( (pSeg->flags & FTS5_SEGITER_REVERSE)==0 ){ |
| 176892 | pgnoSave = pgno+1; |
| 176893 | } |
| 176894 | |
| @@ -177309,12 +178215,11 @@ | |
| 177309 | ** Append a rowid and position-list size field to the writers output. |
| 177310 | */ |
| 177311 | static void fts5WriteAppendRowid( |
| 177312 | Fts5Index *p, |
| 177313 | Fts5SegWriter *pWriter, |
| 177314 | i64 iRowid, |
| 177315 | int nPos |
| 177316 | ){ |
| 177317 | if( p->rc==SQLITE_OK ){ |
| 177318 | Fts5PageWriter *pPage = &pWriter->writer; |
| 177319 | |
| 177320 | if( (pPage->buf.n + pPage->pgidx.n)>=p->pConfig->pgsz ){ |
| @@ -177337,12 +178242,10 @@ | |
| 177337 | fts5BufferAppendVarint(&p->rc, &pPage->buf, iRowid - pWriter->iPrevRowid); |
| 177338 | } |
| 177339 | pWriter->iPrevRowid = iRowid; |
| 177340 | pWriter->bFirstRowidInDoclist = 0; |
| 177341 | pWriter->bFirstRowidInPage = 0; |
| 177342 | |
| 177343 | fts5BufferAppendVarint(&p->rc, &pPage->buf, nPos); |
| 177344 | } |
| 177345 | } |
| 177346 | |
| 177347 | static void fts5WriteAppendPoslistData( |
| 177348 | Fts5Index *p, |
| @@ -177534,10 +178437,11 @@ | |
| 177534 | int nInput; /* Number of input segments */ |
| 177535 | Fts5SegWriter writer; /* Writer object */ |
| 177536 | Fts5StructureSegment *pSeg; /* Output segment */ |
| 177537 | Fts5Buffer term; |
| 177538 | int bOldest; /* True if the output segment is the oldest */ |
| 177539 | |
| 177540 | assert( iLvl<pStruct->nLevel ); |
| 177541 | assert( pLvl->nMerge<=pLvl->nSeg ); |
| 177542 | |
| 177543 | memset(&writer, 0, sizeof(Fts5SegWriter)); |
| @@ -177603,15 +178507,25 @@ | |
| 177603 | fts5BufferSet(&p->rc, &term, nTerm, pTerm); |
| 177604 | } |
| 177605 | |
| 177606 | /* Append the rowid to the output */ |
| 177607 | /* WRITEPOSLISTSIZE */ |
| 177608 | nPos = pSegIter->nPos*2 + pSegIter->bDel; |
| 177609 | fts5WriteAppendRowid(p, &writer, fts5MultiIterRowid(pIter), nPos); |
| 177610 | |
| 177611 | /* Append the position-list data to the output */ |
| 177612 | fts5ChunkIterate(p, pSegIter, (void*)&writer, fts5MergeChunkCallback); |
| 177613 | } |
| 177614 | |
| 177615 | /* Flush the last leaf page to disk. Set the output segment b-tree height |
| 177616 | ** and last leaf page number at the same time. */ |
| 177617 | fts5WriteFinish(p, &writer, &pSeg->pgnoLast); |
| @@ -177795,11 +178709,11 @@ | |
| 177795 | pStruct = fts5StructureRead(p); |
| 177796 | iSegid = fts5AllocateSegid(p, pStruct); |
| 177797 | |
| 177798 | if( iSegid ){ |
| 177799 | const int pgsz = p->pConfig->pgsz; |
| 177800 | |
| 177801 | Fts5StructureSegment *pSeg; /* New segment within pStruct */ |
| 177802 | Fts5Buffer *pBuf; /* Buffer in which to assemble leaf page */ |
| 177803 | Fts5Buffer *pPgidx; /* Buffer in which to assemble pgidx */ |
| 177804 | |
| 177805 | Fts5SegWriter writer; |
| @@ -177838,16 +178752,11 @@ | |
| 177838 | |
| 177839 | /* The entire doclist will not fit on this leaf. The following |
| 177840 | ** loop iterates through the poslists that make up the current |
| 177841 | ** doclist. */ |
| 177842 | while( p->rc==SQLITE_OK && iOff<nDoclist ){ |
| 177843 | int nPos; |
| 177844 | int nCopy; |
| 177845 | int bDummy; |
| 177846 | iOff += fts5GetVarint(&pDoclist[iOff], (u64*)&iDelta); |
| 177847 | nCopy = fts5GetPoslistSize(&pDoclist[iOff], &nPos, &bDummy); |
| 177848 | nCopy += nPos; |
| 177849 | iRowid += iDelta; |
| 177850 | |
| 177851 | if( writer.bFirstRowidInPage ){ |
| 177852 | fts5PutU16(&pBuf->p[0], (u16)pBuf->n); /* first rowid on page */ |
| 177853 | pBuf->n += sqlite3Fts5PutVarint(&pBuf->p[pBuf->n], iRowid); |
| @@ -177856,38 +178765,56 @@ | |
| 177856 | }else{ |
| 177857 | pBuf->n += sqlite3Fts5PutVarint(&pBuf->p[pBuf->n], iDelta); |
| 177858 | } |
| 177859 | assert( pBuf->n<=pBuf->nSpace ); |
| 177860 | |
| 177861 | if( (pBuf->n + pPgidx->n + nCopy) <= pgsz ){ |
| 177862 | /* The entire poslist will fit on the current leaf. So copy |
| 177863 | ** it in one go. */ |
| 177864 | fts5BufferSafeAppendBlob(pBuf, &pDoclist[iOff], nCopy); |
| 177865 | }else{ |
| 177866 | /* The entire poslist will not fit on this leaf. So it needs |
| 177867 | ** to be broken into sections. The only qualification being |
| 177868 | ** that each varint must be stored contiguously. */ |
| 177869 | const u8 *pPoslist = &pDoclist[iOff]; |
| 177870 | int iPos = 0; |
| 177871 | while( p->rc==SQLITE_OK ){ |
| 177872 | int nSpace = pgsz - pBuf->n - pPgidx->n; |
| 177873 | int n = 0; |
| 177874 | if( (nCopy - iPos)<=nSpace ){ |
| 177875 | n = nCopy - iPos; |
| 177876 | }else{ |
| 177877 | n = fts5PoslistPrefix(&pPoslist[iPos], nSpace); |
| 177878 | } |
| 177879 | assert( n>0 ); |
| 177880 | fts5BufferSafeAppendBlob(pBuf, &pPoslist[iPos], n); |
| 177881 | iPos += n; |
| 177882 | if( (pBuf->n + pPgidx->n)>=pgsz ){ |
| 177883 | fts5WriteFlushLeaf(p, &writer); |
| 177884 | } |
| 177885 | if( iPos>=nCopy ) break; |
| 177886 | } |
| 177887 | } |
| 177888 | iOff += nCopy; |
| 177889 | } |
| 177890 | } |
| 177891 | |
| 177892 | /* TODO2: Doclist terminator written here. */ |
| 177893 | /* pBuf->p[pBuf->n++] = '\0'; */ |
| @@ -178018,10 +178945,18 @@ | |
| 178018 | Fts5Buffer *pBuf; /* Append to this buffer */ |
| 178019 | Fts5Colset *pColset; /* Restrict matches to this column */ |
| 178020 | int eState; /* See above */ |
| 178021 | }; |
| 178022 | |
| 178023 | /* |
| 178024 | ** TODO: Make this more efficient! |
| 178025 | */ |
| 178026 | static int fts5IndexColsetTest(Fts5Colset *pColset, int iCol){ |
| 178027 | int i; |
| @@ -178028,10 +178963,32 @@ | |
| 178028 | for(i=0; i<pColset->nCol; i++){ |
| 178029 | if( pColset->aiCol[i]==iCol ) return 1; |
| 178030 | } |
| 178031 | return 0; |
| 178032 | } |
| 178033 | |
| 178034 | static void fts5PoslistFilterCallback( |
| 178035 | Fts5Index *p, |
| 178036 | void *pContext, |
| 178037 | const u8 *pChunk, int nChunk |
| @@ -178096,16 +179053,24 @@ | |
| 178096 | ){ |
| 178097 | if( 0==fts5BufferGrow(&p->rc, pBuf, pSeg->nPos) ){ |
| 178098 | if( pColset==0 ){ |
| 178099 | fts5ChunkIterate(p, pSeg, (void*)pBuf, fts5PoslistCallback); |
| 178100 | }else{ |
| 178101 | PoslistCallbackCtx sCtx; |
| 178102 | sCtx.pBuf = pBuf; |
| 178103 | sCtx.pColset = pColset; |
| 178104 | sCtx.eState = fts5IndexColsetTest(pColset, 0); |
| 178105 | assert( sCtx.eState==0 || sCtx.eState==1 ); |
| 178106 | fts5ChunkIterate(p, pSeg, (void*)&sCtx, fts5PoslistFilterCallback); |
| 178107 | } |
| 178108 | } |
| 178109 | } |
| 178110 | |
| 178111 | /* |
| @@ -178144,10 +179109,20 @@ | |
| 178144 | prev = *p++; |
| 178145 | } |
| 178146 | return p - (*pa); |
| 178147 | } |
| 178148 | |
| 178149 | |
| 178150 | /* |
| 178151 | ** Iterator pMulti currently points to a valid entry (not EOF). This |
| 178152 | ** function appends the following to buffer pBuf: |
| 178153 | ** |
| @@ -178171,12 +179146,12 @@ | |
| 178171 | if( p->rc==SQLITE_OK ){ |
| 178172 | Fts5SegIter *pSeg = &pMulti->aSeg[ pMulti->aFirst[1].iFirst ]; |
| 178173 | assert( fts5MultiIterEof(p, pMulti)==0 ); |
| 178174 | assert( pSeg->nPos>0 ); |
| 178175 | if( 0==fts5BufferGrow(&p->rc, pBuf, pSeg->nPos+9+9) ){ |
| 178176 | |
| 178177 | if( pSeg->iLeafOffset+pSeg->nPos<=pSeg->pLeaf->szLeaf |
| 178178 | && (pColset==0 || pColset->nCol==1) |
| 178179 | ){ |
| 178180 | const u8 *pPos = &pSeg->pLeaf->p[pSeg->iLeafOffset]; |
| 178181 | int nPos; |
| 178182 | if( pColset ){ |
| @@ -178217,16 +179192,16 @@ | |
| 178217 | sqlite3Fts5PutVarint(&pBuf->p[iSv2], nActual*2); |
| 178218 | } |
| 178219 | } |
| 178220 | } |
| 178221 | } |
| 178222 | |
| 178223 | } |
| 178224 | } |
| 178225 | |
| 178226 | return 0; |
| 178227 | } |
| 178228 | |
| 178229 | static void fts5DoclistIterNext(Fts5DoclistIter *pIter){ |
| 178230 | u8 *p = pIter->aPoslist + pIter->nSize + pIter->nPoslist; |
| 178231 | |
| 178232 | assert( pIter->aPoslist ); |
| @@ -178283,10 +179258,73 @@ | |
| 178283 | #define fts5MergeAppendDocid(pBuf, iLastRowid, iRowid) { \ |
| 178284 | assert( (pBuf)->n!=0 || (iLastRowid)==0 ); \ |
| 178285 | fts5BufferSafeAppendVarint((pBuf), (iRowid) - (iLastRowid)); \ |
| 178286 | (iLastRowid) = (iRowid); \ |
| 178287 | } |
| 178288 | |
| 178289 | /* |
| 178290 | ** Buffers p1 and p2 contain doclists. This function merges the content |
| 178291 | ** of the two doclists together and sets buffer p1 to the result before |
| 178292 | ** returning. |
| @@ -178352,11 +179390,13 @@ | |
| 178352 | sqlite3Fts5PoslistNext64(a2, i2.nPoslist, &iOff2, &iPos2); |
| 178353 | if( iPos1==iPos2 ){ |
| 178354 | sqlite3Fts5PoslistNext64(a1, i1.nPoslist, &iOff1,&iPos1); |
| 178355 | } |
| 178356 | } |
| 178357 | p->rc = sqlite3Fts5PoslistWriterAppend(&tmp, &writer, iNew); |
| 178358 | } |
| 178359 | |
| 178360 | /* WRITEPOSLISTSIZE */ |
| 178361 | fts5BufferSafeAppendVarint(&out, tmp.n * 2); |
| 178362 | fts5BufferSafeAppendBlob(&out, tmp.p, tmp.n); |
| @@ -178369,16 +179409,10 @@ | |
| 178369 | fts5BufferFree(&tmp); |
| 178370 | fts5BufferFree(&out); |
| 178371 | } |
| 178372 | } |
| 178373 | |
| 178374 | static void fts5BufferSwap(Fts5Buffer *p1, Fts5Buffer *p2){ |
| 178375 | Fts5Buffer tmp = *p1; |
| 178376 | *p1 = *p2; |
| 178377 | *p2 = tmp; |
| 178378 | } |
| 178379 | |
| 178380 | static void fts5SetupPrefixIter( |
| 178381 | Fts5Index *p, /* Index to read from */ |
| 178382 | int bDesc, /* True for "ORDER BY rowid DESC" */ |
| 178383 | const u8 *pToken, /* Buffer containing prefix to match */ |
| 178384 | int nToken, /* Size of buffer pToken in bytes */ |
| @@ -178386,10 +179420,20 @@ | |
| 178386 | Fts5IndexIter **ppIter /* OUT: New iterator */ |
| 178387 | ){ |
| 178388 | Fts5Structure *pStruct; |
| 178389 | Fts5Buffer *aBuf; |
| 178390 | const int nBuf = 32; |
| 178391 | |
| 178392 | aBuf = (Fts5Buffer*)fts5IdxMalloc(p, sizeof(Fts5Buffer)*nBuf); |
| 178393 | pStruct = fts5StructureRead(p); |
| 178394 | |
| 178395 | if( aBuf && pStruct ){ |
| @@ -178419,25 +179463,25 @@ | |
| 178419 | assert( i<nBuf ); |
| 178420 | if( aBuf[i].n==0 ){ |
| 178421 | fts5BufferSwap(&doclist, &aBuf[i]); |
| 178422 | fts5BufferZero(&doclist); |
| 178423 | }else{ |
| 178424 | fts5MergePrefixLists(p, &doclist, &aBuf[i]); |
| 178425 | fts5BufferZero(&aBuf[i]); |
| 178426 | } |
| 178427 | } |
| 178428 | iLastRowid = 0; |
| 178429 | } |
| 178430 | |
| 178431 | if( !fts5AppendPoslist(p, iRowid-iLastRowid, p1, pColset, &doclist) ){ |
| 178432 | iLastRowid = iRowid; |
| 178433 | } |
| 178434 | } |
| 178435 | |
| 178436 | for(i=0; i<nBuf; i++){ |
| 178437 | if( p->rc==SQLITE_OK ){ |
| 178438 | fts5MergePrefixLists(p, &doclist, &aBuf[i]); |
| 178439 | } |
| 178440 | fts5BufferFree(&aBuf[i]); |
| 178441 | } |
| 178442 | fts5MultiIterFree(p, p1); |
| 178443 | |
| @@ -178463,11 +179507,11 @@ | |
| 178463 | static int sqlite3Fts5IndexBeginWrite(Fts5Index *p, int bDelete, i64 iRowid){ |
| 178464 | assert( p->rc==SQLITE_OK ); |
| 178465 | |
| 178466 | /* Allocate the hash table if it has not already been allocated */ |
| 178467 | if( p->pHash==0 ){ |
| 178468 | p->rc = sqlite3Fts5HashNew(&p->pHash, &p->nPendingData); |
| 178469 | } |
| 178470 | |
| 178471 | /* Flush the hash table to disk if required */ |
| 178472 | if( iRowid<p->iWriteRowid |
| 178473 | || (iRowid==p->iWriteRowid && p->bDelete==0) |
| @@ -178584,11 +179628,15 @@ | |
| 178584 | /* |
| 178585 | ** Argument p points to a buffer containing utf-8 text that is n bytes in |
| 178586 | ** size. Return the number of bytes in the nChar character prefix of the |
| 178587 | ** buffer, or 0 if there are less than nChar characters in total. |
| 178588 | */ |
| 178589 | static int fts5IndexCharlenToBytelen(const char *p, int nByte, int nChar){ |
| 178590 | int n = 0; |
| 178591 | int i; |
| 178592 | for(i=0; i<nChar; i++){ |
| 178593 | if( n>=nByte ) return 0; /* Input contains fewer than nChar chars */ |
| 178594 | if( (unsigned char)p[n++]>=0xc0 ){ |
| @@ -178641,11 +179689,12 @@ | |
| 178641 | rc = sqlite3Fts5HashWrite( |
| 178642 | p->pHash, p->iWriteRowid, iCol, iPos, FTS5_MAIN_PREFIX, pToken, nToken |
| 178643 | ); |
| 178644 | |
| 178645 | for(i=0; i<pConfig->nPrefix && rc==SQLITE_OK; i++){ |
| 178646 | int nByte = fts5IndexCharlenToBytelen(pToken, nToken, pConfig->aPrefix[i]); |
| 178647 | if( nByte ){ |
| 178648 | rc = sqlite3Fts5HashWrite(p->pHash, |
| 178649 | p->iWriteRowid, iCol, iPos, (char)(FTS5_MAIN_PREFIX+i+1), pToken, |
| 178650 | nByte |
| 178651 | ); |
| @@ -178819,13 +179868,20 @@ | |
| 178819 | const u8 **pp, /* OUT: Pointer to position-list data */ |
| 178820 | int *pn, /* OUT: Size of position-list in bytes */ |
| 178821 | i64 *piRowid /* OUT: Current rowid */ |
| 178822 | ){ |
| 178823 | Fts5SegIter *pSeg = &pIter->aSeg[ pIter->aFirst[1].iFirst ]; |
| 178824 | assert( pIter->pIndex->rc==SQLITE_OK ); |
| 178825 | *piRowid = pSeg->iRowid; |
| 178826 | if( pSeg->iLeafOffset+pSeg->nPos<=pSeg->pLeaf->szLeaf ){ |
| 178827 | u8 *pPos = &pSeg->pLeaf->p[pSeg->iLeafOffset]; |
| 178828 | if( pColset==0 || pIter->bFiltered ){ |
| 178829 | *pn = pSeg->nPos; |
| 178830 | *pp = pPos; |
| 178831 | }else if( pColset->nCol==1 ){ |
| @@ -178838,15 +179894,28 @@ | |
| 178838 | *pn = pIter->poslist.n; |
| 178839 | } |
| 178840 | }else{ |
| 178841 | fts5BufferZero(&pIter->poslist); |
| 178842 | fts5SegiterPoslist(pIter->pIndex, pSeg, pColset, &pIter->poslist); |
| 178843 | *pp = pIter->poslist.p; |
| 178844 | *pn = pIter->poslist.n; |
| 178845 | } |
| 178846 | return fts5IndexReturn(pIter->pIndex); |
| 178847 | } |
| 178848 | |
| 178849 | /* |
| 178850 | ** This function is similar to sqlite3Fts5IterPoslist(), except that it |
| 178851 | ** copies the position list into the buffer supplied as the second |
| 178852 | ** argument. |
| @@ -178957,11 +180026,11 @@ | |
| 178957 | */ |
| 178958 | |
| 178959 | /* |
| 178960 | ** Return a simple checksum value based on the arguments. |
| 178961 | */ |
| 178962 | static u64 fts5IndexEntryCksum( |
| 178963 | i64 iRowid, |
| 178964 | int iCol, |
| 178965 | int iPos, |
| 178966 | int iIdx, |
| 178967 | const char *pTerm, |
| @@ -179027,34 +180096,41 @@ | |
| 179027 | const char *z, /* Index key to query for */ |
| 179028 | int n, /* Size of index key in bytes */ |
| 179029 | int flags, /* Flags for Fts5IndexQuery */ |
| 179030 | u64 *pCksum /* IN/OUT: Checksum value */ |
| 179031 | ){ |
| 179032 | u64 cksum = *pCksum; |
| 179033 | Fts5IndexIter *pIdxIter = 0; |
| 179034 | int rc = sqlite3Fts5IndexQuery(p, z, n, flags, 0, &pIdxIter); |
| 179035 | |
| 179036 | while( rc==SQLITE_OK && 0==sqlite3Fts5IterEof(pIdxIter) ){ |
| 179037 | i64 dummy; |
| 179038 | const u8 *pPos; |
| 179039 | int nPos; |
| 179040 | i64 rowid = sqlite3Fts5IterRowid(pIdxIter); |
| 179041 | rc = sqlite3Fts5IterPoslist(pIdxIter, 0, &pPos, &nPos, &dummy); |
| 179042 | if( rc==SQLITE_OK ){ |
| 179043 | Fts5PoslistReader sReader; |
| 179044 | for(sqlite3Fts5PoslistReaderInit(pPos, nPos, &sReader); |
| 179045 | sReader.bEof==0; |
| 179046 | sqlite3Fts5PoslistReaderNext(&sReader) |
| 179047 | ){ |
| 179048 | int iCol = FTS5_POS2COLUMN(sReader.iPos); |
| 179049 | int iOff = FTS5_POS2OFFSET(sReader.iPos); |
| 179050 | cksum ^= fts5IndexEntryCksum(rowid, iCol, iOff, iIdx, z, n); |
| 179051 | } |
| 179052 | rc = sqlite3Fts5IterNext(pIdxIter); |
| 179053 | } |
| 179054 | } |
| 179055 | sqlite3Fts5IterClose(pIdxIter); |
| 179056 | |
| 179057 | *pCksum = cksum; |
| 179058 | return rc; |
| 179059 | } |
| 179060 | |
| @@ -179344,18 +180420,19 @@ | |
| 179344 | |
| 179345 | |
| 179346 | /* |
| 179347 | ** Run internal checks to ensure that the FTS index (a) is internally |
| 179348 | ** consistent and (b) contains entries for which the XOR of the checksums |
| 179349 | ** as calculated by fts5IndexEntryCksum() is cksum. |
| 179350 | ** |
| 179351 | ** Return SQLITE_CORRUPT if any of the internal checks fail, or if the |
| 179352 | ** checksum does not match. Return SQLITE_OK if all checks pass without |
| 179353 | ** error, or some other SQLite error code if another error (e.g. OOM) |
| 179354 | ** occurs. |
| 179355 | */ |
| 179356 | static int sqlite3Fts5IndexIntegrityCheck(Fts5Index *p, u64 cksum){ |
| 179357 | u64 cksum2 = 0; /* Checksum based on contents of indexes */ |
| 179358 | Fts5Buffer poslist = {0,0,0}; /* Buffer used to hold a poslist */ |
| 179359 | Fts5IndexIter *pIter; /* Used to iterate through entire index */ |
| 179360 | Fts5Structure *pStruct; /* Index structure */ |
| 179361 | |
| @@ -179403,16 +180480,22 @@ | |
| 179403 | char *z = (char*)fts5MultiIterTerm(pIter, &n); |
| 179404 | |
| 179405 | /* If this is a new term, query for it. Update cksum3 with the results. */ |
| 179406 | fts5TestTerm(p, &term, z, n, cksum2, &cksum3); |
| 179407 | |
| 179408 | poslist.n = 0; |
| 179409 | fts5SegiterPoslist(p, &pIter->aSeg[pIter->aFirst[1].iFirst] , 0, &poslist); |
| 179410 | while( 0==sqlite3Fts5PoslistNext64(poslist.p, poslist.n, &iOff, &iPos) ){ |
| 179411 | int iCol = FTS5_POS2COLUMN(iPos); |
| 179412 | int iTokOff = FTS5_POS2OFFSET(iPos); |
| 179413 | cksum2 ^= fts5IndexEntryCksum(iRowid, iCol, iTokOff, -1, z, n); |
| 179414 | } |
| 179415 | } |
| 179416 | fts5TestTerm(p, &term, 0, 0, cksum2, &cksum3); |
| 179417 | |
| 179418 | fts5MultiIterFree(p, pIter); |
| @@ -179424,38 +180507,10 @@ | |
| 179424 | #endif |
| 179425 | fts5BufferFree(&poslist); |
| 179426 | return fts5IndexReturn(p); |
| 179427 | } |
| 179428 | |
| 179429 | |
| 179430 | /* |
| 179431 | ** Calculate and return a checksum that is the XOR of the index entry |
| 179432 | ** checksum of all entries that would be generated by the token specified |
| 179433 | ** by the final 5 arguments. |
| 179434 | */ |
| 179435 | static u64 sqlite3Fts5IndexCksum( |
| 179436 | Fts5Config *pConfig, /* Configuration object */ |
| 179437 | i64 iRowid, /* Document term appears in */ |
| 179438 | int iCol, /* Column term appears in */ |
| 179439 | int iPos, /* Position term appears in */ |
| 179440 | const char *pTerm, int nTerm /* Term at iPos */ |
| 179441 | ){ |
| 179442 | u64 ret = 0; /* Return value */ |
| 179443 | int iIdx; /* For iterating through indexes */ |
| 179444 | |
| 179445 | ret = fts5IndexEntryCksum(iRowid, iCol, iPos, 0, pTerm, nTerm); |
| 179446 | |
| 179447 | for(iIdx=0; iIdx<pConfig->nPrefix; iIdx++){ |
| 179448 | int nByte = fts5IndexCharlenToBytelen(pTerm, nTerm, pConfig->aPrefix[iIdx]); |
| 179449 | if( nByte ){ |
| 179450 | ret ^= fts5IndexEntryCksum(iRowid, iCol, iPos, iIdx+1, pTerm, nByte); |
| 179451 | } |
| 179452 | } |
| 179453 | |
| 179454 | return ret; |
| 179455 | } |
| 179456 | |
| 179457 | /************************************************************************* |
| 179458 | ************************************************************************** |
| 179459 | ** Below this point is the implementation of the fts5_decode() scalar |
| 179460 | ** function only. |
| 179461 | */ |
| @@ -180039,10 +181094,11 @@ | |
| 180039 | #define FTS5CSR_REQUIRE_DOCSIZE 0x02 |
| 180040 | #define FTS5CSR_REQUIRE_INST 0x04 |
| 180041 | #define FTS5CSR_EOF 0x08 |
| 180042 | #define FTS5CSR_FREE_ZRANK 0x10 |
| 180043 | #define FTS5CSR_REQUIRE_RESEEK 0x20 |
| 180044 | |
| 180045 | #define BitFlagAllTest(x,y) (((x) & (y))==(y)) |
| 180046 | #define BitFlagTest(x,y) (((x) & (y))!=0) |
| 180047 | |
| 180048 | |
| @@ -180452,10 +181508,11 @@ | |
| 180452 | static void fts5CsrNewrow(Fts5Cursor *pCsr){ |
| 180453 | CsrFlagSet(pCsr, |
| 180454 | FTS5CSR_REQUIRE_CONTENT |
| 180455 | | FTS5CSR_REQUIRE_DOCSIZE |
| 180456 | | FTS5CSR_REQUIRE_INST |
| 180457 | ); |
| 180458 | } |
| 180459 | |
| 180460 | static void fts5FreeCursorComponents(Fts5Cursor *pCsr){ |
| 180461 | Fts5Table *pTab = (Fts5Table*)(pCsr->base.pVtab); |
| @@ -180534,19 +181591,22 @@ | |
| 180534 | |
| 180535 | pSorter->iRowid = sqlite3_column_int64(pSorter->pStmt, 0); |
| 180536 | nBlob = sqlite3_column_bytes(pSorter->pStmt, 1); |
| 180537 | aBlob = a = sqlite3_column_blob(pSorter->pStmt, 1); |
| 180538 | |
| 180539 | for(i=0; i<(pSorter->nIdx-1); i++){ |
| 180540 | int iVal; |
| 180541 | a += fts5GetVarint32(a, iVal); |
| 180542 | iOff += iVal; |
| 180543 | pSorter->aIdx[i] = iOff; |
| 180544 | } |
| 180545 | pSorter->aIdx[i] = &aBlob[nBlob] - a; |
| 180546 | |
| 180547 | pSorter->aPoslist = a; |
| 180548 | fts5CsrNewrow(pCsr); |
| 180549 | } |
| 180550 | |
| 180551 | return rc; |
| 180552 | } |
| @@ -180980,10 +182040,11 @@ | |
| 180980 | assert( pCsr->iLastRowid==LARGEST_INT64 ); |
| 180981 | assert( pCsr->iFirstRowid==SMALLEST_INT64 ); |
| 180982 | pCsr->ePlan = FTS5_PLAN_SOURCE; |
| 180983 | pCsr->pExpr = pTab->pSortCsr->pExpr; |
| 180984 | rc = fts5CursorFirst(pTab, pCsr, bDesc); |
| 180985 | }else if( pMatch ){ |
| 180986 | const char *zExpr = (const char*)sqlite3_value_text(apVal[0]); |
| 180987 | if( zExpr==0 ) zExpr = ""; |
| 180988 | |
| 180989 | rc = fts5CursorParseRank(pConfig, pCsr, pRank); |
| @@ -181409,10 +182470,12 @@ | |
| 181409 | fts5CheckTransactionState(pTab, FTS5_ROLLBACK, 0); |
| 181410 | rc = sqlite3Fts5StorageRollback(pTab->pStorage); |
| 181411 | return rc; |
| 181412 | } |
| 181413 | |
| 181414 | static void *fts5ApiUserData(Fts5Context *pCtx){ |
| 181415 | Fts5Cursor *pCsr = (Fts5Cursor*)pCtx; |
| 181416 | return pCsr->pAux->pUserData; |
| 181417 | } |
| 181418 | |
| @@ -181458,21 +182521,76 @@ | |
| 181458 | static int fts5ApiPhraseSize(Fts5Context *pCtx, int iPhrase){ |
| 181459 | Fts5Cursor *pCsr = (Fts5Cursor*)pCtx; |
| 181460 | return sqlite3Fts5ExprPhraseSize(pCsr->pExpr, iPhrase); |
| 181461 | } |
| 181462 | |
| 181463 | static int fts5CsrPoslist(Fts5Cursor *pCsr, int iPhrase, const u8 **pa){ |
| 181464 | int n; |
| 181465 | if( pCsr->pSorter ){ |
| 181466 | Fts5Sorter *pSorter = pCsr->pSorter; |
| 181467 | int i1 = (iPhrase==0 ? 0 : pSorter->aIdx[iPhrase-1]); |
| 181468 | n = pSorter->aIdx[iPhrase] - i1; |
| 181469 | *pa = &pSorter->aPoslist[i1]; |
| 181470 | }else{ |
| 181471 | n = sqlite3Fts5ExprPoslist(pCsr->pExpr, iPhrase, pa); |
| 181472 | } |
| 181473 | return n; |
| 181474 | } |
| 181475 | |
| 181476 | /* |
| 181477 | ** Ensure that the Fts5Cursor.nInstCount and aInst[] variables are populated |
| 181478 | ** correctly for the current view. Return SQLITE_OK if successful, or an |
| @@ -181493,47 +182611,50 @@ | |
| 181493 | if( aIter ){ |
| 181494 | int nInst = 0; /* Number instances seen so far */ |
| 181495 | int i; |
| 181496 | |
| 181497 | /* Initialize all iterators */ |
| 181498 | for(i=0; i<nIter; i++){ |
| 181499 | const u8 *a; |
| 181500 | int n = fts5CsrPoslist(pCsr, i, &a); |
| 181501 | sqlite3Fts5PoslistReaderInit(a, n, &aIter[i]); |
| 181502 | } |
| 181503 | |
| 181504 | while( 1 ){ |
| 181505 | int *aInst; |
| 181506 | int iBest = -1; |
| 181507 | for(i=0; i<nIter; i++){ |
| 181508 | if( (aIter[i].bEof==0) |
| 181509 | && (iBest<0 || aIter[i].iPos<aIter[iBest].iPos) |
| 181510 | ){ |
| 181511 | iBest = i; |
| 181512 | } |
| 181513 | } |
| 181514 | if( iBest<0 ) break; |
| 181515 | |
| 181516 | nInst++; |
| 181517 | if( nInst>=pCsr->nInstAlloc ){ |
| 181518 | pCsr->nInstAlloc = pCsr->nInstAlloc ? pCsr->nInstAlloc*2 : 32; |
| 181519 | aInst = (int*)sqlite3_realloc( |
| 181520 | pCsr->aInst, pCsr->nInstAlloc*sizeof(int)*3 |
| 181521 | ); |
| 181522 | if( aInst ){ |
| 181523 | pCsr->aInst = aInst; |
| 181524 | }else{ |
| 181525 | rc = SQLITE_NOMEM; |
| 181526 | break; |
| 181527 | } |
| 181528 | } |
| 181529 | |
| 181530 | aInst = &pCsr->aInst[3 * (nInst-1)]; |
| 181531 | aInst[0] = iBest; |
| 181532 | aInst[1] = FTS5_POS2COLUMN(aIter[iBest].iPos); |
| 181533 | aInst[2] = FTS5_POS2OFFSET(aIter[iBest].iPos); |
| 181534 | sqlite3Fts5PoslistReaderNext(&aIter[iBest]); |
| 181535 | } |
| 181536 | |
| 181537 | pCsr->nInstCount = nInst; |
| 181538 | CsrFlagClear(pCsr, FTS5CSR_REQUIRE_INST); |
| 181539 | } |
| @@ -181562,10 +182683,16 @@ | |
| 181562 | if( CsrFlagTest(pCsr, FTS5CSR_REQUIRE_INST)==0 |
| 181563 | || SQLITE_OK==(rc = fts5CacheInstArray(pCsr)) |
| 181564 | ){ |
| 181565 | if( iIdx<0 || iIdx>=pCsr->nInstCount ){ |
| 181566 | rc = SQLITE_RANGE; |
| 181567 | }else{ |
| 181568 | *piPhrase = pCsr->aInst[iIdx*3]; |
| 181569 | *piCol = pCsr->aInst[iIdx*3 + 1]; |
| 181570 | *piOff = pCsr->aInst[iIdx*3 + 2]; |
| 181571 | } |
| @@ -181575,31 +182702,10 @@ | |
| 181575 | |
| 181576 | static sqlite3_int64 fts5ApiRowid(Fts5Context *pCtx){ |
| 181577 | return fts5CursorRowid((Fts5Cursor*)pCtx); |
| 181578 | } |
| 181579 | |
| 181580 | static int fts5ApiColumnText( |
| 181581 | Fts5Context *pCtx, |
| 181582 | int iCol, |
| 181583 | const char **pz, |
| 181584 | int *pn |
| 181585 | ){ |
| 181586 | int rc = SQLITE_OK; |
| 181587 | Fts5Cursor *pCsr = (Fts5Cursor*)pCtx; |
| 181588 | if( fts5IsContentless((Fts5Table*)(pCsr->base.pVtab)) ){ |
| 181589 | *pz = 0; |
| 181590 | *pn = 0; |
| 181591 | }else{ |
| 181592 | rc = fts5SeekCursor(pCsr, 0); |
| 181593 | if( rc==SQLITE_OK ){ |
| 181594 | *pz = (const char*)sqlite3_column_text(pCsr->pStmt, iCol+1); |
| 181595 | *pn = sqlite3_column_bytes(pCsr->pStmt, iCol+1); |
| 181596 | } |
| 181597 | } |
| 181598 | return rc; |
| 181599 | } |
| 181600 | |
| 181601 | static int fts5ColumnSizeCb( |
| 181602 | void *pContext, /* Pointer to int */ |
| 181603 | int tflags, |
| 181604 | const char *pToken, /* Buffer containing token */ |
| 181605 | int nToken, /* Size of token in bytes */ |
| @@ -181740,23 +182846,94 @@ | |
| 181740 | } |
| 181741 | *piOff += (iVal-2); |
| 181742 | } |
| 181743 | } |
| 181744 | |
| 181745 | static void fts5ApiPhraseFirst( |
| 181746 | Fts5Context *pCtx, |
| 181747 | int iPhrase, |
| 181748 | Fts5PhraseIter *pIter, |
| 181749 | int *piCol, int *piOff |
| 181750 | ){ |
| 181751 | Fts5Cursor *pCsr = (Fts5Cursor*)pCtx; |
| 181752 | int n = fts5CsrPoslist(pCsr, iPhrase, &pIter->a); |
| 181753 | pIter->b = &pIter->a[n]; |
| 181754 | *piCol = 0; |
| 181755 | *piOff = 0; |
| 181756 | fts5ApiPhraseNext(pCtx, pIter, piCol, piOff); |
| 181757 | } |
| 181758 | |
| 181759 | static int fts5ApiQueryPhrase(Fts5Context*, int, void*, |
| 181760 | int(*)(const Fts5ExtensionApi*, Fts5Context*, void*) |
| 181761 | ); |
| 181762 | |
| @@ -181777,12 +182954,13 @@ | |
| 181777 | fts5ApiQueryPhrase, |
| 181778 | fts5ApiSetAuxdata, |
| 181779 | fts5ApiGetAuxdata, |
| 181780 | fts5ApiPhraseFirst, |
| 181781 | fts5ApiPhraseNext, |
| 181782 | }; |
| 181783 | |
| 181784 | |
| 181785 | /* |
| 181786 | ** Implementation of API function xQueryPhrase(). |
| 181787 | */ |
| 181788 | static int fts5ApiQueryPhrase( |
| @@ -181911,24 +183089,50 @@ | |
| 181911 | int rc = SQLITE_OK; |
| 181912 | int nPhrase = sqlite3Fts5ExprPhraseCount(pCsr->pExpr); |
| 181913 | Fts5Buffer val; |
| 181914 | |
| 181915 | memset(&val, 0, sizeof(Fts5Buffer)); |
| 181916 | |
| 181917 | /* Append the varints */ |
| 181918 | for(i=0; i<(nPhrase-1); i++){ |
| 181919 | const u8 *dummy; |
| 181920 | int nByte = sqlite3Fts5ExprPoslist(pCsr->pExpr, i, &dummy); |
| 181921 | sqlite3Fts5BufferAppendVarint(&rc, &val, nByte); |
| 181922 | } |
| 181923 | |
| 181924 | /* Append the position lists */ |
| 181925 | for(i=0; i<nPhrase; i++){ |
| 181926 | const u8 *pPoslist; |
| 181927 | int nPoslist; |
| 181928 | nPoslist = sqlite3Fts5ExprPoslist(pCsr->pExpr, i, &pPoslist); |
| 181929 | sqlite3Fts5BufferAppendBlob(&rc, &val, nPoslist, pPoslist); |
| 181930 | } |
| 181931 | |
| 181932 | sqlite3_result_blob(pCtx, val.p, val.n, sqlite3_free); |
| 181933 | return rc; |
| 181934 | } |
| @@ -182247,11 +183451,11 @@ | |
| 182247 | sqlite3_context *pCtx, /* Function call context */ |
| 182248 | int nArg, /* Number of args */ |
| 182249 | sqlite3_value **apVal /* Function arguments */ |
| 182250 | ){ |
| 182251 | assert( nArg==0 ); |
| 182252 | sqlite3_result_text(pCtx, "fts5: 2016-01-06 11:01:07 fd0a50f0797d154fefff724624f00548b5320566", -1, SQLITE_TRANSIENT); |
| 182253 | } |
| 182254 | |
| 182255 | static int fts5Init(sqlite3 *db){ |
| 182256 | static const sqlite3_module fts5Mod = { |
| 182257 | /* iVersion */ 2, |
| @@ -183179,32 +184383,77 @@ | |
| 183179 | struct Fts5IntegrityCtx { |
| 183180 | i64 iRowid; |
| 183181 | int iCol; |
| 183182 | int szCol; |
| 183183 | u64 cksum; |
| 183184 | Fts5Config *pConfig; |
| 183185 | }; |
| 183186 | |
| 183187 | /* |
| 183188 | ** Tokenization callback used by integrity check. |
| 183189 | */ |
| 183190 | static int fts5StorageIntegrityCallback( |
| 183191 | void *pContext, /* Pointer to Fts5InsertCtx object */ |
| 183192 | int tflags, |
| 183193 | const char *pToken, /* Buffer containing token */ |
| 183194 | int nToken, /* Size of token in bytes */ |
| 183195 | int iStart, /* Start offset of token */ |
| 183196 | int iEnd /* End offset of token */ |
| 183197 | ){ |
| 183198 | Fts5IntegrityCtx *pCtx = (Fts5IntegrityCtx*)pContext; |
| 183199 | if( (tflags & FTS5_TOKEN_COLOCATED)==0 || pCtx->szCol==0 ){ |
| 183200 | pCtx->szCol++; |
| 183201 | } |
| 183202 | pCtx->cksum ^= sqlite3Fts5IndexCksum( |
| 183203 | pCtx->pConfig, pCtx->iRowid, pCtx->iCol, pCtx->szCol-1, pToken, nToken |
| 183204 | ); |
| 183205 | return SQLITE_OK; |
| 183206 | } |
| 183207 | |
| 183208 | /* |
| 183209 | ** Check that the contents of the FTS index match that of the %_content |
| 183210 | ** table. Return SQLITE_OK if they do, or SQLITE_CORRUPT if not. Return |
| @@ -183235,27 +184484,42 @@ | |
| 183235 | int i; |
| 183236 | ctx.iRowid = sqlite3_column_int64(pScan, 0); |
| 183237 | ctx.szCol = 0; |
| 183238 | if( pConfig->bColumnsize ){ |
| 183239 | rc = sqlite3Fts5StorageDocsize(p, ctx.iRowid, aColSize); |
| 183240 | } |
| 183241 | for(i=0; rc==SQLITE_OK && i<pConfig->nCol; i++){ |
| 183242 | if( pConfig->abUnindexed[i] ) continue; |
| 183243 | ctx.iCol = i; |
| 183244 | ctx.szCol = 0; |
| 183245 | rc = sqlite3Fts5Tokenize(pConfig, |
| 183246 | FTS5_TOKENIZE_DOCUMENT, |
| 183247 | (const char*)sqlite3_column_text(pScan, i+1), |
| 183248 | sqlite3_column_bytes(pScan, i+1), |
| 183249 | (void*)&ctx, |
| 183250 | fts5StorageIntegrityCallback |
| 183251 | ); |
| 183252 | if( pConfig->bColumnsize && ctx.szCol!=aColSize[i] ){ |
| 183253 | rc = FTS5_CORRUPT; |
| 183254 | } |
| 183255 | aTotalSize[i] += ctx.szCol; |
| 183256 | } |
| 183257 | if( rc!=SQLITE_OK ) break; |
| 183258 | } |
| 183259 | rc2 = sqlite3_reset(pScan); |
| 183260 | if( rc==SQLITE_OK ) rc = rc2; |
| 183261 | } |
| @@ -185777,11 +187041,11 @@ | |
| 185777 | |
| 185778 | pCsr->rowid++; |
| 185779 | |
| 185780 | if( pTab->eType==FTS5_VOCAB_COL ){ |
| 185781 | for(pCsr->iCol++; pCsr->iCol<nCol; pCsr->iCol++){ |
| 185782 | if( pCsr->aCnt[pCsr->iCol] ) break; |
| 185783 | } |
| 185784 | } |
| 185785 | |
| 185786 | if( pTab->eType==FTS5_VOCAB_ROW || pCsr->iCol>=nCol ){ |
| 185787 | if( sqlite3Fts5IterEof(pCsr->pIter) ){ |
| @@ -185810,28 +187074,56 @@ | |
| 185810 | i64 dummy; |
| 185811 | const u8 *pPos; int nPos; /* Position list */ |
| 185812 | i64 iPos = 0; /* 64-bit position read from poslist */ |
| 185813 | int iOff = 0; /* Current offset within position list */ |
| 185814 | |
| 185815 | rc = sqlite3Fts5IterPoslist(pCsr->pIter, 0, &pPos, &nPos, &dummy); |
| 185816 | if( rc==SQLITE_OK ){ |
| 185817 | if( pTab->eType==FTS5_VOCAB_ROW ){ |
| 185818 | while( 0==sqlite3Fts5PoslistNext64(pPos, nPos, &iOff, &iPos) ){ |
| 185819 | pCsr->aCnt[0]++; |
| 185820 | } |
| 185821 | pCsr->aDoc[0]++; |
| 185822 | }else{ |
| 185823 | int iCol = -1; |
| 185824 | while( 0==sqlite3Fts5PoslistNext64(pPos, nPos, &iOff, &iPos) ){ |
| 185825 | int ii = FTS5_POS2COLUMN(iPos); |
| 185826 | pCsr->aCnt[ii]++; |
| 185827 | if( iCol!=ii ){ |
| 185828 | pCsr->aDoc[ii]++; |
| 185829 | iCol = ii; |
| 185830 | } |
| 185831 | } |
| 185832 | } |
| 185833 | rc = sqlite3Fts5IterNextScan(pCsr->pIter); |
| 185834 | } |
| 185835 | |
| 185836 | if( rc==SQLITE_OK ){ |
| 185837 | zTerm = sqlite3Fts5IterTerm(pCsr->pIter, &nTerm); |
| @@ -185843,11 +187135,11 @@ | |
| 185843 | } |
| 185844 | } |
| 185845 | } |
| 185846 | |
| 185847 | if( pCsr->bEof==0 && pTab->eType==FTS5_VOCAB_COL ){ |
| 185848 | while( pCsr->aCnt[pCsr->iCol]==0 ) pCsr->iCol++; |
| 185849 | assert( pCsr->iCol<pCsr->pConfig->nCol ); |
| 185850 | } |
| 185851 | return rc; |
| 185852 | } |
| 185853 | |
| @@ -185923,34 +187215,40 @@ | |
| 185923 | sqlite3_vtab_cursor *pCursor, /* Cursor to retrieve value from */ |
| 185924 | sqlite3_context *pCtx, /* Context for sqlite3_result_xxx() calls */ |
| 185925 | int iCol /* Index of column to read value from */ |
| 185926 | ){ |
| 185927 | Fts5VocabCursor *pCsr = (Fts5VocabCursor*)pCursor; |
| 185928 | |
| 185929 | if( iCol==0 ){ |
| 185930 | sqlite3_result_text( |
| 185931 | pCtx, (const char*)pCsr->term.p, pCsr->term.n, SQLITE_TRANSIENT |
| 185932 | ); |
| 185933 | } |
| 185934 | else if( ((Fts5VocabTable*)(pCursor->pVtab))->eType==FTS5_VOCAB_COL ){ |
| 185935 | assert( iCol==1 || iCol==2 || iCol==3 ); |
| 185936 | if( iCol==1 ){ |
| 185937 | const char *z = pCsr->pConfig->azCol[pCsr->iCol]; |
| 185938 | sqlite3_result_text(pCtx, z, -1, SQLITE_STATIC); |
| 185939 | }else if( iCol==2 ){ |
| 185940 | sqlite3_result_int64(pCtx, pCsr->aDoc[pCsr->iCol]); |
| 185941 | }else{ |
| 185942 | sqlite3_result_int64(pCtx, pCsr->aCnt[pCsr->iCol]); |
| 185943 | } |
| 185944 | }else{ |
| 185945 | assert( iCol==1 || iCol==2 ); |
| 185946 | if( iCol==1 ){ |
| 185947 | sqlite3_result_int64(pCtx, pCsr->aDoc[0]); |
| 185948 | }else{ |
| 185949 | sqlite3_result_int64(pCtx, pCsr->aCnt[0]); |
| 185950 | } |
| 185951 | } |
| 185952 | return SQLITE_OK; |
| 185953 | } |
| 185954 | |
| 185955 | /* |
| 185956 | ** This is the xRowid method. The SQLite core calls this routine to |
| 185957 |
| --- src/sqlite3.c | |
| +++ src/sqlite3.c | |
| @@ -1,8 +1,8 @@ | |
| 1 | /****************************************************************************** |
| 2 | ** This file is an amalgamation of many separate C source files from SQLite |
| 3 | ** version 3.11.0. By combining all the individual C code files into this |
| 4 | ** single large file, the entire code can be compiled as a single translation |
| 5 | ** unit. This allows many compilers to do optimizations that would not be |
| 6 | ** possible if the files were compiled separately. Performance improvements |
| 7 | ** of 5% or more are commonly seen when SQLite is compiled as a single |
| 8 | ** translation unit. |
| @@ -119,10 +119,12 @@ | |
| 119 | #define SQLITE_ENABLE_LOCKING_STYLE 0 |
| 120 | #define HAVE_UTIME 1 |
| 121 | #else |
| 122 | /* This is not VxWorks. */ |
| 123 | #define OS_VXWORKS 0 |
| 124 | #define HAVE_FCHOWN 1 |
| 125 | #define HAVE_READLINK 1 |
| 126 | #endif /* defined(_WRS_KERNEL) */ |
| 127 | |
| 128 | /************** End of vxworks.h *********************************************/ |
| 129 | /************** Continuing where we left off in sqliteInt.h ******************/ |
| 130 | |
| @@ -323,13 +325,13 @@ | |
| 325 | ** |
| 326 | ** See also: [sqlite3_libversion()], |
| 327 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 328 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 329 | */ |
| 330 | #define SQLITE_VERSION "3.11.0" |
| 331 | #define SQLITE_VERSION_NUMBER 3011000 |
| 332 | #define SQLITE_SOURCE_ID "2016-01-14 14:19:50 d17bc2c92f4d086280e49a3cc72993be7fee2da7" |
| 333 | |
| 334 | /* |
| 335 | ** CAPI3REF: Run-Time Library Version Numbers |
| 336 | ** KEYWORDS: sqlite3_version, sqlite3_sourceid |
| 337 | ** |
| @@ -1006,12 +1008,17 @@ | |
| 1008 | ** improve performance on some systems. |
| 1009 | ** |
| 1010 | ** <li>[[SQLITE_FCNTL_FILE_POINTER]] |
| 1011 | ** The [SQLITE_FCNTL_FILE_POINTER] opcode is used to obtain a pointer |
| 1012 | ** to the [sqlite3_file] object associated with a particular database |
| 1013 | ** connection. See also [SQLITE_FCNTL_JOURNAL_POINTER]. |
| 1014 | ** |
| 1015 | ** <li>[[SQLITE_FCNTL_JOURNAL_POINTER]] |
| 1016 | ** The [SQLITE_FCNTL_JOURNAL_POINTER] opcode is used to obtain a pointer |
| 1017 | ** to the [sqlite3_file] object associated with the journal file (either |
| 1018 | ** the [rollback journal] or the [write-ahead log]) for a particular database |
| 1019 | ** connection. See also [SQLITE_FCNTL_FILE_POINTER]. |
| 1020 | ** |
| 1021 | ** <li>[[SQLITE_FCNTL_SYNC_OMITTED]] |
| 1022 | ** No longer in use. |
| 1023 | ** |
| 1024 | ** <li>[[SQLITE_FCNTL_SYNC]] |
| @@ -1222,10 +1229,11 @@ | |
| 1229 | #define SQLITE_FCNTL_WIN32_SET_HANDLE 23 |
| 1230 | #define SQLITE_FCNTL_WAL_BLOCK 24 |
| 1231 | #define SQLITE_FCNTL_ZIPVFS 25 |
| 1232 | #define SQLITE_FCNTL_RBU 26 |
| 1233 | #define SQLITE_FCNTL_VFS_POINTER 27 |
| 1234 | #define SQLITE_FCNTL_JOURNAL_POINTER 28 |
| 1235 | |
| 1236 | /* deprecated names */ |
| 1237 | #define SQLITE_GET_LOCKPROXYFILE SQLITE_FCNTL_GET_LOCKPROXYFILE |
| 1238 | #define SQLITE_SET_LOCKPROXYFILE SQLITE_FCNTL_SET_LOCKPROXYFILE |
| 1239 | #define SQLITE_LAST_ERRNO SQLITE_FCNTL_LAST_ERRNO |
| @@ -8399,10 +8407,13 @@ | |
| 8407 | ** If parameter iCol is greater than or equal to the number of columns |
| 8408 | ** in the table, SQLITE_RANGE is returned. Or, if an error occurs (e.g. |
| 8409 | ** an OOM condition or IO error), an appropriate SQLite error code is |
| 8410 | ** returned. |
| 8411 | ** |
| 8412 | ** This function may be quite inefficient if used with an FTS5 table |
| 8413 | ** created with the "columnsize=0" option. |
| 8414 | ** |
| 8415 | ** xColumnText: |
| 8416 | ** This function attempts to retrieve the text of column iCol of the |
| 8417 | ** current document. If successful, (*pz) is set to point to a buffer |
| 8418 | ** containing the text in utf-8 encoding, (*pn) is set to the size in bytes |
| 8419 | ** (not characters) of the buffer and SQLITE_OK is returned. Otherwise, |
| @@ -8419,18 +8430,32 @@ | |
| 8430 | ** xInstCount: |
| 8431 | ** Set *pnInst to the total number of occurrences of all phrases within |
| 8432 | ** the query within the current row. Return SQLITE_OK if successful, or |
| 8433 | ** an error code (i.e. SQLITE_NOMEM) if an error occurs. |
| 8434 | ** |
| 8435 | ** This API can be quite slow if used with an FTS5 table created with the |
| 8436 | ** "detail=none" or "detail=column" option. If the FTS5 table is created |
| 8437 | ** with either "detail=none" or "detail=column" and "content=" option |
| 8438 | ** (i.e. if it is a contentless table), then this API always returns 0. |
| 8439 | ** |
| 8440 | ** xInst: |
| 8441 | ** Query for the details of phrase match iIdx within the current row. |
| 8442 | ** Phrase matches are numbered starting from zero, so the iIdx argument |
| 8443 | ** should be greater than or equal to zero and smaller than the value |
| 8444 | ** output by xInstCount(). |
| 8445 | ** |
| 8446 | ** Usually, output parameter *piPhrase is set to the phrase number, *piCol |
| 8447 | ** to the column in which it occurs and *piOff the token offset of the |
| 8448 | ** first token of the phrase. The exception is if the table was created |
| 8449 | ** with the offsets=0 option specified. In this case *piOff is always |
| 8450 | ** set to -1. |
| 8451 | ** |
| 8452 | ** Returns SQLITE_OK if successful, or an error code (i.e. SQLITE_NOMEM) |
| 8453 | ** if an error occurs. |
| 8454 | ** |
| 8455 | ** This API can be quite slow if used with an FTS5 table created with the |
| 8456 | ** "detail=none" or "detail=column" option. |
| 8457 | ** |
| 8458 | ** xRowid: |
| 8459 | ** Returns the rowid of the current row. |
| 8460 | ** |
| 8461 | ** xTokenize: |
| @@ -8511,25 +8536,63 @@ | |
| 8536 | ** through instances of phrase iPhrase, use the following code: |
| 8537 | ** |
| 8538 | ** Fts5PhraseIter iter; |
| 8539 | ** int iCol, iOff; |
| 8540 | ** for(pApi->xPhraseFirst(pFts, iPhrase, &iter, &iCol, &iOff); |
| 8541 | ** iCol>=0; |
| 8542 | ** pApi->xPhraseNext(pFts, &iter, &iCol, &iOff) |
| 8543 | ** ){ |
| 8544 | ** // An instance of phrase iPhrase at offset iOff of column iCol |
| 8545 | ** } |
| 8546 | ** |
| 8547 | ** The Fts5PhraseIter structure is defined above. Applications should not |
| 8548 | ** modify this structure directly - it should only be used as shown above |
| 8549 | ** with the xPhraseFirst() and xPhraseNext() API methods (and by |
| 8550 | ** xPhraseFirstColumn() and xPhraseNextColumn() as illustrated below). |
| 8551 | ** |
| 8552 | ** This API can be quite slow if used with an FTS5 table created with the |
| 8553 | ** "detail=none" or "detail=column" option. If the FTS5 table is created |
| 8554 | ** with either "detail=none" or "detail=column" and "content=" option |
| 8555 | ** (i.e. if it is a contentless table), then this API always iterates |
| 8556 | ** through an empty set (all calls to xPhraseFirst() set iCol to -1). |
| 8557 | ** |
| 8558 | ** xPhraseNext() |
| 8559 | ** See xPhraseFirst above. |
| 8560 | ** |
| 8561 | ** xPhraseFirstColumn() |
| 8562 | ** This function and xPhraseNextColumn() are similar to the xPhraseFirst() |
| 8563 | ** and xPhraseNext() APIs described above. The difference is that instead |
| 8564 | ** of iterating through all instances of a phrase in the current row, these |
| 8565 | ** APIs are used to iterate through the set of columns in the current row |
| 8566 | ** that contain one or more instances of a specified phrase. For example: |
| 8567 | ** |
| 8568 | ** Fts5PhraseIter iter; |
| 8569 | ** int iCol; |
| 8570 | ** for(pApi->xPhraseFirstColumn(pFts, iPhrase, &iter, &iCol); |
| 8571 | ** iCol>=0; |
| 8572 | ** pApi->xPhraseNextColumn(pFts, &iter, &iCol) |
| 8573 | ** ){ |
| 8574 | ** // Column iCol contains at least one instance of phrase iPhrase |
| 8575 | ** } |
| 8576 | ** |
| 8577 | ** This API can be quite slow if used with an FTS5 table created with the |
| 8578 | ** "detail=none" option. If the FTS5 table is created with either |
| 8579 | ** "detail=none" "content=" option (i.e. if it is a contentless table), |
| 8580 | ** then this API always iterates through an empty set (all calls to |
| 8581 | ** xPhraseFirstColumn() set iCol to -1). |
| 8582 | ** |
| 8583 | ** The information accessed using this API and its companion |
| 8584 | ** xPhraseFirstColumn() may also be obtained using xPhraseFirst/xPhraseNext |
| 8585 | ** (or xInst/xInstCount). The chief advantage of this API is that it is |
| 8586 | ** significantly more efficient than those alternatives when used with |
| 8587 | ** "detail=column" tables. |
| 8588 | ** |
| 8589 | ** xPhraseNextColumn() |
| 8590 | ** See xPhraseFirstColumn above. |
| 8591 | */ |
| 8592 | struct Fts5ExtensionApi { |
| 8593 | int iVersion; /* Currently always set to 3 */ |
| 8594 | |
| 8595 | void *(*xUserData)(Fts5Context*); |
| 8596 | |
| 8597 | int (*xColumnCount)(Fts5Context*); |
| 8598 | int (*xRowCount)(Fts5Context*, sqlite3_int64 *pnRow); |
| @@ -8555,12 +8618,15 @@ | |
| 8618 | int(*)(const Fts5ExtensionApi*,Fts5Context*,void*) |
| 8619 | ); |
| 8620 | int (*xSetAuxdata)(Fts5Context*, void *pAux, void(*xDelete)(void*)); |
| 8621 | void *(*xGetAuxdata)(Fts5Context*, int bClear); |
| 8622 | |
| 8623 | int (*xPhraseFirst)(Fts5Context*, int iPhrase, Fts5PhraseIter*, int*, int*); |
| 8624 | void (*xPhraseNext)(Fts5Context*, Fts5PhraseIter*, int *piCol, int *piOff); |
| 8625 | |
| 8626 | int (*xPhraseFirstColumn)(Fts5Context*, int iPhrase, Fts5PhraseIter*, int*); |
| 8627 | void (*xPhraseNextColumn)(Fts5Context*, Fts5PhraseIter*, int *piCol); |
| 8628 | }; |
| 8629 | |
| 8630 | /* |
| 8631 | ** CUSTOM AUXILIARY FUNCTIONS |
| 8632 | *************************************************************************/ |
| @@ -9976,14 +10042,10 @@ | |
| 10042 | /* |
| 10043 | ** Default maximum size of memory used by memory-mapped I/O in the VFS |
| 10044 | */ |
| 10045 | #ifdef __APPLE__ |
| 10046 | # include <TargetConditionals.h> |
| 10047 | #endif |
| 10048 | #ifndef SQLITE_MAX_MMAP_SIZE |
| 10049 | # if defined(__linux__) \ |
| 10050 | || defined(_WIN32) \ |
| 10051 | || (defined(__APPLE__) && defined(__MACH__)) \ |
| @@ -10479,19 +10541,21 @@ | |
| 10541 | ** Enter and Leave procedures no-ops. |
| 10542 | */ |
| 10543 | #ifndef SQLITE_OMIT_SHARED_CACHE |
| 10544 | SQLITE_PRIVATE void sqlite3BtreeEnter(Btree*); |
| 10545 | SQLITE_PRIVATE void sqlite3BtreeEnterAll(sqlite3*); |
| 10546 | SQLITE_PRIVATE int sqlite3BtreeSharable(Btree*); |
| 10547 | SQLITE_PRIVATE void sqlite3BtreeEnterCursor(BtCursor*); |
| 10548 | #else |
| 10549 | # define sqlite3BtreeEnter(X) |
| 10550 | # define sqlite3BtreeEnterAll(X) |
| 10551 | # define sqlite3BtreeSharable(X) 0 |
| 10552 | # define sqlite3BtreeEnterCursor(X) |
| 10553 | #endif |
| 10554 | |
| 10555 | #if !defined(SQLITE_OMIT_SHARED_CACHE) && SQLITE_THREADSAFE |
| 10556 | SQLITE_PRIVATE void sqlite3BtreeLeave(Btree*); |
| 10557 | SQLITE_PRIVATE void sqlite3BtreeLeaveCursor(BtCursor*); |
| 10558 | SQLITE_PRIVATE void sqlite3BtreeLeaveAll(sqlite3*); |
| 10559 | #ifndef NDEBUG |
| 10560 | /* These routines are used inside assert() statements only. */ |
| 10561 | SQLITE_PRIVATE int sqlite3BtreeHoldsMutex(Btree*); |
| @@ -10498,13 +10562,11 @@ | |
| 10562 | SQLITE_PRIVATE int sqlite3BtreeHoldsAllMutexes(sqlite3*); |
| 10563 | SQLITE_PRIVATE int sqlite3SchemaMutexHeld(sqlite3*,int,Schema*); |
| 10564 | #endif |
| 10565 | #else |
| 10566 | |
| 10567 | # define sqlite3BtreeLeave(X) |
| 10568 | # define sqlite3BtreeLeaveCursor(X) |
| 10569 | # define sqlite3BtreeLeaveAll(X) |
| 10570 | |
| 10571 | # define sqlite3BtreeHoldsMutex(X) 1 |
| 10572 | # define sqlite3BtreeHoldsAllMutexes(X) 1 |
| @@ -11215,10 +11277,11 @@ | |
| 11277 | #endif |
| 11278 | SQLITE_PRIVATE int sqlite3PagerMemUsed(Pager*); |
| 11279 | SQLITE_PRIVATE const char *sqlite3PagerFilename(Pager*, int); |
| 11280 | SQLITE_PRIVATE sqlite3_vfs *sqlite3PagerVfs(Pager*); |
| 11281 | SQLITE_PRIVATE sqlite3_file *sqlite3PagerFile(Pager*); |
| 11282 | SQLITE_PRIVATE sqlite3_file *sqlite3PagerJrnlFile(Pager*); |
| 11283 | SQLITE_PRIVATE const char *sqlite3PagerJournalname(Pager*); |
| 11284 | SQLITE_PRIVATE int sqlite3PagerNosync(Pager*); |
| 11285 | SQLITE_PRIVATE void *sqlite3PagerTempSpace(Pager*); |
| 11286 | SQLITE_PRIVATE int sqlite3PagerIsMemdb(Pager*); |
| 11287 | SQLITE_PRIVATE void sqlite3PagerCacheStat(Pager *, int, int, int *); |
| @@ -11309,10 +11372,12 @@ | |
| 11372 | #define PGHDR_NEED_SYNC 0x008 /* Fsync the rollback journal before |
| 11373 | ** writing this page to the database */ |
| 11374 | #define PGHDR_NEED_READ 0x010 /* Content is unread */ |
| 11375 | #define PGHDR_DONT_WRITE 0x020 /* Do not write content to disk */ |
| 11376 | #define PGHDR_MMAP 0x040 /* This is an mmap page object */ |
| 11377 | |
| 11378 | #define PGHDR_WAL_APPEND 0x080 /* Appended to wal file */ |
| 11379 | |
| 11380 | /* Initialize and shutdown the page cache subsystem */ |
| 11381 | SQLITE_PRIVATE int sqlite3PcacheInitialize(void); |
| 11382 | SQLITE_PRIVATE void sqlite3PcacheShutdown(void); |
| 11383 | |
| @@ -14135,11 +14200,10 @@ | |
| 14200 | SQLITE_PRIVATE int sqlite3InitCallback(void*, int, char**, char**); |
| 14201 | SQLITE_PRIVATE void sqlite3Pragma(Parse*,Token*,Token*,Token*,int); |
| 14202 | SQLITE_PRIVATE void sqlite3ResetAllSchemasOfConnection(sqlite3*); |
| 14203 | SQLITE_PRIVATE void sqlite3ResetOneSchema(sqlite3*,int); |
| 14204 | SQLITE_PRIVATE void sqlite3CollapseDatabaseArray(sqlite3*); |
| 14205 | SQLITE_PRIVATE void sqlite3CommitInternalChanges(sqlite3*); |
| 14206 | SQLITE_PRIVATE void sqlite3DeleteColumnNames(sqlite3*,Table*); |
| 14207 | SQLITE_PRIVATE int sqlite3ColumnsFromExprList(Parse*,ExprList*,i16*,Column**); |
| 14208 | SQLITE_PRIVATE Table *sqlite3ResultSetOfSelect(Parse*,Select*); |
| 14209 | SQLITE_PRIVATE void sqlite3OpenMasterTable(Parse *, int); |
| @@ -16066,15 +16130,19 @@ | |
| 16130 | SQLITE_PRIVATE int sqlite3VdbeSorterNext(sqlite3 *, const VdbeCursor *, int *); |
| 16131 | SQLITE_PRIVATE int sqlite3VdbeSorterRewind(const VdbeCursor *, int *); |
| 16132 | SQLITE_PRIVATE int sqlite3VdbeSorterWrite(const VdbeCursor *, Mem *); |
| 16133 | SQLITE_PRIVATE int sqlite3VdbeSorterCompare(const VdbeCursor *, Mem *, int, int *); |
| 16134 | |
| 16135 | #if !defined(SQLITE_OMIT_SHARED_CACHE) |
| 16136 | SQLITE_PRIVATE void sqlite3VdbeEnter(Vdbe*); |
| 16137 | #else |
| 16138 | # define sqlite3VdbeEnter(X) |
| 16139 | #endif |
| 16140 | |
| 16141 | #if !defined(SQLITE_OMIT_SHARED_CACHE) && SQLITE_THREADSAFE>0 |
| 16142 | SQLITE_PRIVATE void sqlite3VdbeLeave(Vdbe*); |
| 16143 | #else |
| 16144 | # define sqlite3VdbeLeave(X) |
| 16145 | #endif |
| 16146 | |
| 16147 | #ifdef SQLITE_DEBUG |
| 16148 | SQLITE_PRIVATE void sqlite3VdbeMemAboutToChange(Vdbe*,Mem*); |
| @@ -19758,10 +19826,11 @@ | |
| 19826 | /* |
| 19827 | ** Mutex to control access to the memory allocation subsystem. |
| 19828 | */ |
| 19829 | sqlite3_mutex *mutex; |
| 19830 | |
| 19831 | #if defined(SQLITE_DEBUG) || defined(SQLITE_TEST) |
| 19832 | /* |
| 19833 | ** Performance statistics |
| 19834 | */ |
| 19835 | u64 nAlloc; /* Total number of calls to malloc */ |
| 19836 | u64 totalAlloc; /* Total of all malloc calls - includes internal frag */ |
| @@ -19769,10 +19838,11 @@ | |
| 19838 | u32 currentOut; /* Current checkout, including internal fragmentation */ |
| 19839 | u32 currentCount; /* Current number of distinct checkouts */ |
| 19840 | u32 maxOut; /* Maximum instantaneous currentOut */ |
| 19841 | u32 maxCount; /* Maximum instantaneous currentCount */ |
| 19842 | u32 maxRequest; /* Largest allocation (exclusive of internal frag) */ |
| 19843 | #endif |
| 19844 | |
| 19845 | /* |
| 19846 | ** Lists of free blocks. aiFreelist[0] is a list of free blocks of |
| 19847 | ** size mem5.szAtom. aiFreelist[1] holds blocks of size szAtom*2. |
| 19848 | ** aiFreelist[2] holds free blocks of size szAtom*4. And so forth. |
| @@ -19880,18 +19950,21 @@ | |
| 19950 | int iLogsize; /* Log2 of iFullSz/POW2_MIN */ |
| 19951 | |
| 19952 | /* nByte must be a positive */ |
| 19953 | assert( nByte>0 ); |
| 19954 | |
| 19955 | /* No more than 1GiB per allocation */ |
| 19956 | if( nByte > 0x40000000 ) return 0; |
| 19957 | |
| 19958 | #if defined(SQLITE_DEBUG) || defined(SQLITE_TEST) |
| 19959 | /* Keep track of the maximum allocation request. Even unfulfilled |
| 19960 | ** requests are counted */ |
| 19961 | if( (u32)nByte>mem5.maxRequest ){ |
| 19962 | mem5.maxRequest = nByte; |
| 19963 | } |
| 19964 | #endif |
| 19965 | |
| 19966 | |
| 19967 | /* Round nByte up to the next valid power of two */ |
| 19968 | for(iFullSz=mem5.szAtom,iLogsize=0; iFullSz<nByte; iFullSz*=2,iLogsize++){} |
| 19969 | |
| 19970 | /* Make sure mem5.aiFreelist[iLogsize] contains at least one free |
| @@ -19914,18 +19987,20 @@ | |
| 19987 | mem5.aCtrl[i+newSize] = CTRL_FREE | iBin; |
| 19988 | memsys5Link(i+newSize, iBin); |
| 19989 | } |
| 19990 | mem5.aCtrl[i] = iLogsize; |
| 19991 | |
| 19992 | #if defined(SQLITE_DEBUG) || defined(SQLITE_TEST) |
| 19993 | /* Update allocator performance statistics. */ |
| 19994 | mem5.nAlloc++; |
| 19995 | mem5.totalAlloc += iFullSz; |
| 19996 | mem5.totalExcess += iFullSz - nByte; |
| 19997 | mem5.currentCount++; |
| 19998 | mem5.currentOut += iFullSz; |
| 19999 | if( mem5.maxCount<mem5.currentCount ) mem5.maxCount = mem5.currentCount; |
| 20000 | if( mem5.maxOut<mem5.currentOut ) mem5.maxOut = mem5.currentOut; |
| 20001 | #endif |
| 20002 | |
| 20003 | #ifdef SQLITE_DEBUG |
| 20004 | /* Make sure the allocated memory does not assume that it is set to zero |
| 20005 | ** or retains a value from a previous allocation */ |
| 20006 | memset(&mem5.zPool[i*mem5.szAtom], 0xAA, iFullSz); |
| @@ -19956,16 +20031,19 @@ | |
| 20031 | size = 1<<iLogsize; |
| 20032 | assert( iBlock+size-1<(u32)mem5.nBlock ); |
| 20033 | |
| 20034 | mem5.aCtrl[iBlock] |= CTRL_FREE; |
| 20035 | mem5.aCtrl[iBlock+size-1] |= CTRL_FREE; |
| 20036 | |
| 20037 | #if defined(SQLITE_DEBUG) || defined(SQLITE_TEST) |
| 20038 | assert( mem5.currentCount>0 ); |
| 20039 | assert( mem5.currentOut>=(size*mem5.szAtom) ); |
| 20040 | mem5.currentCount--; |
| 20041 | mem5.currentOut -= size*mem5.szAtom; |
| 20042 | assert( mem5.currentOut>0 || mem5.currentCount==0 ); |
| 20043 | assert( mem5.currentCount>0 || mem5.currentOut==0 ); |
| 20044 | #endif |
| 20045 | |
| 20046 | mem5.aCtrl[iBlock] = CTRL_FREE | iLogsize; |
| 20047 | while( ALWAYS(iLogsize<LOGMAX) ){ |
| 20048 | int iBuddy; |
| 20049 | if( (iBlock>>iLogsize) & 1 ){ |
| @@ -27479,37 +27557,55 @@ | |
| 27557 | #define osMkdir ((int(*)(const char*,mode_t))aSyscall[18].pCurrent) |
| 27558 | |
| 27559 | { "rmdir", (sqlite3_syscall_ptr)rmdir, 0 }, |
| 27560 | #define osRmdir ((int(*)(const char*))aSyscall[19].pCurrent) |
| 27561 | |
| 27562 | #if defined(HAVE_FCHOWN) |
| 27563 | { "fchown", (sqlite3_syscall_ptr)fchown, 0 }, |
| 27564 | #else |
| 27565 | { "fchown", (sqlite3_syscall_ptr)0, 0 }, |
| 27566 | #endif |
| 27567 | #define osFchown ((int(*)(int,uid_t,gid_t))aSyscall[20].pCurrent) |
| 27568 | |
| 27569 | { "geteuid", (sqlite3_syscall_ptr)geteuid, 0 }, |
| 27570 | #define osGeteuid ((uid_t(*)(void))aSyscall[21].pCurrent) |
| 27571 | |
| 27572 | #if !defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0 |
| 27573 | { "mmap", (sqlite3_syscall_ptr)mmap, 0 }, |
| 27574 | #else |
| 27575 | { "mmap", (sqlite3_syscall_ptr)0, 0 }, |
| 27576 | #endif |
| 27577 | #define osMmap ((void*(*)(void*,size_t,int,int,int,off_t))aSyscall[22].pCurrent) |
| 27578 | |
| 27579 | #if !defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0 |
| 27580 | { "munmap", (sqlite3_syscall_ptr)munmap, 0 }, |
| 27581 | #else |
| 27582 | { "munmap", (sqlite3_syscall_ptr)0, 0 }, |
| 27583 | #endif |
| 27584 | #define osMunmap ((void*(*)(void*,size_t))aSyscall[23].pCurrent) |
| 27585 | |
| 27586 | #if HAVE_MREMAP && (!defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0) |
| 27587 | { "mremap", (sqlite3_syscall_ptr)mremap, 0 }, |
| 27588 | #else |
| 27589 | { "mremap", (sqlite3_syscall_ptr)0, 0 }, |
| 27590 | #endif |
| 27591 | #define osMremap ((void*(*)(void*,size_t,size_t,int,...))aSyscall[24].pCurrent) |
| 27592 | |
| 27593 | #if !defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0 |
| 27594 | { "getpagesize", (sqlite3_syscall_ptr)unixGetpagesize, 0 }, |
| 27595 | #else |
| 27596 | { "getpagesize", (sqlite3_syscall_ptr)0, 0 }, |
| 27597 | #endif |
| 27598 | #define osGetpagesize ((int(*)(void))aSyscall[25].pCurrent) |
| 27599 | |
| 27600 | #if defined(HAVE_READLINK) |
| 27601 | { "readlink", (sqlite3_syscall_ptr)readlink, 0 }, |
| 27602 | #else |
| 27603 | { "readlink", (sqlite3_syscall_ptr)0, 0 }, |
| 27604 | #endif |
| 27605 | #define osReadlink ((ssize_t(*)(const char*,char*,size_t))aSyscall[26].pCurrent) |
| 27606 | |
| 27607 | |
| 27608 | }; /* End of the overrideable system calls */ |
| 27609 | |
| 27610 | |
| 27611 | /* |
| @@ -27516,14 +27612,14 @@ | |
| 27612 | ** On some systems, calls to fchown() will trigger a message in a security |
| 27613 | ** log if they come from non-root processes. So avoid calling fchown() if |
| 27614 | ** we are not running as root. |
| 27615 | */ |
| 27616 | static int robustFchown(int fd, uid_t uid, gid_t gid){ |
| 27617 | #if defined(HAVE_FCHOWN) |
| 27618 | return osGeteuid() ? 0 : osFchown(fd,uid,gid); |
| 27619 | #else |
| 27620 | return 0; |
| 27621 | #endif |
| 27622 | } |
| 27623 | |
| 27624 | /* |
| 27625 | ** This is the xSetSystemCall() method of sqlite3_vfs for all of the |
| @@ -32986,10 +33082,11 @@ | |
| 33082 | SimulateIOError( return SQLITE_ERROR ); |
| 33083 | |
| 33084 | assert( pVfs->mxPathname==MAX_PATHNAME ); |
| 33085 | UNUSED_PARAMETER(pVfs); |
| 33086 | |
| 33087 | #if defined(HAVE_READLINK) |
| 33088 | /* Attempt to resolve the path as if it were a symbolic link. If it is |
| 33089 | ** a symbolic link, the resolved path is stored in buffer zOut[]. Or, if |
| 33090 | ** the identified file is not a symbolic link or does not exist, then |
| 33091 | ** zPath is copied directly into zOut. Either way, nByte is left set to |
| 33092 | ** the size of the string copied into zOut[] in bytes. */ |
| @@ -33001,10 +33098,11 @@ | |
| 33098 | sqlite3_snprintf(nOut, zOut, "%s", zPath); |
| 33099 | nByte = sqlite3Strlen30(zOut); |
| 33100 | }else{ |
| 33101 | zOut[nByte] = '\0'; |
| 33102 | } |
| 33103 | #endif |
| 33104 | |
| 33105 | /* If buffer zOut[] now contains an absolute path there is nothing more |
| 33106 | ** to do. If it contains a relative path, do the following: |
| 33107 | ** |
| 33108 | ** * move the relative path string so that it is at the end of th |
| @@ -43327,10 +43425,11 @@ | |
| 43425 | # define sqlite3WalCallback(z) 0 |
| 43426 | # define sqlite3WalExclusiveMode(y,z) 0 |
| 43427 | # define sqlite3WalHeapMemory(z) 0 |
| 43428 | # define sqlite3WalFramesize(z) 0 |
| 43429 | # define sqlite3WalFindFrame(x,y,z) 0 |
| 43430 | # define sqlite3WalFile(x) 0 |
| 43431 | #else |
| 43432 | |
| 43433 | #define WAL_SAVEPOINT_NDATA 4 |
| 43434 | |
| 43435 | /* Connection to a write-ahead log (WAL) file. |
| @@ -43421,10 +43520,13 @@ | |
| 43520 | ** stored in each frame (i.e. the db page-size when the WAL was created). |
| 43521 | */ |
| 43522 | SQLITE_PRIVATE int sqlite3WalFramesize(Wal *pWal); |
| 43523 | #endif |
| 43524 | |
| 43525 | /* Return the sqlite3_file object for the WAL file */ |
| 43526 | SQLITE_PRIVATE sqlite3_file *sqlite3WalFile(Wal *pWal); |
| 43527 | |
| 43528 | #endif /* ifndef SQLITE_OMIT_WAL */ |
| 43529 | #endif /* _WAL_H_ */ |
| 43530 | |
| 43531 | /************** End of wal.h *************************************************/ |
| 43532 | /************** Continuing where we left off in pager.c **********************/ |
| @@ -49032,11 +49134,11 @@ | |
| 49134 | if( pPager->exclusiveMode && sqlite3WalExclusiveMode(pPager->pWal, -1) ){ |
| 49135 | rc = pagerLockDb(pPager, EXCLUSIVE_LOCK); |
| 49136 | if( rc!=SQLITE_OK ){ |
| 49137 | return rc; |
| 49138 | } |
| 49139 | (void)sqlite3WalExclusiveMode(pPager->pWal, 1); |
| 49140 | } |
| 49141 | |
| 49142 | /* Grab the write lock on the log file. If successful, upgrade to |
| 49143 | ** PAGER_RESERVED state. Otherwise, return an error code to the caller. |
| 49144 | ** The busy-handler is not invoked if another connection already |
| @@ -50096,10 +50198,22 @@ | |
| 50198 | ** not yet been opened. |
| 50199 | */ |
| 50200 | SQLITE_PRIVATE sqlite3_file *sqlite3PagerFile(Pager *pPager){ |
| 50201 | return pPager->fd; |
| 50202 | } |
| 50203 | |
| 50204 | /* |
| 50205 | ** Return the file handle for the journal file (if it exists). |
| 50206 | ** This will be either the rollback journal or the WAL file. |
| 50207 | */ |
| 50208 | SQLITE_PRIVATE sqlite3_file *sqlite3PagerJrnlFile(Pager *pPager){ |
| 50209 | #if SQLITE_OMIT_WAL |
| 50210 | return pPager->jfd; |
| 50211 | #else |
| 50212 | return pPager->pWal ? sqlite3WalFile(pPager->pWal) : pPager->jfd; |
| 50213 | #endif |
| 50214 | } |
| 50215 | |
| 50216 | /* |
| 50217 | ** Return the full pathname of the journal file. |
| 50218 | */ |
| 50219 | SQLITE_PRIVATE const char *sqlite3PagerJournalname(Pager *pPager){ |
| @@ -51202,10 +51316,11 @@ | |
| 51316 | u8 truncateOnCommit; /* True to truncate WAL file on commit */ |
| 51317 | u8 syncHeader; /* Fsync the WAL header if true */ |
| 51318 | u8 padToSectorBoundary; /* Pad transactions out to the next sector */ |
| 51319 | WalIndexHdr hdr; /* Wal-index header for current transaction */ |
| 51320 | u32 minFrame; /* Ignore wal frames before this one */ |
| 51321 | u32 iReCksum; /* On commit, recalculate checksums from here */ |
| 51322 | const char *zWalName; /* Name of WAL file */ |
| 51323 | u32 nCkpt; /* Checkpoint sequence counter in the wal-header */ |
| 51324 | #ifdef SQLITE_DEBUG |
| 51325 | u8 lockError; /* True if a locking error has occurred */ |
| 51326 | #endif |
| @@ -51455,18 +51570,22 @@ | |
| 51570 | int nativeCksum; /* True for native byte-order checksums */ |
| 51571 | u32 *aCksum = pWal->hdr.aFrameCksum; |
| 51572 | assert( WAL_FRAME_HDRSIZE==24 ); |
| 51573 | sqlite3Put4byte(&aFrame[0], iPage); |
| 51574 | sqlite3Put4byte(&aFrame[4], nTruncate); |
| 51575 | if( pWal->iReCksum==0 ){ |
| 51576 | memcpy(&aFrame[8], pWal->hdr.aSalt, 8); |
| 51577 | |
| 51578 | nativeCksum = (pWal->hdr.bigEndCksum==SQLITE_BIGENDIAN); |
| 51579 | walChecksumBytes(nativeCksum, aFrame, 8, aCksum, aCksum); |
| 51580 | walChecksumBytes(nativeCksum, aData, pWal->szPage, aCksum, aCksum); |
| 51581 | |
| 51582 | sqlite3Put4byte(&aFrame[16], aCksum[0]); |
| 51583 | sqlite3Put4byte(&aFrame[20], aCksum[1]); |
| 51584 | }else{ |
| 51585 | memset(&aFrame[8], 0, 16); |
| 51586 | } |
| 51587 | } |
| 51588 | |
| 51589 | /* |
| 51590 | ** Check to see if the frame with header in aFrame[] and content |
| 51591 | ** in aData[] is valid. If it is a valid frame, fill *piPage and |
| @@ -53389,10 +53508,11 @@ | |
| 53508 | int rc; |
| 53509 | |
| 53510 | /* Cannot start a write transaction without first holding a read |
| 53511 | ** transaction. */ |
| 53512 | assert( pWal->readLock>=0 ); |
| 53513 | assert( pWal->writeLock==0 && pWal->iReCksum==0 ); |
| 53514 | |
| 53515 | if( pWal->readOnly ){ |
| 53516 | return SQLITE_READONLY; |
| 53517 | } |
| 53518 | |
| @@ -53424,10 +53544,11 @@ | |
| 53544 | */ |
| 53545 | SQLITE_PRIVATE int sqlite3WalEndWriteTransaction(Wal *pWal){ |
| 53546 | if( pWal->writeLock ){ |
| 53547 | walUnlockExclusive(pWal, WAL_WRITE_LOCK, 1); |
| 53548 | pWal->writeLock = 0; |
| 53549 | pWal->iReCksum = 0; |
| 53550 | pWal->truncateOnCommit = 0; |
| 53551 | } |
| 53552 | return SQLITE_OK; |
| 53553 | } |
| 53554 | |
| @@ -53641,10 +53762,63 @@ | |
| 53762 | if( rc ) return rc; |
| 53763 | /* Write the page data */ |
| 53764 | rc = walWriteToLog(p, pData, p->szPage, iOffset+sizeof(aFrame)); |
| 53765 | return rc; |
| 53766 | } |
| 53767 | |
| 53768 | /* |
| 53769 | ** This function is called as part of committing a transaction within which |
| 53770 | ** one or more frames have been overwritten. It updates the checksums for |
| 53771 | ** all frames written to the wal file by the current transaction starting |
| 53772 | ** with the earliest to have been overwritten. |
| 53773 | ** |
| 53774 | ** SQLITE_OK is returned if successful, or an SQLite error code otherwise. |
| 53775 | */ |
| 53776 | static int walRewriteChecksums(Wal *pWal, u32 iLast){ |
| 53777 | const int szPage = pWal->szPage;/* Database page size */ |
| 53778 | int rc = SQLITE_OK; /* Return code */ |
| 53779 | u8 *aBuf; /* Buffer to load data from wal file into */ |
| 53780 | u8 aFrame[WAL_FRAME_HDRSIZE]; /* Buffer to assemble frame-headers in */ |
| 53781 | u32 iRead; /* Next frame to read from wal file */ |
| 53782 | i64 iCksumOff; |
| 53783 | |
| 53784 | aBuf = sqlite3_malloc(szPage + WAL_FRAME_HDRSIZE); |
| 53785 | if( aBuf==0 ) return SQLITE_NOMEM; |
| 53786 | |
| 53787 | /* Find the checksum values to use as input for the recalculating the |
| 53788 | ** first checksum. If the first frame is frame 1 (implying that the current |
| 53789 | ** transaction restarted the wal file), these values must be read from the |
| 53790 | ** wal-file header. Otherwise, read them from the frame header of the |
| 53791 | ** previous frame. */ |
| 53792 | assert( pWal->iReCksum>0 ); |
| 53793 | if( pWal->iReCksum==1 ){ |
| 53794 | iCksumOff = 24; |
| 53795 | }else{ |
| 53796 | iCksumOff = walFrameOffset(pWal->iReCksum-1, szPage) + 16; |
| 53797 | } |
| 53798 | rc = sqlite3OsRead(pWal->pWalFd, aBuf, sizeof(u32)*2, iCksumOff); |
| 53799 | pWal->hdr.aFrameCksum[0] = sqlite3Get4byte(aBuf); |
| 53800 | pWal->hdr.aFrameCksum[1] = sqlite3Get4byte(&aBuf[sizeof(u32)]); |
| 53801 | |
| 53802 | iRead = pWal->iReCksum; |
| 53803 | pWal->iReCksum = 0; |
| 53804 | for(; rc==SQLITE_OK && iRead<=iLast; iRead++){ |
| 53805 | i64 iOff = walFrameOffset(iRead, szPage); |
| 53806 | rc = sqlite3OsRead(pWal->pWalFd, aBuf, szPage+WAL_FRAME_HDRSIZE, iOff); |
| 53807 | if( rc==SQLITE_OK ){ |
| 53808 | u32 iPgno, nDbSize; |
| 53809 | iPgno = sqlite3Get4byte(aBuf); |
| 53810 | nDbSize = sqlite3Get4byte(&aBuf[4]); |
| 53811 | |
| 53812 | walEncodeFrame(pWal, iPgno, nDbSize, &aBuf[WAL_FRAME_HDRSIZE], aFrame); |
| 53813 | rc = sqlite3OsWrite(pWal->pWalFd, aFrame, sizeof(aFrame), iOff); |
| 53814 | } |
| 53815 | } |
| 53816 | |
| 53817 | sqlite3_free(aBuf); |
| 53818 | return rc; |
| 53819 | } |
| 53820 | |
| 53821 | /* |
| 53822 | ** Write a set of frames to the log. The caller must hold the write-lock |
| 53823 | ** on the log file (obtained using sqlite3WalBeginWriteTransaction()). |
| 53824 | */ |
| @@ -53662,10 +53836,12 @@ | |
| 53836 | PgHdr *pLast = 0; /* Last frame in list */ |
| 53837 | int nExtra = 0; /* Number of extra copies of last page */ |
| 53838 | int szFrame; /* The size of a single frame */ |
| 53839 | i64 iOffset; /* Next byte to write in WAL file */ |
| 53840 | WalWriter w; /* The writer */ |
| 53841 | u32 iFirst = 0; /* First frame that may be overwritten */ |
| 53842 | WalIndexHdr *pLive; /* Pointer to shared header */ |
| 53843 | |
| 53844 | assert( pList ); |
| 53845 | assert( pWal->writeLock ); |
| 53846 | |
| 53847 | /* If this frame set completes a transaction, then nTruncate>0. If |
| @@ -53676,10 +53852,15 @@ | |
| 53852 | { int cnt; for(cnt=0, p=pList; p; p=p->pDirty, cnt++){} |
| 53853 | WALTRACE(("WAL%p: frame write begin. %d frames. mxFrame=%d. %s\n", |
| 53854 | pWal, cnt, pWal->hdr.mxFrame, isCommit ? "Commit" : "Spill")); |
| 53855 | } |
| 53856 | #endif |
| 53857 | |
| 53858 | pLive = (WalIndexHdr*)walIndexHdr(pWal); |
| 53859 | if( memcmp(&pWal->hdr, (void *)pLive, sizeof(WalIndexHdr))!=0 ){ |
| 53860 | iFirst = pLive->mxFrame+1; |
| 53861 | } |
| 53862 | |
| 53863 | /* See if it is possible to write these frames into the start of the |
| 53864 | ** log file, instead of appending to it at pWal->hdr.mxFrame. |
| 53865 | */ |
| 53866 | if( SQLITE_OK!=(rc = walRestartLog(pWal)) ){ |
| @@ -53741,17 +53922,45 @@ | |
| 53922 | szFrame = szPage + WAL_FRAME_HDRSIZE; |
| 53923 | |
| 53924 | /* Write all frames into the log file exactly once */ |
| 53925 | for(p=pList; p; p=p->pDirty){ |
| 53926 | int nDbSize; /* 0 normally. Positive == commit flag */ |
| 53927 | |
| 53928 | /* Check if this page has already been written into the wal file by |
| 53929 | ** the current transaction. If so, overwrite the existing frame and |
| 53930 | ** set Wal.writeLock to WAL_WRITELOCK_RECKSUM - indicating that |
| 53931 | ** checksums must be recomputed when the transaction is committed. */ |
| 53932 | if( iFirst && (p->pDirty || isCommit==0) ){ |
| 53933 | u32 iWrite = 0; |
| 53934 | VVA_ONLY(rc =) sqlite3WalFindFrame(pWal, p->pgno, &iWrite); |
| 53935 | assert( rc==SQLITE_OK || iWrite==0 ); |
| 53936 | if( iWrite>=iFirst ){ |
| 53937 | i64 iOff = walFrameOffset(iWrite, szPage) + WAL_FRAME_HDRSIZE; |
| 53938 | if( pWal->iReCksum==0 || iWrite<pWal->iReCksum ){ |
| 53939 | pWal->iReCksum = iWrite; |
| 53940 | } |
| 53941 | rc = sqlite3OsWrite(pWal->pWalFd, p->pData, szPage, iOff); |
| 53942 | if( rc ) return rc; |
| 53943 | p->flags &= ~PGHDR_WAL_APPEND; |
| 53944 | continue; |
| 53945 | } |
| 53946 | } |
| 53947 | |
| 53948 | iFrame++; |
| 53949 | assert( iOffset==walFrameOffset(iFrame, szPage) ); |
| 53950 | nDbSize = (isCommit && p->pDirty==0) ? nTruncate : 0; |
| 53951 | rc = walWriteOneFrame(&w, p, nDbSize, iOffset); |
| 53952 | if( rc ) return rc; |
| 53953 | pLast = p; |
| 53954 | iOffset += szFrame; |
| 53955 | p->flags |= PGHDR_WAL_APPEND; |
| 53956 | } |
| 53957 | |
| 53958 | /* Recalculate checksums within the wal file if required. */ |
| 53959 | if( isCommit && pWal->iReCksum ){ |
| 53960 | rc = walRewriteChecksums(pWal, iFrame); |
| 53961 | if( rc ) return rc; |
| 53962 | } |
| 53963 | |
| 53964 | /* If this is the end of a transaction, then we might need to pad |
| 53965 | ** the transaction and/or sync the WAL file. |
| 53966 | ** |
| @@ -53799,10 +54008,11 @@ | |
| 54008 | ** guarantees that there are no other writers, and no data that may |
| 54009 | ** be in use by existing readers is being overwritten. |
| 54010 | */ |
| 54011 | iFrame = pWal->hdr.mxFrame; |
| 54012 | for(p=pList; p && rc==SQLITE_OK; p=p->pDirty){ |
| 54013 | if( (p->flags & PGHDR_WAL_APPEND)==0 ) continue; |
| 54014 | iFrame++; |
| 54015 | rc = walIndexAppend(pWal, iFrame, p->pgno); |
| 54016 | } |
| 54017 | while( rc==SQLITE_OK && nExtra>0 ){ |
| 54018 | iFrame++; |
| @@ -53911,10 +54121,11 @@ | |
| 54121 | } |
| 54122 | } |
| 54123 | |
| 54124 | /* Copy data from the log to the database file. */ |
| 54125 | if( rc==SQLITE_OK ){ |
| 54126 | |
| 54127 | if( pWal->hdr.mxFrame && walPagesize(pWal)!=nBuf ){ |
| 54128 | rc = SQLITE_CORRUPT_BKPT; |
| 54129 | }else{ |
| 54130 | rc = walCheckpoint(pWal, eMode2, xBusy2, pBusyArg, sync_flags, zBuf); |
| 54131 | } |
| @@ -54066,10 +54277,16 @@ | |
| 54277 | SQLITE_PRIVATE int sqlite3WalFramesize(Wal *pWal){ |
| 54278 | assert( pWal==0 || pWal->readLock>=0 ); |
| 54279 | return (pWal ? pWal->szPage : 0); |
| 54280 | } |
| 54281 | #endif |
| 54282 | |
| 54283 | /* Return the sqlite3_file object for the WAL file |
| 54284 | */ |
| 54285 | SQLITE_PRIVATE sqlite3_file *sqlite3WalFile(Wal *pWal){ |
| 54286 | return pWal->pWalFd; |
| 54287 | } |
| 54288 | |
| 54289 | #endif /* #ifndef SQLITE_OMIT_WAL */ |
| 54290 | |
| 54291 | /************** End of wal.c *************************************************/ |
| 54292 | /************** Begin file btmutex.c *****************************************/ |
| @@ -54368,11 +54585,10 @@ | |
| 54585 | struct MemPage { |
| 54586 | u8 isInit; /* True if previously initialized. MUST BE FIRST! */ |
| 54587 | u8 nOverflow; /* Number of overflow cell bodies in aCell[] */ |
| 54588 | u8 intKey; /* True if table b-trees. False for index b-trees */ |
| 54589 | u8 intKeyLeaf; /* True if the leaf of an intKey table */ |
| 54590 | u8 leaf; /* True if a leaf page */ |
| 54591 | u8 hdrOffset; /* 100 for page 1. 0 otherwise */ |
| 54592 | u8 childPtrSize; /* 0 if leaf==1. 4 if leaf==0 */ |
| 54593 | u8 max1bytePayload; /* min(maxLocal,127) */ |
| 54594 | u8 bBusy; /* Prevent endless loops on corrupt database files */ |
| @@ -54955,25 +55171,10 @@ | |
| 55171 | |
| 55172 | return (p->sharable==0 || p->locked); |
| 55173 | } |
| 55174 | #endif |
| 55175 | |
| 55176 | |
| 55177 | /* |
| 55178 | ** Enter the mutex on every Btree associated with a database |
| 55179 | ** connection. This is needed (for example) prior to parsing |
| 55180 | ** a statement since we will be comparing table and column names |
| @@ -55004,18 +55205,10 @@ | |
| 55205 | p = db->aDb[i].pBt; |
| 55206 | if( p ) sqlite3BtreeLeave(p); |
| 55207 | } |
| 55208 | } |
| 55209 | |
| 55210 | #ifndef NDEBUG |
| 55211 | /* |
| 55212 | ** Return true if the current thread holds the database connection |
| 55213 | ** mutex and all required BtShared mutexes. |
| 55214 | ** |
| @@ -55085,10 +55278,29 @@ | |
| 55278 | p->pBt->db = p->db; |
| 55279 | } |
| 55280 | } |
| 55281 | } |
| 55282 | #endif /* if SQLITE_THREADSAFE */ |
| 55283 | |
| 55284 | #ifndef SQLITE_OMIT_INCRBLOB |
| 55285 | /* |
| 55286 | ** Enter a mutex on a Btree given a cursor owned by that Btree. |
| 55287 | ** |
| 55288 | ** These entry points are used by incremental I/O only. Enter() is required |
| 55289 | ** any time OMIT_SHARED_CACHE is not defined, regardless of whether or not |
| 55290 | ** the build is threadsafe. Leave() is only required by threadsafe builds. |
| 55291 | */ |
| 55292 | SQLITE_PRIVATE void sqlite3BtreeEnterCursor(BtCursor *pCur){ |
| 55293 | sqlite3BtreeEnter(pCur->pBtree); |
| 55294 | } |
| 55295 | # if SQLITE_THREADSAFE |
| 55296 | SQLITE_PRIVATE void sqlite3BtreeLeaveCursor(BtCursor *pCur){ |
| 55297 | sqlite3BtreeLeave(pCur->pBtree); |
| 55298 | } |
| 55299 | # endif |
| 55300 | #endif /* ifndef SQLITE_OMIT_INCRBLOB */ |
| 55301 | |
| 55302 | #endif /* ifndef SQLITE_OMIT_SHARED_CACHE */ |
| 55303 | |
| 55304 | /************** End of btmutex.c *********************************************/ |
| 55305 | /************** Begin file btree.c *******************************************/ |
| 55306 | /* |
| @@ -55541,10 +55753,14 @@ | |
| 55753 | */ |
| 55754 | #ifdef SQLITE_DEBUG |
| 55755 | static int cursorHoldsMutex(BtCursor *p){ |
| 55756 | return sqlite3_mutex_held(p->pBt->mutex); |
| 55757 | } |
| 55758 | static int cursorOwnsBtShared(BtCursor *p){ |
| 55759 | assert( cursorHoldsMutex(p) ); |
| 55760 | return (p->pBtree->db==p->pBt->db); |
| 55761 | } |
| 55762 | #endif |
| 55763 | |
| 55764 | /* |
| 55765 | ** Invalidate the overflow cache of the cursor passed as the first argument. |
| 55766 | ** on the shared btree structure pBt. |
| @@ -55877,11 +56093,11 @@ | |
| 56093 | ** saveCursorPosition(). |
| 56094 | */ |
| 56095 | static int btreeRestoreCursorPosition(BtCursor *pCur){ |
| 56096 | int rc; |
| 56097 | int skipNext; |
| 56098 | assert( cursorOwnsBtShared(pCur) ); |
| 56099 | assert( pCur->eState>=CURSOR_REQUIRESEEK ); |
| 56100 | if( pCur->eState==CURSOR_FAULT ){ |
| 56101 | return pCur->skipNext; |
| 56102 | } |
| 56103 | pCur->eState = CURSOR_INVALID; |
| @@ -56166,11 +56382,10 @@ | |
| 56382 | u8 *pCell, /* Pointer to the cell text. */ |
| 56383 | CellInfo *pInfo /* Fill in this structure */ |
| 56384 | ){ |
| 56385 | assert( sqlite3_mutex_held(pPage->pBt->mutex) ); |
| 56386 | assert( pPage->leaf==0 ); |
| 56387 | assert( pPage->childPtrSize==4 ); |
| 56388 | #ifndef SQLITE_DEBUG |
| 56389 | UNUSED_PARAMETER(pPage); |
| 56390 | #endif |
| 56391 | pInfo->nSize = 4 + getVarint(&pCell[4], (u64*)&pInfo->nKey); |
| @@ -56188,12 +56403,10 @@ | |
| 56403 | u32 nPayload; /* Number of bytes of cell payload */ |
| 56404 | u64 iKey; /* Extracted Key value */ |
| 56405 | |
| 56406 | assert( sqlite3_mutex_held(pPage->pBt->mutex) ); |
| 56407 | assert( pPage->leaf==0 || pPage->leaf==1 ); |
| 56408 | assert( pPage->intKeyLeaf ); |
| 56409 | assert( pPage->childPtrSize==0 ); |
| 56410 | pIter = pCell; |
| 56411 | |
| 56412 | /* The next block of code is equivalent to: |
| @@ -56258,11 +56471,10 @@ | |
| 56471 | u32 nPayload; /* Number of bytes of cell payload */ |
| 56472 | |
| 56473 | assert( sqlite3_mutex_held(pPage->pBt->mutex) ); |
| 56474 | assert( pPage->leaf==0 || pPage->leaf==1 ); |
| 56475 | assert( pPage->intKeyLeaf==0 ); |
| 56476 | pIter = pCell + pPage->childPtrSize; |
| 56477 | nPayload = *pIter; |
| 56478 | if( nPayload>=0x80 ){ |
| 56479 | u8 *pEnd = &pIter[8]; |
| 56480 | nPayload &= 0x7f; |
| @@ -56319,11 +56531,10 @@ | |
| 56531 | ** this function verifies that this invariant is not violated. */ |
| 56532 | CellInfo debuginfo; |
| 56533 | pPage->xParseCell(pPage, pCell, &debuginfo); |
| 56534 | #endif |
| 56535 | |
| 56536 | nSize = *pIter; |
| 56537 | if( nSize>=0x80 ){ |
| 56538 | pEnd = &pIter[8]; |
| 56539 | nSize &= 0x7f; |
| 56540 | do{ |
| @@ -56777,15 +56988,13 @@ | |
| 56988 | ** table b-tree page. */ |
| 56989 | assert( (PTF_LEAFDATA|PTF_INTKEY|PTF_LEAF)==13 ); |
| 56990 | pPage->intKey = 1; |
| 56991 | if( pPage->leaf ){ |
| 56992 | pPage->intKeyLeaf = 1; |
| 56993 | pPage->xParseCell = btreeParseCellPtr; |
| 56994 | }else{ |
| 56995 | pPage->intKeyLeaf = 0; |
| 56996 | pPage->xCellSize = cellSizePtrNoPayload; |
| 56997 | pPage->xParseCell = btreeParseCellPtrNoPayload; |
| 56998 | } |
| 56999 | pPage->maxLocal = pBt->maxLeaf; |
| 57000 | pPage->minLocal = pBt->minLeaf; |
| @@ -56796,11 +57005,10 @@ | |
| 57005 | /* EVIDENCE-OF: R-16571-11615 A value of 10 means the page is a leaf |
| 57006 | ** index b-tree page. */ |
| 57007 | assert( (PTF_ZERODATA|PTF_LEAF)==10 ); |
| 57008 | pPage->intKey = 0; |
| 57009 | pPage->intKeyLeaf = 0; |
| 57010 | pPage->xParseCell = btreeParseCellPtrIndex; |
| 57011 | pPage->maxLocal = pBt->maxLocal; |
| 57012 | pPage->minLocal = pBt->minLocal; |
| 57013 | }else{ |
| 57014 | /* EVIDENCE-OF: R-47608-56469 Any other value for the b-tree page type is |
| @@ -58217,11 +58425,10 @@ | |
| 58425 | ** no progress. By returning SQLITE_BUSY and not invoking the busy callback |
| 58426 | ** when A already has a read lock, we encourage A to give up and let B |
| 58427 | ** proceed. |
| 58428 | */ |
| 58429 | SQLITE_PRIVATE int sqlite3BtreeBeginTrans(Btree *p, int wrflag){ |
| 58430 | BtShared *pBt = p->pBt; |
| 58431 | int rc = SQLITE_OK; |
| 58432 | |
| 58433 | sqlite3BtreeEnter(p); |
| 58434 | btreeIntegrity(p); |
| @@ -58240,31 +58447,34 @@ | |
| 58447 | rc = SQLITE_READONLY; |
| 58448 | goto trans_begun; |
| 58449 | } |
| 58450 | |
| 58451 | #ifndef SQLITE_OMIT_SHARED_CACHE |
| 58452 | { |
| 58453 | sqlite3 *pBlock = 0; |
| 58454 | /* If another database handle has already opened a write transaction |
| 58455 | ** on this shared-btree structure and a second write transaction is |
| 58456 | ** requested, return SQLITE_LOCKED. |
| 58457 | */ |
| 58458 | if( (wrflag && pBt->inTransaction==TRANS_WRITE) |
| 58459 | || (pBt->btsFlags & BTS_PENDING)!=0 |
| 58460 | ){ |
| 58461 | pBlock = pBt->pWriter->db; |
| 58462 | }else if( wrflag>1 ){ |
| 58463 | BtLock *pIter; |
| 58464 | for(pIter=pBt->pLock; pIter; pIter=pIter->pNext){ |
| 58465 | if( pIter->pBtree!=p ){ |
| 58466 | pBlock = pIter->pBtree->db; |
| 58467 | break; |
| 58468 | } |
| 58469 | } |
| 58470 | } |
| 58471 | if( pBlock ){ |
| 58472 | sqlite3ConnectionBlocked(p->db, pBlock); |
| 58473 | rc = SQLITE_LOCKED_SHAREDCACHE; |
| 58474 | goto trans_begun; |
| 58475 | } |
| 58476 | } |
| 58477 | #endif |
| 58478 | |
| 58479 | /* Any read-only or read-write transaction implies a read-lock on |
| 58480 | ** page 1. So if some other shared-cache client already has a write-lock |
| @@ -59377,11 +59587,11 @@ | |
| 59587 | ** Failure is not possible. This function always returns SQLITE_OK. |
| 59588 | ** It might just as well be a procedure (returning void) but we continue |
| 59589 | ** to return an integer result code for historical reasons. |
| 59590 | */ |
| 59591 | SQLITE_PRIVATE int sqlite3BtreeDataSize(BtCursor *pCur, u32 *pSize){ |
| 59592 | assert( cursorOwnsBtShared(pCur) ); |
| 59593 | assert( pCur->eState==CURSOR_VALID ); |
| 59594 | assert( pCur->iPage>=0 ); |
| 59595 | assert( pCur->iPage<BTCURSOR_MAX_DEPTH ); |
| 59596 | assert( pCur->apPage[pCur->iPage]->intKeyLeaf==1 ); |
| 59597 | getCellInfo(pCur); |
| @@ -59757,11 +59967,11 @@ | |
| 59967 | if ( pCur->eState==CURSOR_INVALID ){ |
| 59968 | return SQLITE_ABORT; |
| 59969 | } |
| 59970 | #endif |
| 59971 | |
| 59972 | assert( cursorOwnsBtShared(pCur) ); |
| 59973 | rc = restoreCursorPosition(pCur); |
| 59974 | if( rc==SQLITE_OK ){ |
| 59975 | assert( pCur->eState==CURSOR_VALID ); |
| 59976 | assert( pCur->iPage>=0 && pCur->apPage[pCur->iPage] ); |
| 59977 | assert( pCur->aiIdx[pCur->iPage]<pCur->apPage[pCur->iPage]->nCell ); |
| @@ -59795,11 +60005,11 @@ | |
| 60005 | ){ |
| 60006 | u32 amt; |
| 60007 | assert( pCur!=0 && pCur->iPage>=0 && pCur->apPage[pCur->iPage]); |
| 60008 | assert( pCur->eState==CURSOR_VALID ); |
| 60009 | assert( sqlite3_mutex_held(pCur->pBtree->db->mutex) ); |
| 60010 | assert( cursorOwnsBtShared(pCur) ); |
| 60011 | assert( pCur->aiIdx[pCur->iPage]<pCur->apPage[pCur->iPage]->nCell ); |
| 60012 | assert( pCur->info.nSize>0 ); |
| 60013 | assert( pCur->info.pPayload>pCur->apPage[pCur->iPage]->aData || CORRUPT_DB ); |
| 60014 | assert( pCur->info.pPayload<pCur->apPage[pCur->iPage]->aDataEnd ||CORRUPT_DB); |
| 60015 | amt = (int)(pCur->apPage[pCur->iPage]->aDataEnd - pCur->info.pPayload); |
| @@ -59841,11 +60051,11 @@ | |
| 60051 | ** vice-versa). |
| 60052 | */ |
| 60053 | static int moveToChild(BtCursor *pCur, u32 newPgno){ |
| 60054 | BtShared *pBt = pCur->pBt; |
| 60055 | |
| 60056 | assert( cursorOwnsBtShared(pCur) ); |
| 60057 | assert( pCur->eState==CURSOR_VALID ); |
| 60058 | assert( pCur->iPage<BTCURSOR_MAX_DEPTH ); |
| 60059 | assert( pCur->iPage>=0 ); |
| 60060 | if( pCur->iPage>=(BTCURSOR_MAX_DEPTH-1) ){ |
| 60061 | return SQLITE_CORRUPT_BKPT; |
| @@ -59887,11 +60097,11 @@ | |
| 60097 | ** to the page we are coming from. If we are coming from the |
| 60098 | ** right-most child page then pCur->idx is set to one more than |
| 60099 | ** the largest cell index. |
| 60100 | */ |
| 60101 | static void moveToParent(BtCursor *pCur){ |
| 60102 | assert( cursorOwnsBtShared(pCur) ); |
| 60103 | assert( pCur->eState==CURSOR_VALID ); |
| 60104 | assert( pCur->iPage>0 ); |
| 60105 | assert( pCur->apPage[pCur->iPage] ); |
| 60106 | assertParentIndex( |
| 60107 | pCur->apPage[pCur->iPage-1], |
| @@ -59927,11 +60137,11 @@ | |
| 60137 | */ |
| 60138 | static int moveToRoot(BtCursor *pCur){ |
| 60139 | MemPage *pRoot; |
| 60140 | int rc = SQLITE_OK; |
| 60141 | |
| 60142 | assert( cursorOwnsBtShared(pCur) ); |
| 60143 | assert( CURSOR_INVALID < CURSOR_REQUIRESEEK ); |
| 60144 | assert( CURSOR_VALID < CURSOR_REQUIRESEEK ); |
| 60145 | assert( CURSOR_FAULT > CURSOR_REQUIRESEEK ); |
| 60146 | if( pCur->eState>=CURSOR_REQUIRESEEK ){ |
| 60147 | if( pCur->eState==CURSOR_FAULT ){ |
| @@ -60006,11 +60216,11 @@ | |
| 60216 | static int moveToLeftmost(BtCursor *pCur){ |
| 60217 | Pgno pgno; |
| 60218 | int rc = SQLITE_OK; |
| 60219 | MemPage *pPage; |
| 60220 | |
| 60221 | assert( cursorOwnsBtShared(pCur) ); |
| 60222 | assert( pCur->eState==CURSOR_VALID ); |
| 60223 | while( rc==SQLITE_OK && !(pPage = pCur->apPage[pCur->iPage])->leaf ){ |
| 60224 | assert( pCur->aiIdx[pCur->iPage]<pPage->nCell ); |
| 60225 | pgno = get4byte(findCell(pPage, pCur->aiIdx[pCur->iPage])); |
| 60226 | rc = moveToChild(pCur, pgno); |
| @@ -60031,11 +60241,11 @@ | |
| 60241 | static int moveToRightmost(BtCursor *pCur){ |
| 60242 | Pgno pgno; |
| 60243 | int rc = SQLITE_OK; |
| 60244 | MemPage *pPage = 0; |
| 60245 | |
| 60246 | assert( cursorOwnsBtShared(pCur) ); |
| 60247 | assert( pCur->eState==CURSOR_VALID ); |
| 60248 | while( !(pPage = pCur->apPage[pCur->iPage])->leaf ){ |
| 60249 | pgno = get4byte(&pPage->aData[pPage->hdrOffset+8]); |
| 60250 | pCur->aiIdx[pCur->iPage] = pPage->nCell; |
| 60251 | rc = moveToChild(pCur, pgno); |
| @@ -60052,11 +60262,11 @@ | |
| 60262 | ** or set *pRes to 1 if the table is empty. |
| 60263 | */ |
| 60264 | SQLITE_PRIVATE int sqlite3BtreeFirst(BtCursor *pCur, int *pRes){ |
| 60265 | int rc; |
| 60266 | |
| 60267 | assert( cursorOwnsBtShared(pCur) ); |
| 60268 | assert( sqlite3_mutex_held(pCur->pBtree->db->mutex) ); |
| 60269 | rc = moveToRoot(pCur); |
| 60270 | if( rc==SQLITE_OK ){ |
| 60271 | if( pCur->eState==CURSOR_INVALID ){ |
| 60272 | assert( pCur->pgnoRoot==0 || pCur->apPage[pCur->iPage]->nCell==0 ); |
| @@ -60075,11 +60285,11 @@ | |
| 60285 | ** or set *pRes to 1 if the table is empty. |
| 60286 | */ |
| 60287 | SQLITE_PRIVATE int sqlite3BtreeLast(BtCursor *pCur, int *pRes){ |
| 60288 | int rc; |
| 60289 | |
| 60290 | assert( cursorOwnsBtShared(pCur) ); |
| 60291 | assert( sqlite3_mutex_held(pCur->pBtree->db->mutex) ); |
| 60292 | |
| 60293 | /* If the cursor already points to the last entry, this is a no-op. */ |
| 60294 | if( CURSOR_VALID==pCur->eState && (pCur->curFlags & BTCF_AtLast)!=0 ){ |
| 60295 | #ifdef SQLITE_DEBUG |
| @@ -60153,11 +60363,11 @@ | |
| 60363 | int *pRes /* Write search results here */ |
| 60364 | ){ |
| 60365 | int rc; |
| 60366 | RecordCompare xRecordCompare; |
| 60367 | |
| 60368 | assert( cursorOwnsBtShared(pCur) ); |
| 60369 | assert( sqlite3_mutex_held(pCur->pBtree->db->mutex) ); |
| 60370 | assert( pRes ); |
| 60371 | assert( (pIdxKey==0)==(pCur->pKeyInfo==0) ); |
| 60372 | |
| 60373 | /* If the cursor is already positioned at the point we are trying |
| @@ -60401,11 +60611,11 @@ | |
| 60611 | static SQLITE_NOINLINE int btreeNext(BtCursor *pCur, int *pRes){ |
| 60612 | int rc; |
| 60613 | int idx; |
| 60614 | MemPage *pPage; |
| 60615 | |
| 60616 | assert( cursorOwnsBtShared(pCur) ); |
| 60617 | assert( pCur->skipNext==0 || pCur->eState!=CURSOR_VALID ); |
| 60618 | assert( *pRes==0 ); |
| 60619 | if( pCur->eState!=CURSOR_VALID ){ |
| 60620 | assert( (pCur->curFlags & BTCF_ValidOvfl)==0 ); |
| 60621 | rc = restoreCursorPosition(pCur); |
| @@ -60465,11 +60675,11 @@ | |
| 60675 | return moveToLeftmost(pCur); |
| 60676 | } |
| 60677 | } |
| 60678 | SQLITE_PRIVATE int sqlite3BtreeNext(BtCursor *pCur, int *pRes){ |
| 60679 | MemPage *pPage; |
| 60680 | assert( cursorOwnsBtShared(pCur) ); |
| 60681 | assert( pRes!=0 ); |
| 60682 | assert( *pRes==0 || *pRes==1 ); |
| 60683 | assert( pCur->skipNext==0 || pCur->eState!=CURSOR_VALID ); |
| 60684 | pCur->info.nSize = 0; |
| 60685 | pCur->curFlags &= ~(BTCF_ValidNKey|BTCF_ValidOvfl); |
| @@ -60510,11 +60720,11 @@ | |
| 60720 | */ |
| 60721 | static SQLITE_NOINLINE int btreePrevious(BtCursor *pCur, int *pRes){ |
| 60722 | int rc; |
| 60723 | MemPage *pPage; |
| 60724 | |
| 60725 | assert( cursorOwnsBtShared(pCur) ); |
| 60726 | assert( pRes!=0 ); |
| 60727 | assert( *pRes==0 ); |
| 60728 | assert( pCur->skipNext==0 || pCur->eState!=CURSOR_VALID ); |
| 60729 | assert( (pCur->curFlags & (BTCF_AtLast|BTCF_ValidOvfl|BTCF_ValidNKey))==0 ); |
| 60730 | assert( pCur->info.nSize==0 ); |
| @@ -60566,11 +60776,11 @@ | |
| 60776 | } |
| 60777 | } |
| 60778 | return rc; |
| 60779 | } |
| 60780 | SQLITE_PRIVATE int sqlite3BtreePrevious(BtCursor *pCur, int *pRes){ |
| 60781 | assert( cursorOwnsBtShared(pCur) ); |
| 60782 | assert( pRes!=0 ); |
| 60783 | assert( *pRes==0 || *pRes==1 ); |
| 60784 | assert( pCur->skipNext==0 || pCur->eState!=CURSOR_VALID ); |
| 60785 | *pRes = 0; |
| 60786 | pCur->curFlags &= ~(BTCF_AtLast|BTCF_ValidOvfl|BTCF_ValidNKey); |
| @@ -63046,11 +63256,11 @@ | |
| 63256 | if( pCur->eState==CURSOR_FAULT ){ |
| 63257 | assert( pCur->skipNext!=SQLITE_OK ); |
| 63258 | return pCur->skipNext; |
| 63259 | } |
| 63260 | |
| 63261 | assert( cursorOwnsBtShared(pCur) ); |
| 63262 | assert( (pCur->curFlags & BTCF_WriteFlag)!=0 |
| 63263 | && pBt->inTransaction==TRANS_WRITE |
| 63264 | && (pBt->btsFlags & BTS_READ_ONLY)==0 ); |
| 63265 | assert( hasSharedCacheTableLock(p, pCur->pgnoRoot, pCur->pKeyInfo!=0, 2) ); |
| 63266 | |
| @@ -63193,11 +63403,11 @@ | |
| 63403 | int iCellIdx; /* Index of cell to delete */ |
| 63404 | int iCellDepth; /* Depth of node containing pCell */ |
| 63405 | u16 szCell; /* Size of the cell being deleted */ |
| 63406 | int bSkipnext = 0; /* Leaf cursor in SKIPNEXT state */ |
| 63407 | |
| 63408 | assert( cursorOwnsBtShared(pCur) ); |
| 63409 | assert( pBt->inTransaction==TRANS_WRITE ); |
| 63410 | assert( (pBt->btsFlags & BTS_READ_ONLY)==0 ); |
| 63411 | assert( pCur->curFlags & BTCF_WriteFlag ); |
| 63412 | assert( hasSharedCacheTableLock(p, pCur->pgnoRoot, pCur->pKeyInfo!=0, 2) ); |
| 63413 | assert( !hasReadConflicts(p, pCur->pgnoRoot) ); |
| @@ -64655,11 +64865,11 @@ | |
| 64865 | ** parameters that attempt to write past the end of the existing data, |
| 64866 | ** no modifications are made and SQLITE_CORRUPT is returned. |
| 64867 | */ |
| 64868 | SQLITE_PRIVATE int sqlite3BtreePutData(BtCursor *pCsr, u32 offset, u32 amt, void *z){ |
| 64869 | int rc; |
| 64870 | assert( cursorOwnsBtShared(pCsr) ); |
| 64871 | assert( sqlite3_mutex_held(pCsr->pBtree->db->mutex) ); |
| 64872 | assert( pCsr->curFlags & BTCF_Incrblob ); |
| 64873 | |
| 64874 | rc = restoreCursorPosition(pCsr); |
| 64875 | if( rc!=SQLITE_OK ){ |
| @@ -64762,10 +64972,19 @@ | |
| 64972 | |
| 64973 | /* |
| 64974 | ** Return the size of the header added to each page by this module. |
| 64975 | */ |
| 64976 | SQLITE_PRIVATE int sqlite3HeaderSizeBtree(void){ return ROUND8(sizeof(MemPage)); } |
| 64977 | |
| 64978 | #if !defined(SQLITE_OMIT_SHARED_CACHE) |
| 64979 | /* |
| 64980 | ** Return true if the Btree passed as the only argument is sharable. |
| 64981 | */ |
| 64982 | SQLITE_PRIVATE int sqlite3BtreeSharable(Btree *p){ |
| 64983 | return p->sharable; |
| 64984 | } |
| 64985 | #endif |
| 64986 | |
| 64987 | /************** End of btree.c ***********************************************/ |
| 64988 | /************** Begin file backup.c ******************************************/ |
| 64989 | /* |
| 64990 | ** 2009 January 28 |
| @@ -67593,12 +67812,11 @@ | |
| 67812 | ** The zWhere string must have been obtained from sqlite3_malloc(). |
| 67813 | ** This routine will take ownership of the allocated memory. |
| 67814 | */ |
| 67815 | SQLITE_PRIVATE void sqlite3VdbeAddParseSchemaOp(Vdbe *p, int iDb, char *zWhere){ |
| 67816 | int j; |
| 67817 | sqlite3VdbeAddOp4(p, OP_ParseSchema, iDb, 0, 0, zWhere, P4_DYNAMIC); |
| 67818 | for(j=0; j<p->db->nDb; j++) sqlite3VdbeUsesBtree(p, j); |
| 67819 | } |
| 67820 | |
| 67821 | /* |
| 67822 | ** Add an opcode that includes the p4 value as an integer. |
| @@ -68093,11 +68311,11 @@ | |
| 68311 | */ |
| 68312 | static void vdbeFreeOpArray(sqlite3 *db, Op *aOp, int nOp){ |
| 68313 | if( aOp ){ |
| 68314 | Op *pOp; |
| 68315 | for(pOp=aOp; pOp<&aOp[nOp]; pOp++){ |
| 68316 | if( pOp->p4type ) freeP4(db, pOp->p4type, pOp->p4.p); |
| 68317 | #ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS |
| 68318 | sqlite3DbFree(db, pOp->zComment); |
| 68319 | #endif |
| 68320 | } |
| 68321 | } |
| @@ -68155,65 +68373,60 @@ | |
| 68373 | ** to a string or structure that is guaranteed to exist for the lifetime of |
| 68374 | ** the Vdbe. In these cases we can just copy the pointer. |
| 68375 | ** |
| 68376 | ** If addr<0 then change P4 on the most recently inserted instruction. |
| 68377 | */ |
| 68378 | static void SQLITE_NOINLINE vdbeChangeP4Full( |
| 68379 | Vdbe *p, |
| 68380 | Op *pOp, |
| 68381 | const char *zP4, |
| 68382 | int n |
| 68383 | ){ |
| 68384 | if( pOp->p4type ){ |
| 68385 | freeP4(p->db, pOp->p4type, pOp->p4.p); |
| 68386 | pOp->p4type = 0; |
| 68387 | pOp->p4.p = 0; |
| 68388 | } |
| 68389 | if( n<0 ){ |
| 68390 | sqlite3VdbeChangeP4(p, (int)(pOp - p->aOp), zP4, n); |
| 68391 | }else{ |
| 68392 | if( n==0 ) n = sqlite3Strlen30(zP4); |
| 68393 | pOp->p4.z = sqlite3DbStrNDup(p->db, zP4, n); |
| 68394 | pOp->p4type = P4_DYNAMIC; |
| 68395 | } |
| 68396 | } |
| 68397 | SQLITE_PRIVATE void sqlite3VdbeChangeP4(Vdbe *p, int addr, const char *zP4, int n){ |
| 68398 | Op *pOp; |
| 68399 | sqlite3 *db; |
| 68400 | assert( p!=0 ); |
| 68401 | db = p->db; |
| 68402 | assert( p->magic==VDBE_MAGIC_INIT ); |
| 68403 | assert( p->aOp!=0 || db->mallocFailed ); |
| 68404 | if( db->mallocFailed ){ |
| 68405 | if( n!=P4_VTAB ) freeP4(db, n, (void*)*(char**)&zP4); |
| 68406 | return; |
| 68407 | } |
| 68408 | assert( p->nOp>0 ); |
| 68409 | assert( addr<p->nOp ); |
| 68410 | if( addr<0 ){ |
| 68411 | addr = p->nOp - 1; |
| 68412 | } |
| 68413 | pOp = &p->aOp[addr]; |
| 68414 | if( n>=0 || pOp->p4type ){ |
| 68415 | vdbeChangeP4Full(p, pOp, zP4, n); |
| 68416 | return; |
| 68417 | } |
| 68418 | if( n==P4_INT32 ){ |
| 68419 | /* Note: this cast is safe, because the origin data point was an int |
| 68420 | ** that was cast to a (const char *). */ |
| 68421 | pOp->p4.i = SQLITE_PTR_TO_INT(zP4); |
| 68422 | pOp->p4type = P4_INT32; |
| 68423 | }else if( zP4!=0 ){ |
| 68424 | assert( n<0 ); |
| 68425 | pOp->p4.p = (void*)zP4; |
| 68426 | pOp->p4type = (signed char)n; |
| 68427 | if( n==P4_VTAB ) sqlite3VtabLock((VTable*)zP4); |
| 68428 | } |
| 68429 | } |
| 68430 | |
| 68431 | /* |
| 68432 | ** Set the P4 on the most recently added opcode to the KeyInfo for the |
| @@ -68605,11 +68818,11 @@ | |
| 68818 | if( i!=1 && sqlite3BtreeSharable(p->db->aDb[i].pBt) ){ |
| 68819 | DbMaskSet(p->lockMask, i); |
| 68820 | } |
| 68821 | } |
| 68822 | |
| 68823 | #if !defined(SQLITE_OMIT_SHARED_CACHE) |
| 68824 | /* |
| 68825 | ** If SQLite is compiled to support shared-cache mode and to be threadsafe, |
| 68826 | ** this routine obtains the mutex associated with each BtShared structure |
| 68827 | ** that may be accessed by the VM passed as an argument. In doing so it also |
| 68828 | ** sets the BtShared.db member of each of the BtShared structures, ensuring |
| @@ -76059,11 +76272,10 @@ | |
| 76272 | const u8 *zEndHdr; /* Pointer to first byte after the header */ |
| 76273 | u32 offset; /* Offset into the data */ |
| 76274 | u64 offset64; /* 64-bit offset */ |
| 76275 | u32 avail; /* Number of bytes of available data */ |
| 76276 | u32 t; /* A type code from the record header */ |
| 76277 | Mem *pReg; /* PseudoTable input register */ |
| 76278 | |
| 76279 | p2 = pOp->p2; |
| 76280 | assert( pOp->p3>0 && pOp->p3<=(p->nMem-p->nCursor) ); |
| 76281 | pDest = &aMem[pOp->p3]; |
| @@ -76237,14 +76449,35 @@ | |
| 76449 | assert( p2<pC->nHdrParsed ); |
| 76450 | assert( rc==SQLITE_OK ); |
| 76451 | assert( sqlite3VdbeCheckMemInvariants(pDest) ); |
| 76452 | if( VdbeMemDynamic(pDest) ) sqlite3VdbeMemSetNull(pDest); |
| 76453 | assert( t==pC->aType[p2] ); |
| 76454 | pDest->enc = encoding; |
| 76455 | if( pC->szRow>=aOffset[p2+1] ){ |
| 76456 | /* This is the common case where the desired content fits on the original |
| 76457 | ** page - where the content is not on an overflow page */ |
| 76458 | zData = pC->aRow + aOffset[p2]; |
| 76459 | if( t<12 ){ |
| 76460 | sqlite3VdbeSerialGet(zData, t, pDest); |
| 76461 | }else{ |
| 76462 | /* If the column value is a string, we need a persistent value, not |
| 76463 | ** a MEM_Ephem value. This branch is a fast short-cut that is equivalent |
| 76464 | ** to calling sqlite3VdbeSerialGet() and sqlite3VdbeDeephemeralize(). |
| 76465 | */ |
| 76466 | static const u16 aFlag[] = { MEM_Blob, MEM_Str|MEM_Term }; |
| 76467 | pDest->n = len = (t-12)/2; |
| 76468 | if( pDest->szMalloc < len+2 ){ |
| 76469 | pDest->flags = MEM_Null; |
| 76470 | if( sqlite3VdbeMemGrow(pDest, len+2, 0) ) goto no_mem; |
| 76471 | }else{ |
| 76472 | pDest->z = pDest->zMalloc; |
| 76473 | } |
| 76474 | memcpy(pDest->z, zData, len); |
| 76475 | pDest->z[len] = 0; |
| 76476 | pDest->z[len+1] = 0; |
| 76477 | pDest->flags = aFlag[t&1]; |
| 76478 | } |
| 76479 | }else{ |
| 76480 | /* This branch happens only when content is on overflow pages */ |
| 76481 | if( ((pOp->p5 & (OPFLAG_LENGTHARG|OPFLAG_TYPEOFARG))!=0 |
| 76482 | && ((t>=12 && (t&1)==0) || (pOp->p5 & OPFLAG_TYPEOFARG)!=0)) |
| 76483 | || (len = sqlite3VdbeSerialTypeLen(t))==0 |
| @@ -76252,42 +76485,24 @@ | |
| 76485 | /* Content is irrelevant for |
| 76486 | ** 1. the typeof() function, |
| 76487 | ** 2. the length(X) function if X is a blob, and |
| 76488 | ** 3. if the content length is zero. |
| 76489 | ** So we might as well use bogus content rather than reading |
| 76490 | ** content from disk. */ |
| 76491 | static u8 aZero[8]; /* This is the bogus content */ |
| 76492 | sqlite3VdbeSerialGet(aZero, t, pDest); |
| 76493 | }else{ |
| 76494 | rc = sqlite3VdbeMemFromBtree(pCrsr, aOffset[p2], len, !pC->isTable, |
| 76495 | pDest); |
| 76496 | if( rc==SQLITE_OK ){ |
| 76497 | sqlite3VdbeSerialGet((const u8*)pDest->z, t, pDest); |
| 76498 | pDest->flags &= ~MEM_Ephem; |
| 76499 | } |
| 76500 | } |
| 76501 | } |
| 76502 | |
| 76503 | op_column_out: |
| 76504 | op_column_error: |
| 76505 | UPDATE_MAX_BLOBSIZE(pDest); |
| 76506 | REGISTER_TRACE(pOp->p3, pDest); |
| 76507 | break; |
| 76508 | } |
| @@ -81685,11 +81900,11 @@ | |
| 81900 | assert( pReadr->aBuffer==0 ); |
| 81901 | assert( pReadr->aMap==0 ); |
| 81902 | |
| 81903 | rc = vdbePmaReaderSeek(pTask, pReadr, pFile, iStart); |
| 81904 | if( rc==SQLITE_OK ){ |
| 81905 | u64 nByte = 0; /* Size of PMA in bytes */ |
| 81906 | rc = vdbePmaReadVarint(pReadr, &nByte); |
| 81907 | pReadr->iEof = pReadr->iReadOff + nByte; |
| 81908 | *pnByte += nByte; |
| 81909 | } |
| 81910 | |
| @@ -84243,13 +84458,12 @@ | |
| 84458 | ** return the top-level walk call. |
| 84459 | ** |
| 84460 | ** The return value from this routine is WRC_Abort to abandon the tree walk |
| 84461 | ** and WRC_Continue to continue. |
| 84462 | */ |
| 84463 | static SQLITE_NOINLINE int walkExpr(Walker *pWalker, Expr *pExpr){ |
| 84464 | int rc; |
| 84465 | testcase( ExprHasProperty(pExpr, EP_TokenOnly) ); |
| 84466 | testcase( ExprHasProperty(pExpr, EP_Reduced) ); |
| 84467 | rc = pWalker->xExprCallback(pWalker, pExpr); |
| 84468 | if( rc==WRC_Continue |
| 84469 | && !ExprHasProperty(pExpr,EP_TokenOnly) ){ |
| @@ -84260,10 +84474,13 @@ | |
| 84474 | }else{ |
| 84475 | if( sqlite3WalkExprList(pWalker, pExpr->x.pList) ) return WRC_Abort; |
| 84476 | } |
| 84477 | } |
| 84478 | return rc & WRC_Abort; |
| 84479 | } |
| 84480 | SQLITE_PRIVATE int sqlite3WalkExpr(Walker *pWalker, Expr *pExpr){ |
| 84481 | return pExpr ? walkExpr(pWalker,pExpr) : WRC_Continue; |
| 84482 | } |
| 84483 | |
| 84484 | /* |
| 84485 | ** Call sqlite3WalkExpr() for every expression in list p or until |
| 84486 | ** an abort request is seen. |
| @@ -86327,12 +86544,13 @@ | |
| 86544 | || sqlite3GetInt32(pToken->z, &iValue)==0 ){ |
| 86545 | nExtra = pToken->n+1; |
| 86546 | assert( iValue>=0 ); |
| 86547 | } |
| 86548 | } |
| 86549 | pNew = sqlite3DbMallocRaw(db, sizeof(Expr)+nExtra); |
| 86550 | if( pNew ){ |
| 86551 | memset(pNew, 0, sizeof(Expr)); |
| 86552 | pNew->op = (u8)op; |
| 86553 | pNew->iAgg = -1; |
| 86554 | if( pToken ){ |
| 86555 | if( nExtra==0 ){ |
| 86556 | pNew->flags |= EP_IntValue; |
| @@ -93723,19 +93941,10 @@ | |
| 93941 | ** COMMIT |
| 93942 | ** ROLLBACK |
| 93943 | */ |
| 93944 | /* #include "sqliteInt.h" */ |
| 93945 | |
| 93946 | #ifndef SQLITE_OMIT_SHARED_CACHE |
| 93947 | /* |
| 93948 | ** The TableLock structure is only used by the sqlite3TableLock() and |
| 93949 | ** codeTableLocks() functions. |
| 93950 | */ |
| @@ -100072,14 +100281,14 @@ | |
| 100281 | |
| 100282 | /* |
| 100283 | ** A structure defining how to do GLOB-style comparisons. |
| 100284 | */ |
| 100285 | struct compareInfo { |
| 100286 | u8 matchAll; /* "*" or "%" */ |
| 100287 | u8 matchOne; /* "?" or "_" */ |
| 100288 | u8 matchSet; /* "[" or 0 */ |
| 100289 | u8 noCase; /* true to ignore case differences */ |
| 100290 | }; |
| 100291 | |
| 100292 | /* |
| 100293 | ** For LIKE and GLOB matching on EBCDIC machines, assume that every |
| 100294 | ** character is exactly one byte in size. Also, provde the Utf8Read() |
| @@ -100138,26 +100347,18 @@ | |
| 100347 | */ |
| 100348 | static int patternCompare( |
| 100349 | const u8 *zPattern, /* The glob pattern */ |
| 100350 | const u8 *zString, /* The string to compare against the glob */ |
| 100351 | const struct compareInfo *pInfo, /* Information about how to do the compare */ |
| 100352 | u32 matchOther /* The escape char (LIKE) or '[' (GLOB) */ |
| 100353 | ){ |
| 100354 | u32 c, c2; /* Next pattern and input string chars */ |
| 100355 | u32 matchOne = pInfo->matchOne; /* "?" or "_" */ |
| 100356 | u32 matchAll = pInfo->matchAll; /* "*" or "%" */ |
| 100357 | u8 noCase = pInfo->noCase; /* True if uppercase==lowercase */ |
| 100358 | const u8 *zEscaped = 0; /* One past the last escaped input char */ |
| 100359 | |
| 100360 | while( (c = Utf8Read(zPattern))!=0 ){ |
| 100361 | if( c==matchAll ){ /* Match "*" */ |
| 100362 | /* Skip over multiple "*" characters in the pattern. If there |
| 100363 | ** are also "?" characters, skip those as well, but consume a |
| 100364 | ** single character of the input string for each "?" skipped */ |
| @@ -100167,19 +100368,19 @@ | |
| 100368 | } |
| 100369 | } |
| 100370 | if( c==0 ){ |
| 100371 | return 1; /* "*" at the end of the pattern matches */ |
| 100372 | }else if( c==matchOther ){ |
| 100373 | if( pInfo->matchSet==0 ){ |
| 100374 | c = sqlite3Utf8Read(&zPattern); |
| 100375 | if( c==0 ) return 0; |
| 100376 | }else{ |
| 100377 | /* "[...]" immediately follows the "*". We have to do a slow |
| 100378 | ** recursive search in this case, but it is an unusual case. */ |
| 100379 | assert( matchOther<0x80 ); /* '[' is a single-byte character */ |
| 100380 | while( *zString |
| 100381 | && patternCompare(&zPattern[-1],zString,pInfo,matchOther)==0 ){ |
| 100382 | SQLITE_SKIP_UTF8(zString); |
| 100383 | } |
| 100384 | return *zString!=0; |
| 100385 | } |
| 100386 | } |
| @@ -100201,22 +100402,22 @@ | |
| 100402 | }else{ |
| 100403 | cx = c; |
| 100404 | } |
| 100405 | while( (c2 = *(zString++))!=0 ){ |
| 100406 | if( c2!=c && c2!=cx ) continue; |
| 100407 | if( patternCompare(zPattern,zString,pInfo,matchOther) ) return 1; |
| 100408 | } |
| 100409 | }else{ |
| 100410 | while( (c2 = Utf8Read(zString))!=0 ){ |
| 100411 | if( c2!=c ) continue; |
| 100412 | if( patternCompare(zPattern,zString,pInfo,matchOther) ) return 1; |
| 100413 | } |
| 100414 | } |
| 100415 | return 0; |
| 100416 | } |
| 100417 | if( c==matchOther ){ |
| 100418 | if( pInfo->matchSet==0 ){ |
| 100419 | c = sqlite3Utf8Read(&zPattern); |
| 100420 | if( c==0 ) return 0; |
| 100421 | zEscaped = zPattern; |
| 100422 | }else{ |
| 100423 | u32 prior_c = 0; |
| @@ -100265,11 +100466,11 @@ | |
| 100466 | |
| 100467 | /* |
| 100468 | ** The sqlite3_strglob() interface. |
| 100469 | */ |
| 100470 | SQLITE_API int SQLITE_STDCALL sqlite3_strglob(const char *zGlobPattern, const char *zString){ |
| 100471 | return patternCompare((u8*)zGlobPattern, (u8*)zString, &globInfo, '[')==0; |
| 100472 | } |
| 100473 | |
| 100474 | /* |
| 100475 | ** The sqlite3_strlike() interface. |
| 100476 | */ |
| @@ -100303,13 +100504,14 @@ | |
| 100504 | sqlite3_context *context, |
| 100505 | int argc, |
| 100506 | sqlite3_value **argv |
| 100507 | ){ |
| 100508 | const unsigned char *zA, *zB; |
| 100509 | u32 escape; |
| 100510 | int nPat; |
| 100511 | sqlite3 *db = sqlite3_context_db_handle(context); |
| 100512 | struct compareInfo *pInfo = sqlite3_user_data(context); |
| 100513 | |
| 100514 | #ifdef SQLITE_LIKE_DOESNT_MATCH_BLOBS |
| 100515 | if( sqlite3_value_type(argv[0])==SQLITE_BLOB |
| 100516 | || sqlite3_value_type(argv[1])==SQLITE_BLOB |
| 100517 | ){ |
| @@ -100345,17 +100547,17 @@ | |
| 100547 | sqlite3_result_error(context, |
| 100548 | "ESCAPE expression must be a single character", -1); |
| 100549 | return; |
| 100550 | } |
| 100551 | escape = sqlite3Utf8Read(&zEsc); |
| 100552 | }else{ |
| 100553 | escape = pInfo->matchSet; |
| 100554 | } |
| 100555 | if( zA && zB ){ |
| 100556 | #ifdef SQLITE_TEST |
| 100557 | sqlite3_like_count++; |
| 100558 | #endif |
| 100559 | sqlite3_result_int(context, patternCompare(zB, zA, pInfo, escape)); |
| 100560 | } |
| 100561 | } |
| 100562 | |
| 100563 | /* |
| @@ -109688,10 +109890,11 @@ | |
| 109890 | int nOBSat; /* Number of ORDER BY terms satisfied by indices */ |
| 109891 | int iECursor; /* Cursor number for the sorter */ |
| 109892 | int regReturn; /* Register holding block-output return address */ |
| 109893 | int labelBkOut; /* Start label for the block-output subroutine */ |
| 109894 | int addrSortIndex; /* Address of the OP_SorterOpen or OP_OpenEphemeral */ |
| 109895 | int labelDone; /* Jump here when done, ex: LIMIT reached */ |
| 109896 | u8 sortFlags; /* Zero or more SORTFLAG_* bits */ |
| 109897 | }; |
| 109898 | #define SORTFLAG_UseSorter 0x01 /* Use SorterOpen instead of OpenEphemeral */ |
| 109899 | |
| 109900 | /* |
| @@ -109745,33 +109948,41 @@ | |
| 109948 | Expr *pOffset /* OFFSET value. NULL means no offset */ |
| 109949 | ){ |
| 109950 | Select *pNew; |
| 109951 | Select standin; |
| 109952 | sqlite3 *db = pParse->db; |
| 109953 | pNew = sqlite3DbMallocRaw(db, sizeof(*pNew) ); |
| 109954 | if( pNew==0 ){ |
| 109955 | assert( db->mallocFailed ); |
| 109956 | pNew = &standin; |
| 109957 | } |
| 109958 | if( pEList==0 ){ |
| 109959 | pEList = sqlite3ExprListAppend(pParse, 0, sqlite3Expr(db,TK_ASTERISK,0)); |
| 109960 | } |
| 109961 | pNew->pEList = pEList; |
| 109962 | pNew->op = TK_SELECT; |
| 109963 | pNew->selFlags = selFlags; |
| 109964 | pNew->iLimit = 0; |
| 109965 | pNew->iOffset = 0; |
| 109966 | #if SELECTTRACE_ENABLED |
| 109967 | pNew->zSelName[0] = 0; |
| 109968 | #endif |
| 109969 | pNew->addrOpenEphm[0] = -1; |
| 109970 | pNew->addrOpenEphm[1] = -1; |
| 109971 | pNew->nSelectRow = 0; |
| 109972 | if( pSrc==0 ) pSrc = sqlite3DbMallocZero(db, sizeof(*pSrc)); |
| 109973 | pNew->pSrc = pSrc; |
| 109974 | pNew->pWhere = pWhere; |
| 109975 | pNew->pGroupBy = pGroupBy; |
| 109976 | pNew->pHaving = pHaving; |
| 109977 | pNew->pOrderBy = pOrderBy; |
| 109978 | pNew->pPrior = 0; |
| 109979 | pNew->pNext = 0; |
| 109980 | pNew->pLimit = pLimit; |
| 109981 | pNew->pOffset = pOffset; |
| 109982 | pNew->pWith = 0; |
| 109983 | assert( pOffset==0 || pLimit!=0 || pParse->nErr>0 || db->mallocFailed!=0 ); |
| 109984 | if( db->mallocFailed ) { |
| 109985 | clearSelect(db, pNew, pNew!=&standin); |
| 109986 | pNew = 0; |
| 109987 | }else{ |
| 109988 | assert( pNew->pSrc!=0 || pParse->nErr>0 ); |
| @@ -110142,10 +110353,11 @@ | |
| 110353 | int nBase = nExpr + bSeq + nData; /* Fields in sorter record */ |
| 110354 | int regBase; /* Regs for sorter record */ |
| 110355 | int regRecord = ++pParse->nMem; /* Assembled sorter record */ |
| 110356 | int nOBSat = pSort->nOBSat; /* ORDER BY terms to skip */ |
| 110357 | int op; /* Opcode to add sorter record to sorter */ |
| 110358 | int iLimit; /* LIMIT counter */ |
| 110359 | |
| 110360 | assert( bSeq==0 || bSeq==1 ); |
| 110361 | assert( nData==1 || regData==regOrigData ); |
| 110362 | if( nPrefixReg ){ |
| 110363 | assert( nPrefixReg==nExpr+bSeq ); |
| @@ -110152,19 +110364,21 @@ | |
| 110364 | regBase = regData - nExpr - bSeq; |
| 110365 | }else{ |
| 110366 | regBase = pParse->nMem + 1; |
| 110367 | pParse->nMem += nBase; |
| 110368 | } |
| 110369 | assert( pSelect->iOffset==0 || pSelect->iLimit!=0 ); |
| 110370 | iLimit = pSelect->iOffset ? pSelect->iOffset+1 : pSelect->iLimit; |
| 110371 | pSort->labelDone = sqlite3VdbeMakeLabel(v); |
| 110372 | sqlite3ExprCodeExprList(pParse, pSort->pOrderBy, regBase, regOrigData, |
| 110373 | SQLITE_ECEL_DUP|SQLITE_ECEL_REF); |
| 110374 | if( bSeq ){ |
| 110375 | sqlite3VdbeAddOp2(v, OP_Sequence, pSort->iECursor, regBase+nExpr); |
| 110376 | } |
| 110377 | if( nPrefixReg==0 ){ |
| 110378 | sqlite3ExprCodeMove(pParse, regData, regBase+nExpr+bSeq, nData); |
| 110379 | } |
| 110380 | sqlite3VdbeAddOp3(v, OP_MakeRecord, regBase+nOBSat, nBase-nOBSat, regRecord); |
| 110381 | if( nOBSat>0 ){ |
| 110382 | int regPrevKey; /* The first nOBSat columns of the previous row */ |
| 110383 | int addrFirst; /* Address of the OP_IfNot opcode */ |
| 110384 | int addrJmp; /* Address of the OP_Jump opcode */ |
| @@ -110195,10 +110409,14 @@ | |
| 110409 | sqlite3VdbeAddOp3(v, OP_Jump, addrJmp+1, 0, addrJmp+1); VdbeCoverage(v); |
| 110410 | pSort->labelBkOut = sqlite3VdbeMakeLabel(v); |
| 110411 | pSort->regReturn = ++pParse->nMem; |
| 110412 | sqlite3VdbeAddOp2(v, OP_Gosub, pSort->regReturn, pSort->labelBkOut); |
| 110413 | sqlite3VdbeAddOp1(v, OP_ResetSorter, pSort->iECursor); |
| 110414 | if( iLimit ){ |
| 110415 | sqlite3VdbeAddOp2(v, OP_IfNot, iLimit, pSort->labelDone); |
| 110416 | VdbeCoverage(v); |
| 110417 | } |
| 110418 | sqlite3VdbeJumpHere(v, addrFirst); |
| 110419 | sqlite3ExprCodeMove(pParse, regBase, regPrevKey, pSort->nOBSat); |
| 110420 | sqlite3VdbeJumpHere(v, addrJmp); |
| 110421 | } |
| 110422 | if( pSort->sortFlags & SORTFLAG_UseSorter ){ |
| @@ -110205,18 +110423,12 @@ | |
| 110423 | op = OP_SorterInsert; |
| 110424 | }else{ |
| 110425 | op = OP_IdxInsert; |
| 110426 | } |
| 110427 | sqlite3VdbeAddOp2(v, op, pSort->iECursor, regRecord); |
| 110428 | if( iLimit ){ |
| 110429 | int addr; |
| 110430 | addr = sqlite3VdbeAddOp3(v, OP_IfNotZero, iLimit, 0, 1); VdbeCoverage(v); |
| 110431 | sqlite3VdbeAddOp1(v, OP_Last, pSort->iECursor); |
| 110432 | sqlite3VdbeAddOp1(v, OP_Delete, pSort->iECursor); |
| 110433 | sqlite3VdbeJumpHere(v, addr); |
| 110434 | } |
| @@ -110816,11 +111028,11 @@ | |
| 111028 | SortCtx *pSort, /* Information on the ORDER BY clause */ |
| 111029 | int nColumn, /* Number of columns of data */ |
| 111030 | SelectDest *pDest /* Write the sorted results here */ |
| 111031 | ){ |
| 111032 | Vdbe *v = pParse->pVdbe; /* The prepared statement */ |
| 111033 | int addrBreak = pSort->labelDone; /* Jump here to exit loop */ |
| 111034 | int addrContinue = sqlite3VdbeMakeLabel(v); /* Jump here for next cycle */ |
| 111035 | int addr; |
| 111036 | int addrOnce = 0; |
| 111037 | int iTab; |
| 111038 | ExprList *pOrderBy = pSort->pOrderBy; |
| @@ -110835,10 +111047,11 @@ | |
| 111047 | int bSeq; /* True if sorter record includes seq. no. */ |
| 111048 | #ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS |
| 111049 | struct ExprList_item *aOutEx = p->pEList->a; |
| 111050 | #endif |
| 111051 | |
| 111052 | assert( addrBreak<0 ); |
| 111053 | if( pSort->labelBkOut ){ |
| 111054 | sqlite3VdbeAddOp2(v, OP_Gosub, pSort->regReturn, pSort->labelBkOut); |
| 111055 | sqlite3VdbeGoto(v, addrBreak); |
| 111056 | sqlite3VdbeResolveLabel(v, pSort->labelBkOut); |
| 111057 | } |
| @@ -126875,12 +127088,11 @@ | |
| 127088 | testcase( pWInfo->eOnePass==ONEPASS_OFF && pTab->nCol==BMS ); |
| 127089 | if( pWInfo->eOnePass==ONEPASS_OFF && pTab->nCol<BMS && HasRowid(pTab) ){ |
| 127090 | Bitmask b = pTabItem->colUsed; |
| 127091 | int n = 0; |
| 127092 | for(; b; b=b>>1, n++){} |
| 127093 | sqlite3VdbeChangeP4(v, -1, SQLITE_INT_TO_PTR(n), P4_INT32); |
| 127094 | assert( n<=pTab->nCol ); |
| 127095 | } |
| 127096 | #ifdef SQLITE_ENABLE_CURSOR_HINTS |
| 127097 | if( pLoop->u.btree.pIndex!=0 ){ |
| 127098 | sqlite3VdbeChangeP5(v, OPFLAG_SEEKEQ|bFordelete); |
| @@ -129343,18 +129555,15 @@ | |
| 129555 | ** { ... } // User supplied code |
| 129556 | ** #line <lineno> <thisfile> |
| 129557 | ** break; |
| 129558 | */ |
| 129559 | /********** Begin reduce actions **********************************************/ |
| 129560 | case 6: /* explain ::= EXPLAIN */ |
| 129561 | { pParse->explain = 1; } |
| 129562 | break; |
| 129563 | case 7: /* explain ::= EXPLAIN QUERY PLAN */ |
| 129564 | { pParse->explain = 2; } |
| 129565 | break; |
| 129566 | case 8: /* cmdx ::= cmd */ |
| 129567 | { sqlite3FinishCoding(pParse); } |
| 129568 | break; |
| 129569 | case 9: /* cmd ::= BEGIN transtype trans_opt */ |
| @@ -130525,10 +130734,11 @@ | |
| 130734 | /* (0) input ::= cmdlist */ yytestcase(yyruleno==0); |
| 130735 | /* (1) cmdlist ::= cmdlist ecmd */ yytestcase(yyruleno==1); |
| 130736 | /* (2) cmdlist ::= ecmd */ yytestcase(yyruleno==2); |
| 130737 | /* (3) ecmd ::= SEMI */ yytestcase(yyruleno==3); |
| 130738 | /* (4) ecmd ::= explain cmdx SEMI */ yytestcase(yyruleno==4); |
| 130739 | /* (5) explain ::= */ yytestcase(yyruleno==5); |
| 130740 | /* (10) trans_opt ::= */ yytestcase(yyruleno==10); |
| 130741 | /* (11) trans_opt ::= TRANSACTION */ yytestcase(yyruleno==11); |
| 130742 | /* (12) trans_opt ::= TRANSACTION nm */ yytestcase(yyruleno==12); |
| 130743 | /* (20) savepoint_opt ::= SAVEPOINT */ yytestcase(yyruleno==20); |
| 130744 | /* (21) savepoint_opt ::= */ yytestcase(yyruleno==21); |
| @@ -135450,10 +135660,13 @@ | |
| 135660 | *(sqlite3_file**)pArg = fd; |
| 135661 | rc = SQLITE_OK; |
| 135662 | }else if( op==SQLITE_FCNTL_VFS_POINTER ){ |
| 135663 | *(sqlite3_vfs**)pArg = sqlite3PagerVfs(pPager); |
| 135664 | rc = SQLITE_OK; |
| 135665 | }else if( op==SQLITE_FCNTL_JOURNAL_POINTER ){ |
| 135666 | *(sqlite3_file**)pArg = sqlite3PagerJrnlFile(pPager); |
| 135667 | rc = SQLITE_OK; |
| 135668 | }else if( fd->pMethods ){ |
| 135669 | rc = sqlite3OsFileControl(fd, op, pArg); |
| 135670 | }else{ |
| 135671 | rc = SQLITE_NOTFOUND; |
| 135672 | } |
| @@ -161083,11 +161296,11 @@ | |
| 161296 | */ |
| 161297 | static void *rbuMalloc(sqlite3rbu *p, int nByte){ |
| 161298 | void *pRet = 0; |
| 161299 | if( p->rc==SQLITE_OK ){ |
| 161300 | assert( nByte>0 ); |
| 161301 | pRet = sqlite3_malloc64(nByte); |
| 161302 | if( pRet==0 ){ |
| 161303 | p->rc = SQLITE_NOMEM; |
| 161304 | }else{ |
| 161305 | memset(pRet, 0, nByte); |
| 161306 | } |
| @@ -161129,12 +161342,12 @@ | |
| 161342 | static char *rbuStrndup(const char *zStr, int *pRc){ |
| 161343 | char *zRet = 0; |
| 161344 | |
| 161345 | assert( *pRc==SQLITE_OK ); |
| 161346 | if( zStr ){ |
| 161347 | size_t nCopy = strlen(zStr) + 1; |
| 161348 | zRet = (char*)sqlite3_malloc64(nCopy); |
| 161349 | if( zRet ){ |
| 161350 | memcpy(zRet, zStr, nCopy); |
| 161351 | }else{ |
| 161352 | *pRc = SQLITE_NOMEM; |
| 161353 | } |
| @@ -162478,11 +162691,11 @@ | |
| 162691 | |
| 162692 | pRbu->pgsz = iAmt; |
| 162693 | if( pRbu->nFrame==pRbu->nFrameAlloc ){ |
| 162694 | int nNew = (pRbu->nFrameAlloc ? pRbu->nFrameAlloc : 64) * 2; |
| 162695 | RbuFrame *aNew; |
| 162696 | aNew = (RbuFrame*)sqlite3_realloc64(pRbu->aFrame, nNew * sizeof(RbuFrame)); |
| 162697 | if( aNew==0 ) return SQLITE_NOMEM; |
| 162698 | pRbu->aFrame = aNew; |
| 162699 | pRbu->nFrameAlloc = nNew; |
| 162700 | } |
| 162701 | |
| @@ -162543,11 +162756,11 @@ | |
| 162756 | |
| 162757 | nChar = MultiByteToWideChar(CP_UTF8, 0, zFilename, -1, NULL, 0); |
| 162758 | if( nChar==0 ){ |
| 162759 | return 0; |
| 162760 | } |
| 162761 | zWideFilename = sqlite3_malloc64( nChar*sizeof(zWideFilename[0]) ); |
| 162762 | if( zWideFilename==0 ){ |
| 162763 | return 0; |
| 162764 | } |
| 162765 | memset(zWideFilename, 0, nChar*sizeof(zWideFilename[0])); |
| 162766 | nChar = MultiByteToWideChar(CP_UTF8, 0, zFilename, -1, zWideFilename, |
| @@ -163177,15 +163390,16 @@ | |
| 163390 | const char *zTarget, |
| 163391 | const char *zRbu, |
| 163392 | const char *zState |
| 163393 | ){ |
| 163394 | sqlite3rbu *p; |
| 163395 | size_t nTarget = strlen(zTarget); |
| 163396 | size_t nRbu = strlen(zRbu); |
| 163397 | size_t nState = zState ? strlen(zState) : 0; |
| 163398 | size_t nByte = sizeof(sqlite3rbu) + nTarget+1 + nRbu+1+ nState+1; |
| 163399 | |
| 163400 | p = (sqlite3rbu*)sqlite3_malloc64(nByte); |
| 163401 | if( p ){ |
| 163402 | RbuState *pState = 0; |
| 163403 | |
| 163404 | /* Create the custom VFS. */ |
| 163405 | memset(p, 0, sizeof(sqlite3rbu)); |
| @@ -163318,11 +163532,11 @@ | |
| 163532 | ** the pattern "rbu_imp_[0-9]*". |
| 163533 | */ |
| 163534 | static void rbuEditErrmsg(sqlite3rbu *p){ |
| 163535 | if( p->rc==SQLITE_CONSTRAINT && p->zErrmsg ){ |
| 163536 | int i; |
| 163537 | size_t nErrmsg = strlen(p->zErrmsg); |
| 163538 | for(i=0; i<(nErrmsg-8); i++){ |
| 163539 | if( memcmp(&p->zErrmsg[i], "rbu_imp_", 8)==0 ){ |
| 163540 | int nDel = 8; |
| 163541 | while( p->zErrmsg[i+nDel]>='0' && p->zErrmsg[i+nDel]<='9' ) nDel++; |
| 163542 | memmove(&p->zErrmsg[i], &p->zErrmsg[i+nDel], nErrmsg + 1 - i - nDel); |
| @@ -163782,11 +163996,11 @@ | |
| 163996 | ** instead of a file on disk. */ |
| 163997 | assert( p->openFlags & (SQLITE_OPEN_MAIN_DB|SQLITE_OPEN_TEMP_DB) ); |
| 163998 | if( eStage==RBU_STAGE_OAL || eStage==RBU_STAGE_MOVE ){ |
| 163999 | if( iRegion<=p->nShm ){ |
| 164000 | int nByte = (iRegion+1) * sizeof(char*); |
| 164001 | char **apNew = (char**)sqlite3_realloc64(p->apShm, nByte); |
| 164002 | if( apNew==0 ){ |
| 164003 | rc = SQLITE_NOMEM; |
| 164004 | }else{ |
| 164005 | memset(&apNew[p->nShm], 0, sizeof(char*) * (1 + iRegion - p->nShm)); |
| 164006 | p->apShm = apNew; |
| @@ -163793,11 +164007,11 @@ | |
| 164007 | p->nShm = iRegion+1; |
| 164008 | } |
| 164009 | } |
| 164010 | |
| 164011 | if( rc==SQLITE_OK && p->apShm[iRegion]==0 ){ |
| 164012 | char *pNew = (char*)sqlite3_malloc64(szRegion); |
| 164013 | if( pNew==0 ){ |
| 164014 | rc = SQLITE_NOMEM; |
| 164015 | }else{ |
| 164016 | memset(pNew, 0, szRegion); |
| 164017 | p->apShm[iRegion] = pNew; |
| @@ -163903,11 +164117,11 @@ | |
| 164117 | /* A main database has just been opened. The following block sets |
| 164118 | ** (pFd->zWal) to point to a buffer owned by SQLite that contains |
| 164119 | ** the name of the *-wal file this db connection will use. SQLite |
| 164120 | ** happens to pass a pointer to this buffer when using xAccess() |
| 164121 | ** or xOpen() to operate on the *-wal file. */ |
| 164122 | int n = (int)strlen(zName); |
| 164123 | const char *z = &zName[n]; |
| 164124 | if( flags & SQLITE_OPEN_URI ){ |
| 164125 | int odd = 0; |
| 164126 | while( 1 ){ |
| 164127 | if( z[0]==0 ){ |
| @@ -163929,12 +164143,12 @@ | |
| 164143 | if( pDb->pRbu && pDb->pRbu->eStage==RBU_STAGE_OAL ){ |
| 164144 | /* This call is to open a *-wal file. Intead, open the *-oal. This |
| 164145 | ** code ensures that the string passed to xOpen() is terminated by a |
| 164146 | ** pair of '\0' bytes in case the VFS attempts to extract a URI |
| 164147 | ** parameter from it. */ |
| 164148 | size_t nCopy = strlen(zName); |
| 164149 | char *zCopy = sqlite3_malloc64(nCopy+2); |
| 164150 | if( zCopy ){ |
| 164151 | memcpy(zCopy, zName, nCopy); |
| 164152 | zCopy[nCopy-3] = 'o'; |
| 164153 | zCopy[nCopy] = '\0'; |
| 164154 | zCopy[nCopy+1] = '\0'; |
| @@ -164159,17 +164373,17 @@ | |
| 164373 | 0, /* xCurrentTimeInt64 (version 2) */ |
| 164374 | 0, 0, 0 /* Unimplemented version 3 methods */ |
| 164375 | }; |
| 164376 | |
| 164377 | rbu_vfs *pNew = 0; /* Newly allocated VFS */ |
| 164378 | int rc = SQLITE_OK; |
| 164379 | size_t nName; |
| 164380 | size_t nByte; |
| 164381 | |
| 164382 | nName = strlen(zName); |
| 164383 | nByte = sizeof(rbu_vfs) + nName + 1; |
| 164384 | pNew = (rbu_vfs*)sqlite3_malloc64(nByte); |
| 164385 | if( pNew==0 ){ |
| 164386 | rc = SQLITE_NOMEM; |
| 164387 | }else{ |
| 164388 | sqlite3_vfs *pParent; /* Parent VFS */ |
| 164389 | memset(pNew, 0, nByte); |
| @@ -167174,10 +167388,13 @@ | |
| 167388 | ** If parameter iCol is greater than or equal to the number of columns |
| 167389 | ** in the table, SQLITE_RANGE is returned. Or, if an error occurs (e.g. |
| 167390 | ** an OOM condition or IO error), an appropriate SQLite error code is |
| 167391 | ** returned. |
| 167392 | ** |
| 167393 | ** This function may be quite inefficient if used with an FTS5 table |
| 167394 | ** created with the "columnsize=0" option. |
| 167395 | ** |
| 167396 | ** xColumnText: |
| 167397 | ** This function attempts to retrieve the text of column iCol of the |
| 167398 | ** current document. If successful, (*pz) is set to point to a buffer |
| 167399 | ** containing the text in utf-8 encoding, (*pn) is set to the size in bytes |
| 167400 | ** (not characters) of the buffer and SQLITE_OK is returned. Otherwise, |
| @@ -167194,18 +167411,32 @@ | |
| 167411 | ** xInstCount: |
| 167412 | ** Set *pnInst to the total number of occurrences of all phrases within |
| 167413 | ** the query within the current row. Return SQLITE_OK if successful, or |
| 167414 | ** an error code (i.e. SQLITE_NOMEM) if an error occurs. |
| 167415 | ** |
| 167416 | ** This API can be quite slow if used with an FTS5 table created with the |
| 167417 | ** "detail=none" or "detail=column" option. If the FTS5 table is created |
| 167418 | ** with either "detail=none" or "detail=column" and "content=" option |
| 167419 | ** (i.e. if it is a contentless table), then this API always returns 0. |
| 167420 | ** |
| 167421 | ** xInst: |
| 167422 | ** Query for the details of phrase match iIdx within the current row. |
| 167423 | ** Phrase matches are numbered starting from zero, so the iIdx argument |
| 167424 | ** should be greater than or equal to zero and smaller than the value |
| 167425 | ** output by xInstCount(). |
| 167426 | ** |
| 167427 | ** Usually, output parameter *piPhrase is set to the phrase number, *piCol |
| 167428 | ** to the column in which it occurs and *piOff the token offset of the |
| 167429 | ** first token of the phrase. The exception is if the table was created |
| 167430 | ** with the offsets=0 option specified. In this case *piOff is always |
| 167431 | ** set to -1. |
| 167432 | ** |
| 167433 | ** Returns SQLITE_OK if successful, or an error code (i.e. SQLITE_NOMEM) |
| 167434 | ** if an error occurs. |
| 167435 | ** |
| 167436 | ** This API can be quite slow if used with an FTS5 table created with the |
| 167437 | ** "detail=none" or "detail=column" option. |
| 167438 | ** |
| 167439 | ** xRowid: |
| 167440 | ** Returns the rowid of the current row. |
| 167441 | ** |
| 167442 | ** xTokenize: |
| @@ -167286,25 +167517,63 @@ | |
| 167517 | ** through instances of phrase iPhrase, use the following code: |
| 167518 | ** |
| 167519 | ** Fts5PhraseIter iter; |
| 167520 | ** int iCol, iOff; |
| 167521 | ** for(pApi->xPhraseFirst(pFts, iPhrase, &iter, &iCol, &iOff); |
| 167522 | ** iCol>=0; |
| 167523 | ** pApi->xPhraseNext(pFts, &iter, &iCol, &iOff) |
| 167524 | ** ){ |
| 167525 | ** // An instance of phrase iPhrase at offset iOff of column iCol |
| 167526 | ** } |
| 167527 | ** |
| 167528 | ** The Fts5PhraseIter structure is defined above. Applications should not |
| 167529 | ** modify this structure directly - it should only be used as shown above |
| 167530 | ** with the xPhraseFirst() and xPhraseNext() API methods (and by |
| 167531 | ** xPhraseFirstColumn() and xPhraseNextColumn() as illustrated below). |
| 167532 | ** |
| 167533 | ** This API can be quite slow if used with an FTS5 table created with the |
| 167534 | ** "detail=none" or "detail=column" option. If the FTS5 table is created |
| 167535 | ** with either "detail=none" or "detail=column" and "content=" option |
| 167536 | ** (i.e. if it is a contentless table), then this API always iterates |
| 167537 | ** through an empty set (all calls to xPhraseFirst() set iCol to -1). |
| 167538 | ** |
| 167539 | ** xPhraseNext() |
| 167540 | ** See xPhraseFirst above. |
| 167541 | ** |
| 167542 | ** xPhraseFirstColumn() |
| 167543 | ** This function and xPhraseNextColumn() are similar to the xPhraseFirst() |
| 167544 | ** and xPhraseNext() APIs described above. The difference is that instead |
| 167545 | ** of iterating through all instances of a phrase in the current row, these |
| 167546 | ** APIs are used to iterate through the set of columns in the current row |
| 167547 | ** that contain one or more instances of a specified phrase. For example: |
| 167548 | ** |
| 167549 | ** Fts5PhraseIter iter; |
| 167550 | ** int iCol; |
| 167551 | ** for(pApi->xPhraseFirstColumn(pFts, iPhrase, &iter, &iCol); |
| 167552 | ** iCol>=0; |
| 167553 | ** pApi->xPhraseNextColumn(pFts, &iter, &iCol) |
| 167554 | ** ){ |
| 167555 | ** // Column iCol contains at least one instance of phrase iPhrase |
| 167556 | ** } |
| 167557 | ** |
| 167558 | ** This API can be quite slow if used with an FTS5 table created with the |
| 167559 | ** "detail=none" option. If the FTS5 table is created with either |
| 167560 | ** "detail=none" "content=" option (i.e. if it is a contentless table), |
| 167561 | ** then this API always iterates through an empty set (all calls to |
| 167562 | ** xPhraseFirstColumn() set iCol to -1). |
| 167563 | ** |
| 167564 | ** The information accessed using this API and its companion |
| 167565 | ** xPhraseFirstColumn() may also be obtained using xPhraseFirst/xPhraseNext |
| 167566 | ** (or xInst/xInstCount). The chief advantage of this API is that it is |
| 167567 | ** significantly more efficient than those alternatives when used with |
| 167568 | ** "detail=column" tables. |
| 167569 | ** |
| 167570 | ** xPhraseNextColumn() |
| 167571 | ** See xPhraseFirstColumn above. |
| 167572 | */ |
| 167573 | struct Fts5ExtensionApi { |
| 167574 | int iVersion; /* Currently always set to 3 */ |
| 167575 | |
| 167576 | void *(*xUserData)(Fts5Context*); |
| 167577 | |
| 167578 | int (*xColumnCount)(Fts5Context*); |
| 167579 | int (*xRowCount)(Fts5Context*, sqlite3_int64 *pnRow); |
| @@ -167330,12 +167599,15 @@ | |
| 167599 | int(*)(const Fts5ExtensionApi*,Fts5Context*,void*) |
| 167600 | ); |
| 167601 | int (*xSetAuxdata)(Fts5Context*, void *pAux, void(*xDelete)(void*)); |
| 167602 | void *(*xGetAuxdata)(Fts5Context*, int bClear); |
| 167603 | |
| 167604 | int (*xPhraseFirst)(Fts5Context*, int iPhrase, Fts5PhraseIter*, int*, int*); |
| 167605 | void (*xPhraseNext)(Fts5Context*, Fts5PhraseIter*, int *piCol, int *piOff); |
| 167606 | |
| 167607 | int (*xPhraseFirstColumn)(Fts5Context*, int iPhrase, Fts5PhraseIter*, int*); |
| 167608 | void (*xPhraseNextColumn)(Fts5Context*, Fts5PhraseIter*, int *piCol); |
| 167609 | }; |
| 167610 | |
| 167611 | /* |
| 167612 | ** CUSTOM AUXILIARY FUNCTIONS |
| 167613 | *************************************************************************/ |
| @@ -167762,10 +168034,11 @@ | |
| 168034 | int *aPrefix; /* Sizes in bytes of nPrefix prefix indexes */ |
| 168035 | int eContent; /* An FTS5_CONTENT value */ |
| 168036 | char *zContent; /* content table */ |
| 168037 | char *zContentRowid; /* "content_rowid=" option value */ |
| 168038 | int bColumnsize; /* "columnsize=" option value (dflt==1) */ |
| 168039 | int eDetail; /* FTS5_DETAIL_XXX value */ |
| 168040 | char *zContentExprlist; |
| 168041 | Fts5Tokenizer *pTok; |
| 168042 | fts5_tokenizer *pTokApi; |
| 168043 | |
| 168044 | /* Values loaded from the %_config table */ |
| @@ -167790,10 +168063,13 @@ | |
| 168063 | |
| 168064 | #define FTS5_CONTENT_NORMAL 0 |
| 168065 | #define FTS5_CONTENT_NONE 1 |
| 168066 | #define FTS5_CONTENT_EXTERNAL 2 |
| 168067 | |
| 168068 | #define FTS5_DETAIL_FULL 0 |
| 168069 | #define FTS5_DETAIL_NONE 1 |
| 168070 | #define FTS5_DETAIL_COLUMNS 2 |
| 168071 | |
| 168072 | |
| 168073 | |
| 168074 | static int sqlite3Fts5ConfigParse( |
| 168075 | Fts5Global*, sqlite3*, int, const char **, Fts5Config**, char** |
| @@ -167903,10 +168179,17 @@ | |
| 168179 | static char *sqlite3Fts5Strndup(int *pRc, const char *pIn, int nIn); |
| 168180 | |
| 168181 | /* Character set tests (like isspace(), isalpha() etc.) */ |
| 168182 | static int sqlite3Fts5IsBareword(char t); |
| 168183 | |
| 168184 | |
| 168185 | /* Bucket of terms object used by the integrity-check in offsets=0 mode. */ |
| 168186 | typedef struct Fts5Termset Fts5Termset; |
| 168187 | static int sqlite3Fts5TermsetNew(Fts5Termset**); |
| 168188 | static int sqlite3Fts5TermsetAdd(Fts5Termset*, int, const char*, int, int *pbPresent); |
| 168189 | static void sqlite3Fts5TermsetFree(Fts5Termset*); |
| 168190 | |
| 168191 | /* |
| 168192 | ** End of interface to code in fts5_buffer.c. |
| 168193 | **************************************************************************/ |
| 168194 | |
| 168195 | /************************************************************************** |
| @@ -168024,11 +168307,10 @@ | |
| 168307 | static int sqlite3Fts5IndexSetAverages(Fts5Index *p, const u8*, int); |
| 168308 | |
| 168309 | /* |
| 168310 | ** Functions called by the storage module as part of integrity-check. |
| 168311 | */ |
| 168312 | static int sqlite3Fts5IndexIntegrityCheck(Fts5Index*, u64 cksum); |
| 168313 | |
| 168314 | /* |
| 168315 | ** Called during virtual module initialization to register UDF |
| 168316 | ** fts5_decode() with SQLite |
| @@ -168046,10 +168328,12 @@ | |
| 168328 | static int sqlite3Fts5IndexReinit(Fts5Index *p); |
| 168329 | static int sqlite3Fts5IndexOptimize(Fts5Index *p); |
| 168330 | static int sqlite3Fts5IndexMerge(Fts5Index *p, int nMerge); |
| 168331 | |
| 168332 | static int sqlite3Fts5IndexLoadConfig(Fts5Index *p); |
| 168333 | |
| 168334 | static int sqlite3Fts5IterCollist(Fts5IndexIter*, const u8 **, int*); |
| 168335 | |
| 168336 | /* |
| 168337 | ** End of interface to code in fts5_index.c. |
| 168338 | **************************************************************************/ |
| 168339 | |
| @@ -168103,11 +168387,11 @@ | |
| 168387 | typedef struct Fts5Hash Fts5Hash; |
| 168388 | |
| 168389 | /* |
| 168390 | ** Create a hash table, free a hash table. |
| 168391 | */ |
| 168392 | static int sqlite3Fts5HashNew(Fts5Config*, Fts5Hash**, int *pnSize); |
| 168393 | static void sqlite3Fts5HashFree(Fts5Hash*); |
| 168394 | |
| 168395 | static int sqlite3Fts5HashWrite( |
| 168396 | Fts5Hash*, |
| 168397 | i64 iRowid, /* Rowid for this entry */ |
| @@ -168239,12 +168523,22 @@ | |
| 168523 | static int sqlite3Fts5ExprInit(Fts5Global*, sqlite3*); |
| 168524 | |
| 168525 | static int sqlite3Fts5ExprPhraseCount(Fts5Expr*); |
| 168526 | static int sqlite3Fts5ExprPhraseSize(Fts5Expr*, int iPhrase); |
| 168527 | static int sqlite3Fts5ExprPoslist(Fts5Expr*, int, const u8 **); |
| 168528 | |
| 168529 | typedef struct Fts5PoslistPopulator Fts5PoslistPopulator; |
| 168530 | static Fts5PoslistPopulator *sqlite3Fts5ExprClearPoslists(Fts5Expr*, int); |
| 168531 | static int sqlite3Fts5ExprPopulatePoslists( |
| 168532 | Fts5Config*, Fts5Expr*, Fts5PoslistPopulator*, int, const char*, int |
| 168533 | ); |
| 168534 | static void sqlite3Fts5ExprCheckPoslists(Fts5Expr*, i64); |
| 168535 | static void sqlite3Fts5ExprClearEof(Fts5Expr*); |
| 168536 | |
| 168537 | static int sqlite3Fts5ExprClonePhrase(Fts5Config*, Fts5Expr*, int, Fts5Expr**); |
| 168538 | |
| 168539 | static int sqlite3Fts5ExprPhraseCollist(Fts5Expr *, int, const u8 **, int *); |
| 168540 | |
| 168541 | /******************************************* |
| 168542 | ** The fts5_expr.c API above this point is used by the other hand-written |
| 168543 | ** C code in this module. The interfaces below this point are called by |
| 168544 | ** the parser code in fts5parse.y. */ |
| @@ -170397,10 +170691,93 @@ | |
| 170691 | |
| 170692 | return (t & 0x80) || aBareword[(int)t]; |
| 170693 | } |
| 170694 | |
| 170695 | |
| 170696 | /************************************************************************* |
| 170697 | */ |
| 170698 | typedef struct Fts5TermsetEntry Fts5TermsetEntry; |
| 170699 | struct Fts5TermsetEntry { |
| 170700 | char *pTerm; |
| 170701 | int nTerm; |
| 170702 | int iIdx; /* Index (main or aPrefix[] entry) */ |
| 170703 | Fts5TermsetEntry *pNext; |
| 170704 | }; |
| 170705 | |
| 170706 | struct Fts5Termset { |
| 170707 | Fts5TermsetEntry *apHash[512]; |
| 170708 | }; |
| 170709 | |
| 170710 | static int sqlite3Fts5TermsetNew(Fts5Termset **pp){ |
| 170711 | int rc = SQLITE_OK; |
| 170712 | *pp = sqlite3Fts5MallocZero(&rc, sizeof(Fts5Termset)); |
| 170713 | return rc; |
| 170714 | } |
| 170715 | |
| 170716 | static int sqlite3Fts5TermsetAdd( |
| 170717 | Fts5Termset *p, |
| 170718 | int iIdx, |
| 170719 | const char *pTerm, int nTerm, |
| 170720 | int *pbPresent |
| 170721 | ){ |
| 170722 | int rc = SQLITE_OK; |
| 170723 | *pbPresent = 0; |
| 170724 | if( p ){ |
| 170725 | int i; |
| 170726 | int hash; |
| 170727 | Fts5TermsetEntry *pEntry; |
| 170728 | |
| 170729 | /* Calculate a hash value for this term */ |
| 170730 | hash = 104 + iIdx; |
| 170731 | for(i=0; i<nTerm; i++){ |
| 170732 | hash += (hash << 3) + (int)pTerm[i]; |
| 170733 | } |
| 170734 | hash = hash % ArraySize(p->apHash); |
| 170735 | |
| 170736 | for(pEntry=p->apHash[hash]; pEntry; pEntry=pEntry->pNext){ |
| 170737 | if( pEntry->iIdx==iIdx |
| 170738 | && pEntry->nTerm==nTerm |
| 170739 | && memcmp(pEntry->pTerm, pTerm, nTerm)==0 |
| 170740 | ){ |
| 170741 | *pbPresent = 1; |
| 170742 | break; |
| 170743 | } |
| 170744 | } |
| 170745 | |
| 170746 | if( pEntry==0 ){ |
| 170747 | pEntry = sqlite3Fts5MallocZero(&rc, sizeof(Fts5TermsetEntry) + nTerm); |
| 170748 | if( pEntry ){ |
| 170749 | pEntry->pTerm = (char*)&pEntry[1]; |
| 170750 | pEntry->nTerm = nTerm; |
| 170751 | pEntry->iIdx = iIdx; |
| 170752 | memcpy(pEntry->pTerm, pTerm, nTerm); |
| 170753 | pEntry->pNext = p->apHash[hash]; |
| 170754 | p->apHash[hash] = pEntry; |
| 170755 | } |
| 170756 | } |
| 170757 | } |
| 170758 | |
| 170759 | return rc; |
| 170760 | } |
| 170761 | |
| 170762 | static void sqlite3Fts5TermsetFree(Fts5Termset *p){ |
| 170763 | if( p ){ |
| 170764 | int i; |
| 170765 | for(i=0; i<ArraySize(p->apHash); i++){ |
| 170766 | Fts5TermsetEntry *pEntry = p->apHash[i]; |
| 170767 | while( pEntry ){ |
| 170768 | Fts5TermsetEntry *pDel = pEntry; |
| 170769 | pEntry = pEntry->pNext; |
| 170770 | sqlite3_free(pDel); |
| 170771 | } |
| 170772 | } |
| 170773 | sqlite3_free(p); |
| 170774 | } |
| 170775 | } |
| 170776 | |
| 170777 | |
| 170778 | |
| 170779 | |
| 170780 | /* |
| 170781 | ** 2014 Jun 09 |
| 170782 | ** |
| 170783 | ** The author disclaims copyright to this source code. In place of |
| @@ -170412,11 +170789,10 @@ | |
| 170789 | ** |
| 170790 | ****************************************************************************** |
| 170791 | ** |
| 170792 | ** This is an SQLite module implementing full-text search. |
| 170793 | */ |
| 170794 | |
| 170795 | |
| 170796 | /* #include "fts5Int.h" */ |
| 170797 | |
| 170798 | #define FTS5_DEFAULT_PAGE_SIZE 4050 |
| @@ -170595,10 +170971,37 @@ | |
| 170971 | if( quote=='[' || quote=='\'' || quote=='"' || quote=='`' ){ |
| 170972 | fts5Dequote(z); |
| 170973 | } |
| 170974 | } |
| 170975 | |
| 170976 | |
| 170977 | struct Fts5Enum { |
| 170978 | const char *zName; |
| 170979 | int eVal; |
| 170980 | }; |
| 170981 | typedef struct Fts5Enum Fts5Enum; |
| 170982 | |
| 170983 | static int fts5ConfigSetEnum( |
| 170984 | const Fts5Enum *aEnum, |
| 170985 | const char *zEnum, |
| 170986 | int *peVal |
| 170987 | ){ |
| 170988 | int nEnum = strlen(zEnum); |
| 170989 | int i; |
| 170990 | int iVal = -1; |
| 170991 | |
| 170992 | for(i=0; aEnum[i].zName; i++){ |
| 170993 | if( sqlite3_strnicmp(aEnum[i].zName, zEnum, nEnum)==0 ){ |
| 170994 | if( iVal>=0 ) return SQLITE_ERROR; |
| 170995 | iVal = aEnum[i].eVal; |
| 170996 | } |
| 170997 | } |
| 170998 | |
| 170999 | *peVal = iVal; |
| 171000 | return iVal<0 ? SQLITE_ERROR : SQLITE_OK; |
| 171001 | } |
| 171002 | |
| 171003 | /* |
| 171004 | ** Parse a "special" CREATE VIRTUAL TABLE directive and update |
| 171005 | ** configuration object pConfig as appropriate. |
| 171006 | ** |
| 171007 | ** If successful, object pConfig is updated and SQLITE_OK returned. If |
| @@ -170744,10 +171147,24 @@ | |
| 171147 | }else{ |
| 171148 | pConfig->bColumnsize = (zArg[0]=='1'); |
| 171149 | } |
| 171150 | return rc; |
| 171151 | } |
| 171152 | |
| 171153 | if( sqlite3_strnicmp("detail", zCmd, nCmd)==0 ){ |
| 171154 | const Fts5Enum aDetail[] = { |
| 171155 | { "none", FTS5_DETAIL_NONE }, |
| 171156 | { "full", FTS5_DETAIL_FULL }, |
| 171157 | { "columns", FTS5_DETAIL_COLUMNS }, |
| 171158 | { 0, 0 } |
| 171159 | }; |
| 171160 | |
| 171161 | if( (rc = fts5ConfigSetEnum(aDetail, zArg, &pConfig->eDetail)) ){ |
| 171162 | *pzErr = sqlite3_mprintf("malformed detail=... directive"); |
| 171163 | } |
| 171164 | return rc; |
| 171165 | } |
| 171166 | |
| 171167 | *pzErr = sqlite3_mprintf("unrecognized option: \"%.*s\"", nCmd, zCmd); |
| 171168 | return SQLITE_ERROR; |
| 171169 | } |
| 171170 | |
| @@ -170900,10 +171317,11 @@ | |
| 171317 | pRet->azCol = (char**)sqlite3Fts5MallocZero(&rc, nByte); |
| 171318 | pRet->abUnindexed = (u8*)&pRet->azCol[nArg]; |
| 171319 | pRet->zDb = sqlite3Fts5Strndup(&rc, azArg[1], -1); |
| 171320 | pRet->zName = sqlite3Fts5Strndup(&rc, azArg[2], -1); |
| 171321 | pRet->bColumnsize = 1; |
| 171322 | pRet->eDetail = FTS5_DETAIL_FULL; |
| 171323 | #ifdef SQLITE_DEBUG |
| 171324 | pRet->bPrefixIndex = 1; |
| 171325 | #endif |
| 171326 | if( rc==SQLITE_OK && sqlite3_stricmp(pRet->zName, FTS5_RANK_NAME)==0 ){ |
| 171327 | *pzErr = sqlite3_mprintf("reserved fts5 table name: %s", pRet->zName); |
| @@ -171346,10 +171764,11 @@ | |
| 171764 | #endif |
| 171765 | |
| 171766 | |
| 171767 | struct Fts5Expr { |
| 171768 | Fts5Index *pIndex; |
| 171769 | Fts5Config *pConfig; |
| 171770 | Fts5ExprNode *pRoot; |
| 171771 | int bDesc; /* Iterate in descending rowid order */ |
| 171772 | int nPhrase; /* Number of phrases in expression */ |
| 171773 | Fts5ExprPhrase **apExprPhrase; /* Pointers to phrase objects */ |
| 171774 | }; |
| @@ -171541,10 +171960,11 @@ | |
| 171960 | sParse.rc = SQLITE_NOMEM; |
| 171961 | sqlite3Fts5ParseNodeFree(sParse.pExpr); |
| 171962 | }else{ |
| 171963 | pNew->pRoot = sParse.pExpr; |
| 171964 | pNew->pIndex = 0; |
| 171965 | pNew->pConfig = pConfig; |
| 171966 | pNew->apExprPhrase = sParse.apPhrase; |
| 171967 | pNew->nPhrase = sParse.nPhrase; |
| 171968 | sParse.apPhrase = 0; |
| 171969 | } |
| 171970 | } |
| @@ -171605,12 +172025,13 @@ | |
| 172025 | } |
| 172026 | |
| 172027 | /* |
| 172028 | ** Argument pTerm must be a synonym iterator. |
| 172029 | */ |
| 172030 | static int fts5ExprSynonymList( |
| 172031 | Fts5ExprTerm *pTerm, |
| 172032 | int bCollist, |
| 172033 | Fts5Colset *pColset, |
| 172034 | i64 iRowid, |
| 172035 | int *pbDel, /* OUT: Caller should sqlite3_free(*pa) */ |
| 172036 | u8 **pa, int *pn |
| 172037 | ){ |
| @@ -171625,13 +172046,20 @@ | |
| 172046 | for(p=pTerm; p; p=p->pSynonym){ |
| 172047 | Fts5IndexIter *pIter = p->pIter; |
| 172048 | if( sqlite3Fts5IterEof(pIter)==0 && sqlite3Fts5IterRowid(pIter)==iRowid ){ |
| 172049 | const u8 *a; |
| 172050 | int n; |
| 172051 | |
| 172052 | if( bCollist ){ |
| 172053 | rc = sqlite3Fts5IterCollist(pIter, &a, &n); |
| 172054 | }else{ |
| 172055 | i64 dummy; |
| 172056 | rc = sqlite3Fts5IterPoslist(pIter, pColset, &a, &n, &dummy); |
| 172057 | } |
| 172058 | |
| 172059 | if( rc!=SQLITE_OK ) goto synonym_poslist_out; |
| 172060 | if( n==0 ) continue; |
| 172061 | if( nIter==nAlloc ){ |
| 172062 | int nByte = sizeof(Fts5PoslistReader) * nAlloc * 2; |
| 172063 | Fts5PoslistReader *aNew = (Fts5PoslistReader*)sqlite3_malloc(nByte); |
| 172064 | if( aNew==0 ){ |
| 172065 | rc = SQLITE_NOMEM; |
| @@ -171728,12 +172156,12 @@ | |
| 172156 | i64 dummy; |
| 172157 | int n = 0; |
| 172158 | int bFlag = 0; |
| 172159 | const u8 *a = 0; |
| 172160 | if( pTerm->pSynonym ){ |
| 172161 | rc = fts5ExprSynonymList( |
| 172162 | pTerm, 0, pColset, pNode->iRowid, &bFlag, (u8**)&a, &n |
| 172163 | ); |
| 172164 | }else{ |
| 172165 | rc = sqlite3Fts5IterPoslist(pTerm->pIter, pColset, &a, &n, &dummy); |
| 172166 | } |
| 172167 | if( rc!=SQLITE_OK ) goto ismatch_out; |
| @@ -172063,34 +172491,55 @@ | |
| 172491 | Fts5Expr *pExpr, /* Expression that pNear is a part of */ |
| 172492 | Fts5ExprNode *pNode /* The "NEAR" node (FTS5_STRING) */ |
| 172493 | ){ |
| 172494 | Fts5ExprNearset *pNear = pNode->pNear; |
| 172495 | int rc = *pRc; |
| 172496 | |
| 172497 | if( pExpr->pConfig->eDetail!=FTS5_DETAIL_FULL ){ |
| 172498 | Fts5ExprTerm *pTerm; |
| 172499 | Fts5ExprPhrase *pPhrase = pNear->apPhrase[0]; |
| 172500 | pPhrase->poslist.n = 0; |
| 172501 | for(pTerm=&pPhrase->aTerm[0]; pTerm; pTerm=pTerm->pSynonym){ |
| 172502 | Fts5IndexIter *pIter = pTerm->pIter; |
| 172503 | if( sqlite3Fts5IterEof(pIter)==0 ){ |
| 172504 | int n; |
| 172505 | i64 iRowid; |
| 172506 | rc = sqlite3Fts5IterPoslist(pIter, pNear->pColset, 0, &n, &iRowid); |
| 172507 | if( rc!=SQLITE_OK ){ |
| 172508 | *pRc = rc; |
| 172509 | return 0; |
| 172510 | }else if( iRowid==pNode->iRowid && n>0 ){ |
| 172511 | pPhrase->poslist.n = 1; |
| 172512 | } |
| 172513 | } |
| 172514 | } |
| 172515 | return pPhrase->poslist.n; |
| 172516 | }else{ |
| 172517 | int i; |
| 172518 | |
| 172519 | /* Check that each phrase in the nearset matches the current row. |
| 172520 | ** Populate the pPhrase->poslist buffers at the same time. If any |
| 172521 | ** phrase is not a match, break out of the loop early. */ |
| 172522 | for(i=0; rc==SQLITE_OK && i<pNear->nPhrase; i++){ |
| 172523 | Fts5ExprPhrase *pPhrase = pNear->apPhrase[i]; |
| 172524 | if( pPhrase->nTerm>1 || pPhrase->aTerm[0].pSynonym || pNear->pColset ){ |
| 172525 | int bMatch = 0; |
| 172526 | rc = fts5ExprPhraseIsMatch(pNode, pNear->pColset, pPhrase, &bMatch); |
| 172527 | if( bMatch==0 ) break; |
| 172528 | }else{ |
| 172529 | rc = sqlite3Fts5IterPoslistBuffer( |
| 172530 | pPhrase->aTerm[0].pIter, &pPhrase->poslist |
| 172531 | ); |
| 172532 | } |
| 172533 | } |
| 172534 | |
| 172535 | *pRc = rc; |
| 172536 | if( i==pNear->nPhrase && (i==1 || fts5ExprNearIsMatch(pRc, pNear)) ){ |
| 172537 | return 1; |
| 172538 | } |
| 172539 | return 0; |
| 172540 | } |
| 172541 | } |
| 172542 | |
| 172543 | static int fts5ExprTokenTest( |
| 172544 | Fts5Expr *pExpr, /* Expression that pNear is a part of */ |
| 172545 | Fts5ExprNode *pNode /* The "NEAR" node (FTS5_TERM) */ |
| @@ -172524,10 +172973,13 @@ | |
| 172973 | if( cmp || p2->bNomatch ) break; |
| 172974 | rc = fts5ExprNodeNext(pExpr, p1, 0, 0); |
| 172975 | } |
| 172976 | pNode->bEof = p1->bEof; |
| 172977 | pNode->iRowid = p1->iRowid; |
| 172978 | if( p1->bEof ){ |
| 172979 | fts5ExprNodeZeroPoslist(p2); |
| 172980 | } |
| 172981 | break; |
| 172982 | } |
| 172983 | } |
| 172984 | } |
| 172985 | return rc; |
| @@ -172909,10 +173361,11 @@ | |
| 173361 | } |
| 173362 | |
| 173363 | if( rc==SQLITE_OK ){ |
| 173364 | /* All the allocations succeeded. Put the expression object together. */ |
| 173365 | pNew->pIndex = pExpr->pIndex; |
| 173366 | pNew->pConfig = pExpr->pConfig; |
| 173367 | pNew->nPhrase = 1; |
| 173368 | pNew->apExprPhrase[0] = sCtx.pPhrase; |
| 173369 | pNew->pRoot->pNear->apPhrase[0] = sCtx.pPhrase; |
| 173370 | pNew->pRoot->pNear->nPhrase = 1; |
| 173371 | sCtx.pPhrase->pNode = pNew->pRoot; |
| @@ -173050,10 +173503,19 @@ | |
| 173503 | static void sqlite3Fts5ParseSetColset( |
| 173504 | Fts5Parse *pParse, |
| 173505 | Fts5ExprNearset *pNear, |
| 173506 | Fts5Colset *pColset |
| 173507 | ){ |
| 173508 | if( pParse->pConfig->eDetail==FTS5_DETAIL_NONE ){ |
| 173509 | pParse->rc = SQLITE_ERROR; |
| 173510 | pParse->zErr = sqlite3_mprintf( |
| 173511 | "fts5: column queries are not supported (detail=none)" |
| 173512 | ); |
| 173513 | sqlite3_free(pColset); |
| 173514 | return; |
| 173515 | } |
| 173516 | |
| 173517 | if( pNear ){ |
| 173518 | pNear->pColset = pColset; |
| 173519 | }else{ |
| 173520 | sqlite3_free(pColset); |
| 173521 | } |
| @@ -173111,15 +173573,24 @@ | |
| 173573 | if( eType==FTS5_STRING ){ |
| 173574 | int iPhrase; |
| 173575 | for(iPhrase=0; iPhrase<pNear->nPhrase; iPhrase++){ |
| 173576 | pNear->apPhrase[iPhrase]->pNode = pRet; |
| 173577 | } |
| 173578 | if( pNear->nPhrase==1 && pNear->apPhrase[0]->nTerm==1 ){ |
| 173579 | if( pNear->apPhrase[0]->aTerm[0].pSynonym==0 ){ |
| 173580 | pRet->eType = FTS5_TERM; |
| 173581 | } |
| 173582 | }else if( pParse->pConfig->eDetail!=FTS5_DETAIL_FULL ){ |
| 173583 | assert( pParse->rc==SQLITE_OK ); |
| 173584 | pParse->rc = SQLITE_ERROR; |
| 173585 | assert( pParse->zErr==0 ); |
| 173586 | pParse->zErr = sqlite3_mprintf( |
| 173587 | "fts5: %s queries are not supported (detail!=full)", |
| 173588 | pNear->nPhrase==1 ? "phrase": "NEAR" |
| 173589 | ); |
| 173590 | sqlite3_free(pRet); |
| 173591 | pRet = 0; |
| 173592 | } |
| 173593 | }else{ |
| 173594 | fts5ExprAddChildren(pRet, pLeft); |
| 173595 | fts5ExprAddChildren(pRet, pRight); |
| 173596 | } |
| @@ -173229,10 +173700,13 @@ | |
| 173700 | |
| 173701 | zRet = fts5PrintfAppend(zRet, " {"); |
| 173702 | for(iTerm=0; zRet && iTerm<pPhrase->nTerm; iTerm++){ |
| 173703 | char *zTerm = pPhrase->aTerm[iTerm].zTerm; |
| 173704 | zRet = fts5PrintfAppend(zRet, "%s%s", iTerm==0?"":" ", zTerm); |
| 173705 | if( pPhrase->aTerm[iTerm].bPrefix ){ |
| 173706 | zRet = fts5PrintfAppend(zRet, "*"); |
| 173707 | } |
| 173708 | } |
| 173709 | |
| 173710 | if( zRet ) zRet = fts5PrintfAppend(zRet, "}"); |
| 173711 | if( zRet==0 ) return 0; |
| 173712 | } |
| @@ -173541,10 +174015,237 @@ | |
| 174015 | *pa = 0; |
| 174016 | nRet = 0; |
| 174017 | } |
| 174018 | return nRet; |
| 174019 | } |
| 174020 | |
| 174021 | struct Fts5PoslistPopulator { |
| 174022 | Fts5PoslistWriter writer; |
| 174023 | int bOk; /* True if ok to populate */ |
| 174024 | int bMiss; |
| 174025 | }; |
| 174026 | |
| 174027 | static Fts5PoslistPopulator *sqlite3Fts5ExprClearPoslists(Fts5Expr *pExpr, int bLive){ |
| 174028 | Fts5PoslistPopulator *pRet; |
| 174029 | pRet = sqlite3_malloc(sizeof(Fts5PoslistPopulator)*pExpr->nPhrase); |
| 174030 | if( pRet ){ |
| 174031 | int i; |
| 174032 | memset(pRet, 0, sizeof(Fts5PoslistPopulator)*pExpr->nPhrase); |
| 174033 | for(i=0; i<pExpr->nPhrase; i++){ |
| 174034 | Fts5Buffer *pBuf = &pExpr->apExprPhrase[i]->poslist; |
| 174035 | Fts5ExprNode *pNode = pExpr->apExprPhrase[i]->pNode; |
| 174036 | assert( pExpr->apExprPhrase[i]->nTerm==1 ); |
| 174037 | if( bLive && |
| 174038 | (pBuf->n==0 || pNode->iRowid!=pExpr->pRoot->iRowid || pNode->bEof) |
| 174039 | ){ |
| 174040 | pRet[i].bMiss = 1; |
| 174041 | }else{ |
| 174042 | pBuf->n = 0; |
| 174043 | } |
| 174044 | } |
| 174045 | } |
| 174046 | return pRet; |
| 174047 | } |
| 174048 | |
| 174049 | struct Fts5ExprCtx { |
| 174050 | Fts5Expr *pExpr; |
| 174051 | Fts5PoslistPopulator *aPopulator; |
| 174052 | i64 iOff; |
| 174053 | }; |
| 174054 | typedef struct Fts5ExprCtx Fts5ExprCtx; |
| 174055 | |
| 174056 | /* |
| 174057 | ** TODO: Make this more efficient! |
| 174058 | */ |
| 174059 | static int fts5ExprColsetTest(Fts5Colset *pColset, int iCol){ |
| 174060 | int i; |
| 174061 | for(i=0; i<pColset->nCol; i++){ |
| 174062 | if( pColset->aiCol[i]==iCol ) return 1; |
| 174063 | } |
| 174064 | return 0; |
| 174065 | } |
| 174066 | |
| 174067 | static int fts5ExprPopulatePoslistsCb( |
| 174068 | void *pCtx, /* Copy of 2nd argument to xTokenize() */ |
| 174069 | int tflags, /* Mask of FTS5_TOKEN_* flags */ |
| 174070 | const char *pToken, /* Pointer to buffer containing token */ |
| 174071 | int nToken, /* Size of token in bytes */ |
| 174072 | int iStart, /* Byte offset of token within input text */ |
| 174073 | int iEnd /* Byte offset of end of token within input text */ |
| 174074 | ){ |
| 174075 | Fts5ExprCtx *p = (Fts5ExprCtx*)pCtx; |
| 174076 | Fts5Expr *pExpr = p->pExpr; |
| 174077 | int i; |
| 174078 | |
| 174079 | if( (tflags & FTS5_TOKEN_COLOCATED)==0 ) p->iOff++; |
| 174080 | for(i=0; i<pExpr->nPhrase; i++){ |
| 174081 | Fts5ExprTerm *pTerm; |
| 174082 | if( p->aPopulator[i].bOk==0 ) continue; |
| 174083 | for(pTerm=&pExpr->apExprPhrase[i]->aTerm[0]; pTerm; pTerm=pTerm->pSynonym){ |
| 174084 | int nTerm = strlen(pTerm->zTerm); |
| 174085 | if( (nTerm==nToken || (nTerm<nToken && pTerm->bPrefix)) |
| 174086 | && memcmp(pTerm->zTerm, pToken, nTerm)==0 |
| 174087 | ){ |
| 174088 | int rc = sqlite3Fts5PoslistWriterAppend( |
| 174089 | &pExpr->apExprPhrase[i]->poslist, &p->aPopulator[i].writer, p->iOff |
| 174090 | ); |
| 174091 | if( rc ) return rc; |
| 174092 | break; |
| 174093 | } |
| 174094 | } |
| 174095 | } |
| 174096 | return SQLITE_OK; |
| 174097 | } |
| 174098 | |
| 174099 | static int sqlite3Fts5ExprPopulatePoslists( |
| 174100 | Fts5Config *pConfig, |
| 174101 | Fts5Expr *pExpr, |
| 174102 | Fts5PoslistPopulator *aPopulator, |
| 174103 | int iCol, |
| 174104 | const char *z, int n |
| 174105 | ){ |
| 174106 | int i; |
| 174107 | Fts5ExprCtx sCtx; |
| 174108 | sCtx.pExpr = pExpr; |
| 174109 | sCtx.aPopulator = aPopulator; |
| 174110 | sCtx.iOff = (((i64)iCol) << 32) - 1; |
| 174111 | |
| 174112 | for(i=0; i<pExpr->nPhrase; i++){ |
| 174113 | Fts5ExprNode *pNode = pExpr->apExprPhrase[i]->pNode; |
| 174114 | Fts5Colset *pColset = pNode->pNear->pColset; |
| 174115 | if( (pColset && 0==fts5ExprColsetTest(pColset, iCol)) |
| 174116 | || aPopulator[i].bMiss |
| 174117 | ){ |
| 174118 | aPopulator[i].bOk = 0; |
| 174119 | }else{ |
| 174120 | aPopulator[i].bOk = 1; |
| 174121 | } |
| 174122 | } |
| 174123 | |
| 174124 | return sqlite3Fts5Tokenize(pConfig, |
| 174125 | FTS5_TOKENIZE_AUX, z, n, (void*)&sCtx, fts5ExprPopulatePoslistsCb |
| 174126 | ); |
| 174127 | } |
| 174128 | |
| 174129 | static void fts5ExprClearPoslists(Fts5ExprNode *pNode){ |
| 174130 | if( pNode->eType==FTS5_TERM || pNode->eType==FTS5_STRING ){ |
| 174131 | pNode->pNear->apPhrase[0]->poslist.n = 0; |
| 174132 | }else{ |
| 174133 | int i; |
| 174134 | for(i=0; i<pNode->nChild; i++){ |
| 174135 | fts5ExprClearPoslists(pNode->apChild[i]); |
| 174136 | } |
| 174137 | } |
| 174138 | } |
| 174139 | |
| 174140 | static int fts5ExprCheckPoslists(Fts5ExprNode *pNode, i64 iRowid){ |
| 174141 | if( pNode ){ |
| 174142 | pNode->iRowid = iRowid; |
| 174143 | pNode->bEof = 0; |
| 174144 | switch( pNode->eType ){ |
| 174145 | case FTS5_TERM: |
| 174146 | case FTS5_STRING: |
| 174147 | return (pNode->pNear->apPhrase[0]->poslist.n>0); |
| 174148 | |
| 174149 | case FTS5_AND: { |
| 174150 | int i; |
| 174151 | for(i=0; i<pNode->nChild; i++){ |
| 174152 | if( fts5ExprCheckPoslists(pNode->apChild[i], iRowid)==0 ){ |
| 174153 | fts5ExprClearPoslists(pNode); |
| 174154 | return 0; |
| 174155 | } |
| 174156 | } |
| 174157 | break; |
| 174158 | } |
| 174159 | |
| 174160 | case FTS5_OR: { |
| 174161 | int i; |
| 174162 | int bRet = 0; |
| 174163 | for(i=0; i<pNode->nChild; i++){ |
| 174164 | if( fts5ExprCheckPoslists(pNode->apChild[i], iRowid) ){ |
| 174165 | bRet = 1; |
| 174166 | } |
| 174167 | } |
| 174168 | if( bRet==0 ){ |
| 174169 | fts5ExprClearPoslists(pNode); |
| 174170 | } |
| 174171 | return bRet; |
| 174172 | } |
| 174173 | |
| 174174 | default: { |
| 174175 | assert( pNode->eType==FTS5_NOT ); |
| 174176 | if( 0==fts5ExprCheckPoslists(pNode->apChild[0], iRowid) |
| 174177 | || 0!=fts5ExprCheckPoslists(pNode->apChild[1], iRowid) |
| 174178 | ){ |
| 174179 | fts5ExprClearPoslists(pNode); |
| 174180 | return 0; |
| 174181 | } |
| 174182 | break; |
| 174183 | } |
| 174184 | } |
| 174185 | } |
| 174186 | return 1; |
| 174187 | } |
| 174188 | |
| 174189 | static void sqlite3Fts5ExprCheckPoslists(Fts5Expr *pExpr, i64 iRowid){ |
| 174190 | fts5ExprCheckPoslists(pExpr->pRoot, iRowid); |
| 174191 | } |
| 174192 | |
| 174193 | static void fts5ExprClearEof(Fts5ExprNode *pNode){ |
| 174194 | int i; |
| 174195 | for(i=0; i<pNode->nChild; i++){ |
| 174196 | fts5ExprClearEof(pNode->apChild[i]); |
| 174197 | } |
| 174198 | pNode->bEof = 0; |
| 174199 | } |
| 174200 | static void sqlite3Fts5ExprClearEof(Fts5Expr *pExpr){ |
| 174201 | fts5ExprClearEof(pExpr->pRoot); |
| 174202 | } |
| 174203 | |
| 174204 | /* |
| 174205 | ** This function is only called for detail=columns tables. |
| 174206 | */ |
| 174207 | static int sqlite3Fts5ExprPhraseCollist( |
| 174208 | Fts5Expr *pExpr, |
| 174209 | int iPhrase, |
| 174210 | const u8 **ppCollist, |
| 174211 | int *pnCollist |
| 174212 | ){ |
| 174213 | Fts5ExprPhrase *pPhrase = pExpr->apExprPhrase[iPhrase]; |
| 174214 | Fts5ExprNode *pNode = pPhrase->pNode; |
| 174215 | int rc = SQLITE_OK; |
| 174216 | |
| 174217 | assert( iPhrase>=0 && iPhrase<pExpr->nPhrase ); |
| 174218 | if( pNode->bEof==0 |
| 174219 | && pNode->iRowid==pExpr->pRoot->iRowid |
| 174220 | && pPhrase->poslist.n>0 |
| 174221 | ){ |
| 174222 | Fts5ExprTerm *pTerm = &pPhrase->aTerm[0]; |
| 174223 | if( pTerm->pSynonym ){ |
| 174224 | int bDel = 0; |
| 174225 | u8 *a; |
| 174226 | rc = fts5ExprSynonymList( |
| 174227 | pTerm, 1, 0, pNode->iRowid, &bDel, &a, pnCollist |
| 174228 | ); |
| 174229 | if( bDel ){ |
| 174230 | sqlite3Fts5BufferSet(&rc, &pPhrase->poslist, *pnCollist, a); |
| 174231 | *ppCollist = pPhrase->poslist.p; |
| 174232 | sqlite3_free(a); |
| 174233 | }else{ |
| 174234 | *ppCollist = a; |
| 174235 | } |
| 174236 | }else{ |
| 174237 | sqlite3Fts5IterCollist(pPhrase->aTerm[0].pIter, ppCollist, pnCollist); |
| 174238 | } |
| 174239 | }else{ |
| 174240 | *ppCollist = 0; |
| 174241 | *pnCollist = 0; |
| 174242 | } |
| 174243 | |
| 174244 | return rc; |
| 174245 | } |
| 174246 | |
| 174247 | |
| 174248 | /* |
| 174249 | ** 2014 August 11 |
| 174250 | ** |
| 174251 | ** The author disclaims copyright to this source code. In place of |
| @@ -173570,10 +174271,11 @@ | |
| 174271 | ** segment. |
| 174272 | */ |
| 174273 | |
| 174274 | |
| 174275 | struct Fts5Hash { |
| 174276 | int eDetail; /* Copy of Fts5Config.eDetail */ |
| 174277 | int *pnByte; /* Pointer to bytes counter */ |
| 174278 | int nEntry; /* Number of entries currently in hash */ |
| 174279 | int nSlot; /* Size of aSlot[] array */ |
| 174280 | Fts5HashEntry *pScan; /* Current ordered scan item */ |
| 174281 | Fts5HashEntry **aSlot; /* Array of hash slots */ |
| @@ -173606,10 +174308,11 @@ | |
| 174308 | |
| 174309 | int nAlloc; /* Total size of allocation */ |
| 174310 | int iSzPoslist; /* Offset of space for 4-byte poslist size */ |
| 174311 | int nData; /* Total bytes of data (incl. structure) */ |
| 174312 | u8 bDel; /* Set delete-flag @ iSzPoslist */ |
| 174313 | u8 bContent; /* Set content-flag (detail=none mode) */ |
| 174314 | |
| 174315 | int iCol; /* Column of last value written */ |
| 174316 | int iPos; /* Position of last value written */ |
| 174317 | i64 iRowid; /* Rowid of last value written */ |
| 174318 | char zKey[8]; /* Nul-terminated entry key */ |
| @@ -173623,11 +174326,11 @@ | |
| 174326 | |
| 174327 | |
| 174328 | /* |
| 174329 | ** Allocate a new hash table. |
| 174330 | */ |
| 174331 | static int sqlite3Fts5HashNew(Fts5Config *pConfig, Fts5Hash **ppNew, int *pnByte){ |
| 174332 | int rc = SQLITE_OK; |
| 174333 | Fts5Hash *pNew; |
| 174334 | |
| 174335 | *ppNew = pNew = (Fts5Hash*)sqlite3_malloc(sizeof(Fts5Hash)); |
| 174336 | if( pNew==0 ){ |
| @@ -173634,10 +174337,11 @@ | |
| 174337 | rc = SQLITE_NOMEM; |
| 174338 | }else{ |
| 174339 | int nByte; |
| 174340 | memset(pNew, 0, sizeof(Fts5Hash)); |
| 174341 | pNew->pnByte = pnByte; |
| 174342 | pNew->eDetail = pConfig->eDetail; |
| 174343 | |
| 174344 | pNew->nSlot = 1024; |
| 174345 | nByte = sizeof(Fts5HashEntry*) * pNew->nSlot; |
| 174346 | pNew->aSlot = (Fts5HashEntry**)sqlite3_malloc(nByte); |
| 174347 | if( pNew->aSlot==0 ){ |
| @@ -173726,30 +174430,50 @@ | |
| 174430 | pHash->nSlot = nNew; |
| 174431 | pHash->aSlot = apNew; |
| 174432 | return SQLITE_OK; |
| 174433 | } |
| 174434 | |
| 174435 | static void fts5HashAddPoslistSize(Fts5Hash *pHash, Fts5HashEntry *p){ |
| 174436 | if( p->iSzPoslist ){ |
| 174437 | u8 *pPtr = (u8*)p; |
| 174438 | if( pHash->eDetail==FTS5_DETAIL_NONE ){ |
| 174439 | assert( p->nData==p->iSzPoslist ); |
| 174440 | if( p->bDel ){ |
| 174441 | pPtr[p->nData++] = 0x00; |
| 174442 | if( p->bContent ){ |
| 174443 | pPtr[p->nData++] = 0x00; |
| 174444 | } |
| 174445 | } |
| 174446 | }else{ |
| 174447 | int nSz = (p->nData - p->iSzPoslist - 1); /* Size in bytes */ |
| 174448 | int nPos = nSz*2 + p->bDel; /* Value of nPos field */ |
| 174449 | |
| 174450 | assert( p->bDel==0 || p->bDel==1 ); |
| 174451 | if( nPos<=127 ){ |
| 174452 | pPtr[p->iSzPoslist] = (u8)nPos; |
| 174453 | }else{ |
| 174454 | int nByte = sqlite3Fts5GetVarintLen((u32)nPos); |
| 174455 | memmove(&pPtr[p->iSzPoslist + nByte], &pPtr[p->iSzPoslist + 1], nSz); |
| 174456 | sqlite3Fts5PutVarint(&pPtr[p->iSzPoslist], nPos); |
| 174457 | p->nData += (nByte-1); |
| 174458 | } |
| 174459 | } |
| 174460 | |
| 174461 | p->iSzPoslist = 0; |
| 174462 | p->bDel = 0; |
| 174463 | p->bContent = 0; |
| 174464 | } |
| 174465 | } |
| 174466 | |
| 174467 | /* |
| 174468 | ** Add an entry to the in-memory hash table. The key is the concatenation |
| 174469 | ** of bByte and (pToken/nToken). The value is (iRowid/iCol/iPos). |
| 174470 | ** |
| 174471 | ** (bByte || pToken) -> (iRowid,iCol,iPos) |
| 174472 | ** |
| 174473 | ** Or, if iCol is negative, then the value is a delete marker. |
| 174474 | */ |
| 174475 | static int sqlite3Fts5HashWrite( |
| 174476 | Fts5Hash *pHash, |
| 174477 | i64 iRowid, /* Rowid for this entry */ |
| 174478 | int iCol, /* Column token appears in (-ve -> delete) */ |
| 174479 | int iPos, /* Position of token within column */ |
| @@ -173758,10 +174482,13 @@ | |
| 174482 | ){ |
| 174483 | unsigned int iHash; |
| 174484 | Fts5HashEntry *p; |
| 174485 | u8 *pPtr; |
| 174486 | int nIncr = 0; /* Amount to increment (*pHash->pnByte) by */ |
| 174487 | int bNew; /* If non-delete entry should be written */ |
| 174488 | |
| 174489 | bNew = (pHash->eDetail==FTS5_DETAIL_FULL); |
| 174490 | |
| 174491 | /* Attempt to locate an existing hash entry */ |
| 174492 | iHash = fts5HashKey2(pHash->nSlot, (u8)bByte, (const u8*)pToken, nToken); |
| 174493 | for(p=pHash->aSlot[iHash]; p; p=p->pHashNext){ |
| 174494 | if( p->zKey[0]==bByte |
| @@ -173772,92 +174499,120 @@ | |
| 174499 | } |
| 174500 | } |
| 174501 | |
| 174502 | /* If an existing hash entry cannot be found, create a new one. */ |
| 174503 | if( p==0 ){ |
| 174504 | /* Figure out how much space to allocate */ |
| 174505 | int nByte = FTS5_HASHENTRYSIZE + (nToken+1) + 1 + 64; |
| 174506 | if( nByte<128 ) nByte = 128; |
| 174507 | |
| 174508 | /* Grow the Fts5Hash.aSlot[] array if necessary. */ |
| 174509 | if( (pHash->nEntry*2)>=pHash->nSlot ){ |
| 174510 | int rc = fts5HashResize(pHash); |
| 174511 | if( rc!=SQLITE_OK ) return rc; |
| 174512 | iHash = fts5HashKey2(pHash->nSlot, (u8)bByte, (const u8*)pToken, nToken); |
| 174513 | } |
| 174514 | |
| 174515 | /* Allocate new Fts5HashEntry and add it to the hash table. */ |
| 174516 | p = (Fts5HashEntry*)sqlite3_malloc(nByte); |
| 174517 | if( !p ) return SQLITE_NOMEM; |
| 174518 | memset(p, 0, FTS5_HASHENTRYSIZE); |
| 174519 | p->nAlloc = nByte; |
| 174520 | p->zKey[0] = bByte; |
| 174521 | memcpy(&p->zKey[1], pToken, nToken); |
| 174522 | assert( iHash==fts5HashKey(pHash->nSlot, (u8*)p->zKey, nToken+1) ); |
| 174523 | p->zKey[nToken+1] = '\0'; |
| 174524 | p->nData = nToken+1 + 1 + FTS5_HASHENTRYSIZE; |
| 174525 | p->pHashNext = pHash->aSlot[iHash]; |
| 174526 | pHash->aSlot[iHash] = p; |
| 174527 | pHash->nEntry++; |
| 174528 | |
| 174529 | /* Add the first rowid field to the hash-entry */ |
| 174530 | p->nData += sqlite3Fts5PutVarint(&((u8*)p)[p->nData], iRowid); |
| 174531 | p->iRowid = iRowid; |
| 174532 | |
| 174533 | p->iSzPoslist = p->nData; |
| 174534 | if( pHash->eDetail!=FTS5_DETAIL_NONE ){ |
| 174535 | p->nData += 1; |
| 174536 | p->iCol = (pHash->eDetail==FTS5_DETAIL_FULL ? 0 : -1); |
| 174537 | } |
| 174538 | |
| 174539 | nIncr += p->nData; |
| 174540 | }else{ |
| 174541 | |
| 174542 | /* Appending to an existing hash-entry. Check that there is enough |
| 174543 | ** space to append the largest possible new entry. Worst case scenario |
| 174544 | ** is: |
| 174545 | ** |
| 174546 | ** + 9 bytes for a new rowid, |
| 174547 | ** + 4 byte reserved for the "poslist size" varint. |
| 174548 | ** + 1 byte for a "new column" byte, |
| 174549 | ** + 3 bytes for a new column number (16-bit max) as a varint, |
| 174550 | ** + 5 bytes for the new position offset (32-bit max). |
| 174551 | */ |
| 174552 | if( (p->nAlloc - p->nData) < (9 + 4 + 1 + 3 + 5) ){ |
| 174553 | int nNew = p->nAlloc * 2; |
| 174554 | Fts5HashEntry *pNew; |
| 174555 | Fts5HashEntry **pp; |
| 174556 | pNew = (Fts5HashEntry*)sqlite3_realloc(p, nNew); |
| 174557 | if( pNew==0 ) return SQLITE_NOMEM; |
| 174558 | pNew->nAlloc = nNew; |
| 174559 | for(pp=&pHash->aSlot[iHash]; *pp!=p; pp=&(*pp)->pHashNext); |
| 174560 | *pp = pNew; |
| 174561 | p = pNew; |
| 174562 | } |
| 174563 | nIncr -= p->nData; |
| 174564 | } |
| 174565 | assert( (p->nAlloc - p->nData) >= (9 + 4 + 1 + 3 + 5) ); |
| 174566 | |
| 174567 | pPtr = (u8*)p; |
| 174568 | |
| 174569 | /* If this is a new rowid, append the 4-byte size field for the previous |
| 174570 | ** entry, and the new rowid for this entry. */ |
| 174571 | if( iRowid!=p->iRowid ){ |
| 174572 | fts5HashAddPoslistSize(pHash, p); |
| 174573 | p->nData += sqlite3Fts5PutVarint(&pPtr[p->nData], iRowid - p->iRowid); |
| 174574 | p->iRowid = iRowid; |
| 174575 | bNew = 1; |
| 174576 | p->iSzPoslist = p->nData; |
| 174577 | if( pHash->eDetail!=FTS5_DETAIL_NONE ){ |
| 174578 | p->nData += 1; |
| 174579 | p->iCol = (pHash->eDetail==FTS5_DETAIL_FULL ? 0 : -1); |
| 174580 | p->iPos = 0; |
| 174581 | } |
| 174582 | } |
| 174583 | |
| 174584 | if( iCol>=0 ){ |
| 174585 | if( pHash->eDetail==FTS5_DETAIL_NONE ){ |
| 174586 | p->bContent = 1; |
| 174587 | }else{ |
| 174588 | /* Append a new column value, if necessary */ |
| 174589 | assert( iCol>=p->iCol ); |
| 174590 | if( iCol!=p->iCol ){ |
| 174591 | if( pHash->eDetail==FTS5_DETAIL_FULL ){ |
| 174592 | pPtr[p->nData++] = 0x01; |
| 174593 | p->nData += sqlite3Fts5PutVarint(&pPtr[p->nData], iCol); |
| 174594 | p->iCol = iCol; |
| 174595 | p->iPos = 0; |
| 174596 | }else{ |
| 174597 | bNew = 1; |
| 174598 | p->iCol = iPos = iCol; |
| 174599 | } |
| 174600 | } |
| 174601 | |
| 174602 | /* Append the new position offset, if necessary */ |
| 174603 | if( bNew ){ |
| 174604 | p->nData += sqlite3Fts5PutVarint(&pPtr[p->nData], iPos - p->iPos + 2); |
| 174605 | p->iPos = iPos; |
| 174606 | } |
| 174607 | } |
| 174608 | }else{ |
| 174609 | /* This is a delete. Set the delete flag. */ |
| 174610 | p->bDel = 1; |
| 174611 | } |
| 174612 | |
| 174613 | nIncr += p->nData; |
| 174614 | *pHash->pnByte += nIncr; |
| 174615 | return SQLITE_OK; |
| 174616 | } |
| 174617 | |
| 174618 | |
| @@ -173967,11 +174722,11 @@ | |
| 174722 | for(p=pHash->aSlot[iHash]; p; p=p->pHashNext){ |
| 174723 | if( memcmp(p->zKey, pTerm, nTerm)==0 && p->zKey[nTerm]==0 ) break; |
| 174724 | } |
| 174725 | |
| 174726 | if( p ){ |
| 174727 | fts5HashAddPoslistSize(pHash, p); |
| 174728 | *ppDoclist = (const u8*)&p->zKey[nTerm+1]; |
| 174729 | *pnDoclist = p->nData - (FTS5_HASHENTRYSIZE + nTerm + 1); |
| 174730 | }else{ |
| 174731 | *ppDoclist = 0; |
| 174732 | *pnDoclist = 0; |
| @@ -174003,11 +174758,11 @@ | |
| 174758 | int *pnDoclist /* OUT: size of doclist in bytes */ |
| 174759 | ){ |
| 174760 | Fts5HashEntry *p; |
| 174761 | if( (p = pHash->pScan) ){ |
| 174762 | int nTerm = (int)strlen(p->zKey); |
| 174763 | fts5HashAddPoslistSize(pHash, p); |
| 174764 | *pzTerm = p->zKey; |
| 174765 | *ppDoclist = (const u8*)&p->zKey[nTerm+1]; |
| 174766 | *pnDoclist = p->nData - (FTS5_HASHENTRYSIZE + nTerm + 1); |
| 174767 | }else{ |
| 174768 | *pzTerm = 0; |
| @@ -174450,10 +175205,13 @@ | |
| 175205 | int iLeafPgno; /* Current leaf page number */ |
| 175206 | Fts5Data *pLeaf; /* Current leaf data */ |
| 175207 | Fts5Data *pNextLeaf; /* Leaf page (iLeafPgno+1) */ |
| 175208 | int iLeafOffset; /* Byte offset within current leaf */ |
| 175209 | |
| 175210 | /* Next method */ |
| 175211 | void (*xNext)(Fts5Index*, Fts5SegIter*, int*); |
| 175212 | |
| 175213 | /* The page and offset from which the current term was read. The offset |
| 175214 | ** is the offset of the first rowid in the current doclist. */ |
| 175215 | int iTermLeafPgno; |
| 175216 | int iTermLeafOffset; |
| 175217 | |
| @@ -174469,11 +175227,11 @@ | |
| 175227 | |
| 175228 | /* Variables populated based on current entry. */ |
| 175229 | Fts5Buffer term; /* Current term */ |
| 175230 | i64 iRowid; /* Current rowid */ |
| 175231 | int nPos; /* Number of bytes in current position list */ |
| 175232 | u8 bDel; /* True if the delete flag is set */ |
| 175233 | }; |
| 175234 | |
| 175235 | /* |
| 175236 | ** Argument is a pointer to an Fts5Data structure that contains a |
| 175237 | ** leaf page. |
| @@ -174482,11 +175240,10 @@ | |
| 175240 | (x)->szLeaf==(x)->nn || (x)->szLeaf==fts5GetU16(&(x)->p[2]) \ |
| 175241 | ) |
| 175242 | |
| 175243 | #define FTS5_SEGITER_ONETERM 0x01 |
| 175244 | #define FTS5_SEGITER_REVERSE 0x02 |
| 175245 | |
| 175246 | /* |
| 175247 | ** Argument is a pointer to an Fts5Data structure that contains a leaf |
| 175248 | ** page. This macro evaluates to true if the leaf contains no terms, or |
| 175249 | ** false if it contains at least one term. |
| @@ -175509,17 +176266,33 @@ | |
| 176266 | ** position list content (if any). |
| 176267 | */ |
| 176268 | static void fts5SegIterLoadNPos(Fts5Index *p, Fts5SegIter *pIter){ |
| 176269 | if( p->rc==SQLITE_OK ){ |
| 176270 | int iOff = pIter->iLeafOffset; /* Offset to read at */ |
| 176271 | ASSERT_SZLEAF_OK(pIter->pLeaf); |
| 176272 | if( p->pConfig->eDetail==FTS5_DETAIL_NONE ){ |
| 176273 | int iEod = MIN(pIter->iEndofDoclist, pIter->pLeaf->szLeaf); |
| 176274 | pIter->bDel = 0; |
| 176275 | pIter->nPos = 1; |
| 176276 | if( iOff<iEod && pIter->pLeaf->p[iOff]==0 ){ |
| 176277 | pIter->bDel = 1; |
| 176278 | iOff++; |
| 176279 | if( iOff<iEod && pIter->pLeaf->p[iOff]==0 ){ |
| 176280 | pIter->nPos = 1; |
| 176281 | iOff++; |
| 176282 | }else{ |
| 176283 | pIter->nPos = 0; |
| 176284 | } |
| 176285 | } |
| 176286 | }else{ |
| 176287 | int nSz; |
| 176288 | fts5FastGetVarint32(pIter->pLeaf->p, iOff, nSz); |
| 176289 | pIter->bDel = (nSz & 0x0001); |
| 176290 | pIter->nPos = nSz>>1; |
| 176291 | assert_nc( pIter->nPos>=0 ); |
| 176292 | } |
| 176293 | pIter->iLeafOffset = iOff; |
| 176294 | } |
| 176295 | } |
| 176296 | |
| 176297 | static void fts5SegIterLoadRowid(Fts5Index *p, Fts5SegIter *pIter){ |
| 176298 | u8 *a = pIter->pLeaf->p; /* Buffer to read data from */ |
| @@ -175575,10 +176348,24 @@ | |
| 176348 | pIter->iEndofDoclist += nExtra; |
| 176349 | } |
| 176350 | |
| 176351 | fts5SegIterLoadRowid(p, pIter); |
| 176352 | } |
| 176353 | |
| 176354 | static void fts5SegIterNext(Fts5Index*, Fts5SegIter*, int*); |
| 176355 | static void fts5SegIterNext_Reverse(Fts5Index*, Fts5SegIter*, int*); |
| 176356 | static void fts5SegIterNext_None(Fts5Index*, Fts5SegIter*, int*); |
| 176357 | |
| 176358 | static void fts5SegIterSetNext(Fts5Index *p, Fts5SegIter *pIter){ |
| 176359 | if( pIter->flags & FTS5_SEGITER_REVERSE ){ |
| 176360 | pIter->xNext = fts5SegIterNext_Reverse; |
| 176361 | }else if( p->pConfig->eDetail==FTS5_DETAIL_NONE ){ |
| 176362 | pIter->xNext = fts5SegIterNext_None; |
| 176363 | }else{ |
| 176364 | pIter->xNext = fts5SegIterNext; |
| 176365 | } |
| 176366 | } |
| 176367 | |
| 176368 | /* |
| 176369 | ** Initialize the iterator object pIter to iterate through the entries in |
| 176370 | ** segment pSeg. The iterator is left pointing to the first entry when |
| 176371 | ** this function returns. |
| @@ -175601,10 +176388,11 @@ | |
| 176388 | return; |
| 176389 | } |
| 176390 | |
| 176391 | if( p->rc==SQLITE_OK ){ |
| 176392 | memset(pIter, 0, sizeof(*pIter)); |
| 176393 | fts5SegIterSetNext(p, pIter); |
| 176394 | pIter->pSeg = pSeg; |
| 176395 | pIter->iLeafPgno = pSeg->pgnoFirst-1; |
| 176396 | fts5SegIterNextPage(p, pIter); |
| 176397 | } |
| 176398 | |
| @@ -175632,10 +176420,11 @@ | |
| 176420 | ** aRowidOffset[] and iRowidOffset variables. At this point the iterator |
| 176421 | ** is in its regular state - Fts5SegIter.iLeafOffset points to the first |
| 176422 | ** byte of the position list content associated with said rowid. |
| 176423 | */ |
| 176424 | static void fts5SegIterReverseInitPage(Fts5Index *p, Fts5SegIter *pIter){ |
| 176425 | int eDetail = p->pConfig->eDetail; |
| 176426 | int n = pIter->pLeaf->szLeaf; |
| 176427 | int i = pIter->iLeafOffset; |
| 176428 | u8 *a = pIter->pLeaf->p; |
| 176429 | int iRowidOffset = 0; |
| 176430 | |
| @@ -175644,19 +176433,28 @@ | |
| 176433 | } |
| 176434 | |
| 176435 | ASSERT_SZLEAF_OK(pIter->pLeaf); |
| 176436 | while( 1 ){ |
| 176437 | i64 iDelta = 0; |
| 176438 | |
| 176439 | if( eDetail==FTS5_DETAIL_NONE ){ |
| 176440 | /* todo */ |
| 176441 | if( i<n && a[i]==0 ){ |
| 176442 | i++; |
| 176443 | if( i<n && a[i]==0 ) i++; |
| 176444 | } |
| 176445 | }else{ |
| 176446 | int nPos; |
| 176447 | int bDummy; |
| 176448 | i += fts5GetPoslistSize(&a[i], &nPos, &bDummy); |
| 176449 | i += nPos; |
| 176450 | } |
| 176451 | if( i>=n ) break; |
| 176452 | i += fts5GetVarint(&a[i], (u64*)&iDelta); |
| 176453 | pIter->iRowid += iDelta; |
| 176454 | |
| 176455 | /* If necessary, grow the pIter->aRowidOffset[] array. */ |
| 176456 | if( iRowidOffset>=pIter->nRowidOffset ){ |
| 176457 | int nNew = pIter->nRowidOffset + 8; |
| 176458 | int *aNew = (int*)sqlite3_realloc(pIter->aRowidOffset, nNew*sizeof(int)); |
| 176459 | if( aNew==0 ){ |
| 176460 | p->rc = SQLITE_NOMEM; |
| @@ -175730,10 +176528,112 @@ | |
| 176528 | */ |
| 176529 | static int fts5MultiIterIsEmpty(Fts5Index *p, Fts5IndexIter *pIter){ |
| 176530 | Fts5SegIter *pSeg = &pIter->aSeg[pIter->aFirst[1].iFirst]; |
| 176531 | return (p->rc==SQLITE_OK && pSeg->pLeaf && pSeg->nPos==0); |
| 176532 | } |
| 176533 | |
| 176534 | /* |
| 176535 | ** Advance iterator pIter to the next entry. |
| 176536 | ** |
| 176537 | ** This version of fts5SegIterNext() is only used by reverse iterators. |
| 176538 | */ |
| 176539 | static void fts5SegIterNext_Reverse( |
| 176540 | Fts5Index *p, /* FTS5 backend object */ |
| 176541 | Fts5SegIter *pIter, /* Iterator to advance */ |
| 176542 | int *pbNewTerm /* OUT: Set for new term */ |
| 176543 | ){ |
| 176544 | assert( pIter->flags & FTS5_SEGITER_REVERSE ); |
| 176545 | assert( pIter->pNextLeaf==0 ); |
| 176546 | if( pIter->iRowidOffset>0 ){ |
| 176547 | u8 *a = pIter->pLeaf->p; |
| 176548 | int iOff; |
| 176549 | i64 iDelta; |
| 176550 | |
| 176551 | pIter->iRowidOffset--; |
| 176552 | pIter->iLeafOffset = pIter->aRowidOffset[pIter->iRowidOffset]; |
| 176553 | fts5SegIterLoadNPos(p, pIter); |
| 176554 | iOff = pIter->iLeafOffset; |
| 176555 | if( p->pConfig->eDetail!=FTS5_DETAIL_NONE ){ |
| 176556 | iOff += pIter->nPos; |
| 176557 | } |
| 176558 | fts5GetVarint(&a[iOff], (u64*)&iDelta); |
| 176559 | pIter->iRowid -= iDelta; |
| 176560 | }else{ |
| 176561 | fts5SegIterReverseNewPage(p, pIter); |
| 176562 | } |
| 176563 | } |
| 176564 | |
| 176565 | /* |
| 176566 | ** Advance iterator pIter to the next entry. |
| 176567 | ** |
| 176568 | ** This version of fts5SegIterNext() is only used if detail=none and the |
| 176569 | ** iterator is not a reverse direction iterator. |
| 176570 | */ |
| 176571 | static void fts5SegIterNext_None( |
| 176572 | Fts5Index *p, /* FTS5 backend object */ |
| 176573 | Fts5SegIter *pIter, /* Iterator to advance */ |
| 176574 | int *pbNewTerm /* OUT: Set for new term */ |
| 176575 | ){ |
| 176576 | int iOff; |
| 176577 | |
| 176578 | assert( p->rc==SQLITE_OK ); |
| 176579 | assert( (pIter->flags & FTS5_SEGITER_REVERSE)==0 ); |
| 176580 | assert( p->pConfig->eDetail==FTS5_DETAIL_NONE ); |
| 176581 | |
| 176582 | ASSERT_SZLEAF_OK(pIter->pLeaf); |
| 176583 | iOff = pIter->iLeafOffset; |
| 176584 | |
| 176585 | /* Next entry is on the next page */ |
| 176586 | if( pIter->pSeg && iOff>=pIter->pLeaf->szLeaf ){ |
| 176587 | fts5SegIterNextPage(p, pIter); |
| 176588 | if( p->rc || pIter->pLeaf==0 ) return; |
| 176589 | pIter->iRowid = 0; |
| 176590 | iOff = 4; |
| 176591 | } |
| 176592 | |
| 176593 | if( iOff<pIter->iEndofDoclist ){ |
| 176594 | /* Next entry is on the current page */ |
| 176595 | i64 iDelta; |
| 176596 | iOff += sqlite3Fts5GetVarint(&pIter->pLeaf->p[iOff], (u64*)&iDelta); |
| 176597 | pIter->iLeafOffset = iOff; |
| 176598 | pIter->iRowid += iDelta; |
| 176599 | }else if( (pIter->flags & FTS5_SEGITER_ONETERM)==0 ){ |
| 176600 | if( pIter->pSeg ){ |
| 176601 | int nKeep = 0; |
| 176602 | if( iOff!=fts5LeafFirstTermOff(pIter->pLeaf) ){ |
| 176603 | iOff += fts5GetVarint32(&pIter->pLeaf->p[iOff], nKeep); |
| 176604 | } |
| 176605 | pIter->iLeafOffset = iOff; |
| 176606 | fts5SegIterLoadTerm(p, pIter, nKeep); |
| 176607 | }else{ |
| 176608 | const u8 *pList = 0; |
| 176609 | const char *zTerm = 0; |
| 176610 | int nList; |
| 176611 | sqlite3Fts5HashScanNext(p->pHash); |
| 176612 | sqlite3Fts5HashScanEntry(p->pHash, &zTerm, &pList, &nList); |
| 176613 | if( pList==0 ) goto next_none_eof; |
| 176614 | pIter->pLeaf->p = (u8*)pList; |
| 176615 | pIter->pLeaf->nn = nList; |
| 176616 | pIter->pLeaf->szLeaf = nList; |
| 176617 | pIter->iEndofDoclist = nList; |
| 176618 | sqlite3Fts5BufferSet(&p->rc,&pIter->term, (int)strlen(zTerm), (u8*)zTerm); |
| 176619 | pIter->iLeafOffset = fts5GetVarint(pList, (u64*)&pIter->iRowid); |
| 176620 | } |
| 176621 | |
| 176622 | if( pbNewTerm ) *pbNewTerm = 1; |
| 176623 | }else{ |
| 176624 | goto next_none_eof; |
| 176625 | } |
| 176626 | |
| 176627 | fts5SegIterLoadNPos(p, pIter); |
| 176628 | |
| 176629 | return; |
| 176630 | next_none_eof: |
| 176631 | fts5DataRelease(pIter->pLeaf); |
| 176632 | pIter->pLeaf = 0; |
| 176633 | } |
| 176634 | |
| 176635 | |
| 176636 | /* |
| 176637 | ** Advance iterator pIter to the next entry. |
| 176638 | ** |
| 176639 | ** If an error occurs, Fts5Index.rc is set to an appropriate error code. It |
| @@ -175743,144 +176643,133 @@ | |
| 176643 | static void fts5SegIterNext( |
| 176644 | Fts5Index *p, /* FTS5 backend object */ |
| 176645 | Fts5SegIter *pIter, /* Iterator to advance */ |
| 176646 | int *pbNewTerm /* OUT: Set for new term */ |
| 176647 | ){ |
| 176648 | Fts5Data *pLeaf = pIter->pLeaf; |
| 176649 | int iOff; |
| 176650 | int bNewTerm = 0; |
| 176651 | int nKeep = 0; |
| 176652 | |
| 176653 | assert( pbNewTerm==0 || *pbNewTerm==0 ); |
| 176654 | assert( p->pConfig->eDetail!=FTS5_DETAIL_NONE ); |
| 176655 | |
| 176656 | /* Search for the end of the position list within the current page. */ |
| 176657 | u8 *a = pLeaf->p; |
| 176658 | int n = pLeaf->szLeaf; |
| 176659 | |
| 176660 | ASSERT_SZLEAF_OK(pLeaf); |
| 176661 | iOff = pIter->iLeafOffset + pIter->nPos; |
| 176662 | |
| 176663 | if( iOff<n ){ |
| 176664 | /* The next entry is on the current page. */ |
| 176665 | assert_nc( iOff<=pIter->iEndofDoclist ); |
| 176666 | if( iOff>=pIter->iEndofDoclist ){ |
| 176667 | bNewTerm = 1; |
| 176668 | if( iOff!=fts5LeafFirstTermOff(pLeaf) ){ |
| 176669 | iOff += fts5GetVarint32(&a[iOff], nKeep); |
| 176670 | } |
| 176671 | }else{ |
| 176672 | u64 iDelta; |
| 176673 | iOff += sqlite3Fts5GetVarint(&a[iOff], &iDelta); |
| 176674 | pIter->iRowid += iDelta; |
| 176675 | assert_nc( iDelta>0 ); |
| 176676 | } |
| 176677 | pIter->iLeafOffset = iOff; |
| 176678 | |
| 176679 | }else if( pIter->pSeg==0 ){ |
| 176680 | const u8 *pList = 0; |
| 176681 | const char *zTerm = 0; |
| 176682 | int nList = 0; |
| 176683 | assert( (pIter->flags & FTS5_SEGITER_ONETERM) || pbNewTerm ); |
| 176684 | if( 0==(pIter->flags & FTS5_SEGITER_ONETERM) ){ |
| 176685 | sqlite3Fts5HashScanNext(p->pHash); |
| 176686 | sqlite3Fts5HashScanEntry(p->pHash, &zTerm, &pList, &nList); |
| 176687 | } |
| 176688 | if( pList==0 ){ |
| 176689 | fts5DataRelease(pIter->pLeaf); |
| 176690 | pIter->pLeaf = 0; |
| 176691 | }else{ |
| 176692 | pIter->pLeaf->p = (u8*)pList; |
| 176693 | pIter->pLeaf->nn = nList; |
| 176694 | pIter->pLeaf->szLeaf = nList; |
| 176695 | pIter->iEndofDoclist = nList+1; |
| 176696 | sqlite3Fts5BufferSet(&p->rc, &pIter->term, (int)strlen(zTerm), |
| 176697 | (u8*)zTerm); |
| 176698 | pIter->iLeafOffset = fts5GetVarint(pList, (u64*)&pIter->iRowid); |
| 176699 | *pbNewTerm = 1; |
| 176700 | } |
| 176701 | }else{ |
| 176702 | iOff = 0; |
| 176703 | /* Next entry is not on the current page */ |
| 176704 | while( iOff==0 ){ |
| 176705 | fts5SegIterNextPage(p, pIter); |
| 176706 | pLeaf = pIter->pLeaf; |
| 176707 | if( pLeaf==0 ) break; |
| 176708 | ASSERT_SZLEAF_OK(pLeaf); |
| 176709 | if( (iOff = fts5LeafFirstRowidOff(pLeaf)) && iOff<pLeaf->szLeaf ){ |
| 176710 | iOff += sqlite3Fts5GetVarint(&pLeaf->p[iOff], (u64*)&pIter->iRowid); |
| 176711 | pIter->iLeafOffset = iOff; |
| 176712 | |
| 176713 | if( pLeaf->nn>pLeaf->szLeaf ){ |
| 176714 | pIter->iPgidxOff = pLeaf->szLeaf + fts5GetVarint32( |
| 176715 | &pLeaf->p[pLeaf->szLeaf], pIter->iEndofDoclist |
| 176716 | ); |
| 176717 | } |
| 176718 | |
| 176719 | } |
| 176720 | else if( pLeaf->nn>pLeaf->szLeaf ){ |
| 176721 | pIter->iPgidxOff = pLeaf->szLeaf + fts5GetVarint32( |
| 176722 | &pLeaf->p[pLeaf->szLeaf], iOff |
| 176723 | ); |
| 176724 | pIter->iLeafOffset = iOff; |
| 176725 | pIter->iEndofDoclist = iOff; |
| 176726 | bNewTerm = 1; |
| 176727 | } |
| 176728 | assert_nc( iOff<pLeaf->szLeaf ); |
| 176729 | if( iOff>pLeaf->szLeaf ){ |
| 176730 | p->rc = FTS5_CORRUPT; |
| 176731 | return; |
| 176732 | } |
| 176733 | } |
| 176734 | } |
| 176735 | |
| 176736 | /* Check if the iterator is now at EOF. If so, return early. */ |
| 176737 | if( pIter->pLeaf ){ |
| 176738 | if( bNewTerm ){ |
| 176739 | if( pIter->flags & FTS5_SEGITER_ONETERM ){ |
| 176740 | fts5DataRelease(pIter->pLeaf); |
| 176741 | pIter->pLeaf = 0; |
| 176742 | }else{ |
| 176743 | fts5SegIterLoadTerm(p, pIter, nKeep); |
| 176744 | fts5SegIterLoadNPos(p, pIter); |
| 176745 | if( pbNewTerm ) *pbNewTerm = 1; |
| 176746 | } |
| 176747 | }else{ |
| 176748 | /* The following could be done by calling fts5SegIterLoadNPos(). But |
| 176749 | ** this block is particularly performance critical, so equivalent |
| 176750 | ** code is inlined. |
| 176751 | ** |
| 176752 | ** Later: Switched back to fts5SegIterLoadNPos() because it supports |
| 176753 | ** detail=none mode. Not ideal. |
| 176754 | */ |
| 176755 | int nSz; |
| 176756 | assert( p->rc==SQLITE_OK ); |
| 176757 | fts5FastGetVarint32(pIter->pLeaf->p, pIter->iLeafOffset, nSz); |
| 176758 | pIter->bDel = (nSz & 0x0001); |
| 176759 | pIter->nPos = nSz>>1; |
| 176760 | assert_nc( pIter->nPos>=0 ); |
| 176761 | } |
| 176762 | } |
| 176763 | } |
| 176764 | |
| 176765 | #define SWAPVAL(T, a, b) { T tmp; tmp=a; a=b; b=tmp; } |
| 176766 | |
| 176767 | #define fts5IndexSkipVarint(a, iOff) { \ |
| 176768 | int iEnd = iOff+9; \ |
| 176769 | while( (a[iOff++] & 0x80) && iOff<iEnd ); \ |
| 176770 | } |
| 176771 | |
| 176772 | /* |
| 176773 | ** Iterator pIter currently points to the first rowid in a doclist. This |
| 176774 | ** function sets the iterator up so that iterates in reverse order through |
| 176775 | ** the doclist. |
| @@ -175898,11 +176787,21 @@ | |
| 176787 | Fts5Data *pLeaf = pIter->pLeaf; /* Current leaf data */ |
| 176788 | |
| 176789 | /* Currently, Fts5SegIter.iLeafOffset points to the first byte of |
| 176790 | ** position-list content for the current rowid. Back it up so that it |
| 176791 | ** points to the start of the position-list size field. */ |
| 176792 | int iPoslist; |
| 176793 | if( pIter->iTermLeafPgno==pIter->iLeafPgno ){ |
| 176794 | iPoslist = pIter->iTermLeafOffset; |
| 176795 | }else{ |
| 176796 | iPoslist = 4; |
| 176797 | } |
| 176798 | fts5IndexSkipVarint(pLeaf->p, iPoslist); |
| 176799 | assert( p->pConfig->eDetail==FTS5_DETAIL_NONE || iPoslist==( |
| 176800 | pIter->iLeafOffset - sqlite3Fts5GetVarintLen(pIter->nPos*2+pIter->bDel) |
| 176801 | )); |
| 176802 | pIter->iLeafOffset = iPoslist; |
| 176803 | |
| 176804 | /* If this condition is true then the largest rowid for the current |
| 176805 | ** term may not be stored on the current page. So search forward to |
| 176806 | ** see where said rowid really is. */ |
| 176807 | if( pIter->iEndofDoclist>=pLeaf->szLeaf ){ |
| @@ -175982,15 +176881,10 @@ | |
| 176881 | } |
| 176882 | |
| 176883 | pIter->pDlidx = fts5DlidxIterInit(p, bRev, iSeg, pIter->iTermLeafPgno); |
| 176884 | } |
| 176885 | |
| 176886 | /* |
| 176887 | ** The iterator object passed as the second argument currently contains |
| 176888 | ** no valid values except for the Fts5SegIter.pLeaf member variable. This |
| 176889 | ** function searches the leaf page for a term matching (pTerm/nTerm). |
| 176890 | ** |
| @@ -176189,10 +177083,12 @@ | |
| 177083 | fts5SegIterReverse(p, pIter); |
| 177084 | } |
| 177085 | } |
| 177086 | } |
| 177087 | |
| 177088 | fts5SegIterSetNext(p, pIter); |
| 177089 | |
| 177090 | /* Either: |
| 177091 | ** |
| 177092 | ** 1) an error has occurred, or |
| 177093 | ** 2) the iterator points to EOF, or |
| 177094 | ** 3) the iterator points to an entry with term (pTerm/nTerm), or |
| @@ -176246,19 +177142,21 @@ | |
| 177142 | if( pLeaf==0 ) return; |
| 177143 | pLeaf->p = (u8*)pList; |
| 177144 | pLeaf->nn = pLeaf->szLeaf = nList; |
| 177145 | pIter->pLeaf = pLeaf; |
| 177146 | pIter->iLeafOffset = fts5GetVarint(pLeaf->p, (u64*)&pIter->iRowid); |
| 177147 | pIter->iEndofDoclist = pLeaf->nn; |
| 177148 | |
| 177149 | if( flags & FTS5INDEX_QUERY_DESC ){ |
| 177150 | pIter->flags |= FTS5_SEGITER_REVERSE; |
| 177151 | fts5SegIterReverseInitPage(p, pIter); |
| 177152 | }else{ |
| 177153 | fts5SegIterLoadNPos(p, pIter); |
| 177154 | } |
| 177155 | } |
| 177156 | |
| 177157 | fts5SegIterSetNext(p, pIter); |
| 177158 | } |
| 177159 | |
| 177160 | /* |
| 177161 | ** Zero the iterator passed as the only argument. |
| 177162 | */ |
| @@ -176498,11 +177396,11 @@ | |
| 177396 | bMove = 0; |
| 177397 | } |
| 177398 | } |
| 177399 | |
| 177400 | do{ |
| 177401 | if( bMove && p->rc==SQLITE_OK ) pIter->xNext(p, pIter, 0); |
| 177402 | if( pIter->pLeaf==0 ) break; |
| 177403 | if( bRev==0 && pIter->iRowid>=iMatch ) break; |
| 177404 | if( bRev!=0 && pIter->iRowid<=iMatch ) break; |
| 177405 | bMove = 1; |
| 177406 | }while( p->rc==SQLITE_OK ); |
| @@ -176532,11 +177430,13 @@ | |
| 177430 | ){ |
| 177431 | int i; |
| 177432 | for(i=(pIter->nSeg+iChanged)/2; i>=iMinset && p->rc==SQLITE_OK; i=i/2){ |
| 177433 | int iEq; |
| 177434 | if( (iEq = fts5MultiIterDoCompare(pIter, i)) ){ |
| 177435 | Fts5SegIter *pSeg = &pIter->aSeg[iEq]; |
| 177436 | assert( p->rc==SQLITE_OK ); |
| 177437 | pSeg->xNext(p, pSeg, 0); |
| 177438 | i = pIter->nSeg + iEq; |
| 177439 | } |
| 177440 | } |
| 177441 | } |
| 177442 | |
| @@ -176619,11 +177519,11 @@ | |
| 177519 | Fts5SegIter *pSeg = &pIter->aSeg[iFirst]; |
| 177520 | assert( p->rc==SQLITE_OK ); |
| 177521 | if( bUseFrom && pSeg->pDlidx ){ |
| 177522 | fts5SegIterNextFrom(p, pSeg, iFrom); |
| 177523 | }else{ |
| 177524 | pSeg->xNext(p, pSeg, &bNewTerm); |
| 177525 | } |
| 177526 | |
| 177527 | if( pSeg->pLeaf==0 || bNewTerm |
| 177528 | || fts5MultiIterAdvanceRowid(p, pIter, iFirst) |
| 177529 | ){ |
| @@ -176647,11 +177547,12 @@ | |
| 177547 | do { |
| 177548 | int iFirst = pIter->aFirst[1].iFirst; |
| 177549 | Fts5SegIter *pSeg = &pIter->aSeg[iFirst]; |
| 177550 | int bNewTerm = 0; |
| 177551 | |
| 177552 | assert( p->rc==SQLITE_OK ); |
| 177553 | pSeg->xNext(p, pSeg, &bNewTerm); |
| 177554 | if( pSeg->pLeaf==0 || bNewTerm |
| 177555 | || fts5MultiIterAdvanceRowid(p, pIter, iFirst) |
| 177556 | ){ |
| 177557 | fts5MultiIterAdvanced(p, pIter, iFirst, 1); |
| 177558 | fts5MultiIterSetEof(pIter); |
| @@ -176767,11 +177668,12 @@ | |
| 177668 | ** object and set the output variable to NULL. */ |
| 177669 | if( p->rc==SQLITE_OK ){ |
| 177670 | for(iIter=pNew->nSeg-1; iIter>0; iIter--){ |
| 177671 | int iEq; |
| 177672 | if( (iEq = fts5MultiIterDoCompare(pNew, iIter)) ){ |
| 177673 | Fts5SegIter *pSeg = &pNew->aSeg[iEq]; |
| 177674 | if( p->rc==SQLITE_OK ) pSeg->xNext(p, pSeg, 0); |
| 177675 | fts5MultiIterAdvanced(p, pNew, iEq, iIter); |
| 177676 | } |
| 177677 | } |
| 177678 | fts5MultiIterSetEof(pNew); |
| 177679 | fts5AssertMultiIterSetup(p, pNew); |
| @@ -176817,10 +177719,11 @@ | |
| 177719 | } |
| 177720 | pData = 0; |
| 177721 | }else{ |
| 177722 | pNew->bEof = 1; |
| 177723 | } |
| 177724 | fts5SegIterSetNext(p, pIter); |
| 177725 | |
| 177726 | *ppOut = pNew; |
| 177727 | } |
| 177728 | |
| 177729 | fts5DataRelease(pData); |
| @@ -176885,10 +177788,13 @@ | |
| 177788 | Fts5Data *pData = 0; |
| 177789 | u8 *pChunk = &pSeg->pLeaf->p[pSeg->iLeafOffset]; |
| 177790 | int nChunk = MIN(nRem, pSeg->pLeaf->szLeaf - pSeg->iLeafOffset); |
| 177791 | int pgno = pSeg->iLeafPgno; |
| 177792 | int pgnoSave = 0; |
| 177793 | |
| 177794 | /* This function does notmwork with detail=none databases. */ |
| 177795 | assert( p->pConfig->eDetail!=FTS5_DETAIL_NONE ); |
| 177796 | |
| 177797 | if( (pSeg->flags & FTS5_SEGITER_REVERSE)==0 ){ |
| 177798 | pgnoSave = pgno+1; |
| 177799 | } |
| 177800 | |
| @@ -177309,12 +178215,11 @@ | |
| 178215 | ** Append a rowid and position-list size field to the writers output. |
| 178216 | */ |
| 178217 | static void fts5WriteAppendRowid( |
| 178218 | Fts5Index *p, |
| 178219 | Fts5SegWriter *pWriter, |
| 178220 | i64 iRowid |
| 178221 | ){ |
| 178222 | if( p->rc==SQLITE_OK ){ |
| 178223 | Fts5PageWriter *pPage = &pWriter->writer; |
| 178224 | |
| 178225 | if( (pPage->buf.n + pPage->pgidx.n)>=p->pConfig->pgsz ){ |
| @@ -177337,12 +178242,10 @@ | |
| 178242 | fts5BufferAppendVarint(&p->rc, &pPage->buf, iRowid - pWriter->iPrevRowid); |
| 178243 | } |
| 178244 | pWriter->iPrevRowid = iRowid; |
| 178245 | pWriter->bFirstRowidInDoclist = 0; |
| 178246 | pWriter->bFirstRowidInPage = 0; |
| 178247 | } |
| 178248 | } |
| 178249 | |
| 178250 | static void fts5WriteAppendPoslistData( |
| 178251 | Fts5Index *p, |
| @@ -177534,10 +178437,11 @@ | |
| 178437 | int nInput; /* Number of input segments */ |
| 178438 | Fts5SegWriter writer; /* Writer object */ |
| 178439 | Fts5StructureSegment *pSeg; /* Output segment */ |
| 178440 | Fts5Buffer term; |
| 178441 | int bOldest; /* True if the output segment is the oldest */ |
| 178442 | int eDetail = p->pConfig->eDetail; |
| 178443 | |
| 178444 | assert( iLvl<pStruct->nLevel ); |
| 178445 | assert( pLvl->nMerge<=pLvl->nSeg ); |
| 178446 | |
| 178447 | memset(&writer, 0, sizeof(Fts5SegWriter)); |
| @@ -177603,15 +178507,25 @@ | |
| 178507 | fts5BufferSet(&p->rc, &term, nTerm, pTerm); |
| 178508 | } |
| 178509 | |
| 178510 | /* Append the rowid to the output */ |
| 178511 | /* WRITEPOSLISTSIZE */ |
| 178512 | fts5WriteAppendRowid(p, &writer, fts5MultiIterRowid(pIter)); |
| 178513 | |
| 178514 | if( eDetail==FTS5_DETAIL_NONE ){ |
| 178515 | if( pSegIter->bDel ){ |
| 178516 | fts5BufferAppendVarint(&p->rc, &writer.writer.buf, 0); |
| 178517 | if( pSegIter->nPos>0 ){ |
| 178518 | fts5BufferAppendVarint(&p->rc, &writer.writer.buf, 0); |
| 178519 | } |
| 178520 | } |
| 178521 | }else{ |
| 178522 | /* Append the position-list data to the output */ |
| 178523 | nPos = pSegIter->nPos*2 + pSegIter->bDel; |
| 178524 | fts5BufferAppendVarint(&p->rc, &writer.writer.buf, nPos); |
| 178525 | fts5ChunkIterate(p, pSegIter, (void*)&writer, fts5MergeChunkCallback); |
| 178526 | } |
| 178527 | } |
| 178528 | |
| 178529 | /* Flush the last leaf page to disk. Set the output segment b-tree height |
| 178530 | ** and last leaf page number at the same time. */ |
| 178531 | fts5WriteFinish(p, &writer, &pSeg->pgnoLast); |
| @@ -177795,11 +178709,11 @@ | |
| 178709 | pStruct = fts5StructureRead(p); |
| 178710 | iSegid = fts5AllocateSegid(p, pStruct); |
| 178711 | |
| 178712 | if( iSegid ){ |
| 178713 | const int pgsz = p->pConfig->pgsz; |
| 178714 | int eDetail = p->pConfig->eDetail; |
| 178715 | Fts5StructureSegment *pSeg; /* New segment within pStruct */ |
| 178716 | Fts5Buffer *pBuf; /* Buffer in which to assemble leaf page */ |
| 178717 | Fts5Buffer *pPgidx; /* Buffer in which to assemble pgidx */ |
| 178718 | |
| 178719 | Fts5SegWriter writer; |
| @@ -177838,16 +178752,11 @@ | |
| 178752 | |
| 178753 | /* The entire doclist will not fit on this leaf. The following |
| 178754 | ** loop iterates through the poslists that make up the current |
| 178755 | ** doclist. */ |
| 178756 | while( p->rc==SQLITE_OK && iOff<nDoclist ){ |
| 178757 | iOff += fts5GetVarint(&pDoclist[iOff], (u64*)&iDelta); |
| 178758 | iRowid += iDelta; |
| 178759 | |
| 178760 | if( writer.bFirstRowidInPage ){ |
| 178761 | fts5PutU16(&pBuf->p[0], (u16)pBuf->n); /* first rowid on page */ |
| 178762 | pBuf->n += sqlite3Fts5PutVarint(&pBuf->p[pBuf->n], iRowid); |
| @@ -177856,38 +178765,56 @@ | |
| 178765 | }else{ |
| 178766 | pBuf->n += sqlite3Fts5PutVarint(&pBuf->p[pBuf->n], iDelta); |
| 178767 | } |
| 178768 | assert( pBuf->n<=pBuf->nSpace ); |
| 178769 | |
| 178770 | if( eDetail==FTS5_DETAIL_NONE ){ |
| 178771 | if( iOff<nDoclist && pDoclist[iOff]==0 ){ |
| 178772 | pBuf->p[pBuf->n++] = 0; |
| 178773 | iOff++; |
| 178774 | if( iOff<nDoclist && pDoclist[iOff]==0 ){ |
| 178775 | pBuf->p[pBuf->n++] = 0; |
| 178776 | iOff++; |
| 178777 | } |
| 178778 | } |
| 178779 | if( (pBuf->n + pPgidx->n)>=pgsz ){ |
| 178780 | fts5WriteFlushLeaf(p, &writer); |
| 178781 | } |
| 178782 | }else{ |
| 178783 | int bDummy; |
| 178784 | int nPos; |
| 178785 | int nCopy = fts5GetPoslistSize(&pDoclist[iOff], &nPos, &bDummy); |
| 178786 | nCopy += nPos; |
| 178787 | if( (pBuf->n + pPgidx->n + nCopy) <= pgsz ){ |
| 178788 | /* The entire poslist will fit on the current leaf. So copy |
| 178789 | ** it in one go. */ |
| 178790 | fts5BufferSafeAppendBlob(pBuf, &pDoclist[iOff], nCopy); |
| 178791 | }else{ |
| 178792 | /* The entire poslist will not fit on this leaf. So it needs |
| 178793 | ** to be broken into sections. The only qualification being |
| 178794 | ** that each varint must be stored contiguously. */ |
| 178795 | const u8 *pPoslist = &pDoclist[iOff]; |
| 178796 | int iPos = 0; |
| 178797 | while( p->rc==SQLITE_OK ){ |
| 178798 | int nSpace = pgsz - pBuf->n - pPgidx->n; |
| 178799 | int n = 0; |
| 178800 | if( (nCopy - iPos)<=nSpace ){ |
| 178801 | n = nCopy - iPos; |
| 178802 | }else{ |
| 178803 | n = fts5PoslistPrefix(&pPoslist[iPos], nSpace); |
| 178804 | } |
| 178805 | assert( n>0 ); |
| 178806 | fts5BufferSafeAppendBlob(pBuf, &pPoslist[iPos], n); |
| 178807 | iPos += n; |
| 178808 | if( (pBuf->n + pPgidx->n)>=pgsz ){ |
| 178809 | fts5WriteFlushLeaf(p, &writer); |
| 178810 | } |
| 178811 | if( iPos>=nCopy ) break; |
| 178812 | } |
| 178813 | } |
| 178814 | iOff += nCopy; |
| 178815 | } |
| 178816 | } |
| 178817 | } |
| 178818 | |
| 178819 | /* TODO2: Doclist terminator written here. */ |
| 178820 | /* pBuf->p[pBuf->n++] = '\0'; */ |
| @@ -178018,10 +178945,18 @@ | |
| 178945 | Fts5Buffer *pBuf; /* Append to this buffer */ |
| 178946 | Fts5Colset *pColset; /* Restrict matches to this column */ |
| 178947 | int eState; /* See above */ |
| 178948 | }; |
| 178949 | |
| 178950 | typedef struct PoslistOffsetsCtx PoslistOffsetsCtx; |
| 178951 | struct PoslistOffsetsCtx { |
| 178952 | Fts5Buffer *pBuf; /* Append to this buffer */ |
| 178953 | Fts5Colset *pColset; /* Restrict matches to this column */ |
| 178954 | int iRead; |
| 178955 | int iWrite; |
| 178956 | }; |
| 178957 | |
| 178958 | /* |
| 178959 | ** TODO: Make this more efficient! |
| 178960 | */ |
| 178961 | static int fts5IndexColsetTest(Fts5Colset *pColset, int iCol){ |
| 178962 | int i; |
| @@ -178028,10 +178963,32 @@ | |
| 178963 | for(i=0; i<pColset->nCol; i++){ |
| 178964 | if( pColset->aiCol[i]==iCol ) return 1; |
| 178965 | } |
| 178966 | return 0; |
| 178967 | } |
| 178968 | |
| 178969 | static void fts5PoslistOffsetsCallback( |
| 178970 | Fts5Index *p, |
| 178971 | void *pContext, |
| 178972 | const u8 *pChunk, int nChunk |
| 178973 | ){ |
| 178974 | PoslistOffsetsCtx *pCtx = (PoslistOffsetsCtx*)pContext; |
| 178975 | assert_nc( nChunk>=0 ); |
| 178976 | if( nChunk>0 ){ |
| 178977 | int i = 0; |
| 178978 | while( i<nChunk ){ |
| 178979 | int iVal; |
| 178980 | i += fts5GetVarint32(&pChunk[i], iVal); |
| 178981 | iVal += pCtx->iRead - 2; |
| 178982 | pCtx->iRead = iVal; |
| 178983 | if( fts5IndexColsetTest(pCtx->pColset, iVal) ){ |
| 178984 | fts5BufferSafeAppendVarint(pCtx->pBuf, iVal + 2 - pCtx->iWrite); |
| 178985 | pCtx->iWrite = iVal; |
| 178986 | } |
| 178987 | } |
| 178988 | } |
| 178989 | } |
| 178990 | |
| 178991 | static void fts5PoslistFilterCallback( |
| 178992 | Fts5Index *p, |
| 178993 | void *pContext, |
| 178994 | const u8 *pChunk, int nChunk |
| @@ -178096,16 +179053,24 @@ | |
| 179053 | ){ |
| 179054 | if( 0==fts5BufferGrow(&p->rc, pBuf, pSeg->nPos) ){ |
| 179055 | if( pColset==0 ){ |
| 179056 | fts5ChunkIterate(p, pSeg, (void*)pBuf, fts5PoslistCallback); |
| 179057 | }else{ |
| 179058 | if( p->pConfig->eDetail==FTS5_DETAIL_FULL ){ |
| 179059 | PoslistCallbackCtx sCtx; |
| 179060 | sCtx.pBuf = pBuf; |
| 179061 | sCtx.pColset = pColset; |
| 179062 | sCtx.eState = fts5IndexColsetTest(pColset, 0); |
| 179063 | assert( sCtx.eState==0 || sCtx.eState==1 ); |
| 179064 | fts5ChunkIterate(p, pSeg, (void*)&sCtx, fts5PoslistFilterCallback); |
| 179065 | }else{ |
| 179066 | PoslistOffsetsCtx sCtx; |
| 179067 | memset(&sCtx, 0, sizeof(sCtx)); |
| 179068 | sCtx.pBuf = pBuf; |
| 179069 | sCtx.pColset = pColset; |
| 179070 | fts5ChunkIterate(p, pSeg, (void*)&sCtx, fts5PoslistOffsetsCallback); |
| 179071 | } |
| 179072 | } |
| 179073 | } |
| 179074 | } |
| 179075 | |
| 179076 | /* |
| @@ -178144,10 +179109,20 @@ | |
| 179109 | prev = *p++; |
| 179110 | } |
| 179111 | return p - (*pa); |
| 179112 | } |
| 179113 | |
| 179114 | static int fts5AppendRowid( |
| 179115 | Fts5Index *p, |
| 179116 | i64 iDelta, |
| 179117 | Fts5IndexIter *pMulti, |
| 179118 | Fts5Colset *pColset, |
| 179119 | Fts5Buffer *pBuf |
| 179120 | ){ |
| 179121 | fts5BufferAppendVarint(&p->rc, pBuf, iDelta); |
| 179122 | return 0; |
| 179123 | } |
| 179124 | |
| 179125 | /* |
| 179126 | ** Iterator pMulti currently points to a valid entry (not EOF). This |
| 179127 | ** function appends the following to buffer pBuf: |
| 179128 | ** |
| @@ -178171,12 +179146,12 @@ | |
| 179146 | if( p->rc==SQLITE_OK ){ |
| 179147 | Fts5SegIter *pSeg = &pMulti->aSeg[ pMulti->aFirst[1].iFirst ]; |
| 179148 | assert( fts5MultiIterEof(p, pMulti)==0 ); |
| 179149 | assert( pSeg->nPos>0 ); |
| 179150 | if( 0==fts5BufferGrow(&p->rc, pBuf, pSeg->nPos+9+9) ){ |
| 179151 | if( p->pConfig->eDetail==FTS5_DETAIL_FULL |
| 179152 | && pSeg->iLeafOffset+pSeg->nPos<=pSeg->pLeaf->szLeaf |
| 179153 | && (pColset==0 || pColset->nCol==1) |
| 179154 | ){ |
| 179155 | const u8 *pPos = &pSeg->pLeaf->p[pSeg->iLeafOffset]; |
| 179156 | int nPos; |
| 179157 | if( pColset ){ |
| @@ -178217,16 +179192,16 @@ | |
| 179192 | sqlite3Fts5PutVarint(&pBuf->p[iSv2], nActual*2); |
| 179193 | } |
| 179194 | } |
| 179195 | } |
| 179196 | } |
| 179197 | } |
| 179198 | } |
| 179199 | |
| 179200 | return 0; |
| 179201 | } |
| 179202 | |
| 179203 | |
| 179204 | static void fts5DoclistIterNext(Fts5DoclistIter *pIter){ |
| 179205 | u8 *p = pIter->aPoslist + pIter->nSize + pIter->nPoslist; |
| 179206 | |
| 179207 | assert( pIter->aPoslist ); |
| @@ -178283,10 +179258,73 @@ | |
| 179258 | #define fts5MergeAppendDocid(pBuf, iLastRowid, iRowid) { \ |
| 179259 | assert( (pBuf)->n!=0 || (iLastRowid)==0 ); \ |
| 179260 | fts5BufferSafeAppendVarint((pBuf), (iRowid) - (iLastRowid)); \ |
| 179261 | (iLastRowid) = (iRowid); \ |
| 179262 | } |
| 179263 | |
| 179264 | /* |
| 179265 | ** Swap the contents of buffer *p1 with that of *p2. |
| 179266 | */ |
| 179267 | static void fts5BufferSwap(Fts5Buffer *p1, Fts5Buffer *p2){ |
| 179268 | Fts5Buffer tmp = *p1; |
| 179269 | *p1 = *p2; |
| 179270 | *p2 = tmp; |
| 179271 | } |
| 179272 | |
| 179273 | static void fts5NextRowid(Fts5Buffer *pBuf, int *piOff, i64 *piRowid){ |
| 179274 | int i = *piOff; |
| 179275 | if( i>=pBuf->n ){ |
| 179276 | *piOff = -1; |
| 179277 | }else{ |
| 179278 | u64 iVal; |
| 179279 | *piOff = i + sqlite3Fts5GetVarint(&pBuf->p[i], &iVal); |
| 179280 | *piRowid += iVal; |
| 179281 | } |
| 179282 | } |
| 179283 | |
| 179284 | /* |
| 179285 | ** This is the equivalent of fts5MergePrefixLists() for detail=none mode. |
| 179286 | ** In this case the buffers consist of a delta-encoded list of rowids only. |
| 179287 | */ |
| 179288 | static void fts5MergeRowidLists( |
| 179289 | Fts5Index *p, /* FTS5 backend object */ |
| 179290 | Fts5Buffer *p1, /* First list to merge */ |
| 179291 | Fts5Buffer *p2 /* Second list to merge */ |
| 179292 | ){ |
| 179293 | int i1 = 0; |
| 179294 | int i2 = 0; |
| 179295 | i64 iRowid1 = 0; |
| 179296 | i64 iRowid2 = 0; |
| 179297 | i64 iOut = 0; |
| 179298 | |
| 179299 | Fts5Buffer out; |
| 179300 | memset(&out, 0, sizeof(out)); |
| 179301 | sqlite3Fts5BufferSize(&p->rc, &out, p1->n + p2->n); |
| 179302 | if( p->rc ) return; |
| 179303 | |
| 179304 | fts5NextRowid(p1, &i1, &iRowid1); |
| 179305 | fts5NextRowid(p2, &i2, &iRowid2); |
| 179306 | while( i1>=0 || i2>=0 ){ |
| 179307 | if( i1>=0 && (i2<0 || iRowid1<iRowid2) ){ |
| 179308 | assert( iOut==0 || iRowid1>iOut ); |
| 179309 | fts5BufferSafeAppendVarint(&out, iRowid1 - iOut); |
| 179310 | iOut = iRowid1; |
| 179311 | fts5NextRowid(p1, &i1, &iRowid1); |
| 179312 | }else{ |
| 179313 | assert( iOut==0 || iRowid2>iOut ); |
| 179314 | fts5BufferSafeAppendVarint(&out, iRowid2 - iOut); |
| 179315 | iOut = iRowid2; |
| 179316 | if( i1>=0 && iRowid1==iRowid2 ){ |
| 179317 | fts5NextRowid(p1, &i1, &iRowid1); |
| 179318 | } |
| 179319 | fts5NextRowid(p2, &i2, &iRowid2); |
| 179320 | } |
| 179321 | } |
| 179322 | |
| 179323 | fts5BufferSwap(&out, p1); |
| 179324 | fts5BufferFree(&out); |
| 179325 | } |
| 179326 | |
| 179327 | /* |
| 179328 | ** Buffers p1 and p2 contain doclists. This function merges the content |
| 179329 | ** of the two doclists together and sets buffer p1 to the result before |
| 179330 | ** returning. |
| @@ -178352,11 +179390,13 @@ | |
| 179390 | sqlite3Fts5PoslistNext64(a2, i2.nPoslist, &iOff2, &iPos2); |
| 179391 | if( iPos1==iPos2 ){ |
| 179392 | sqlite3Fts5PoslistNext64(a1, i1.nPoslist, &iOff1,&iPos1); |
| 179393 | } |
| 179394 | } |
| 179395 | if( iNew!=writer.iPrev || tmp.n==0 ){ |
| 179396 | p->rc = sqlite3Fts5PoslistWriterAppend(&tmp, &writer, iNew); |
| 179397 | } |
| 179398 | } |
| 179399 | |
| 179400 | /* WRITEPOSLISTSIZE */ |
| 179401 | fts5BufferSafeAppendVarint(&out, tmp.n * 2); |
| 179402 | fts5BufferSafeAppendBlob(&out, tmp.p, tmp.n); |
| @@ -178369,16 +179409,10 @@ | |
| 179409 | fts5BufferFree(&tmp); |
| 179410 | fts5BufferFree(&out); |
| 179411 | } |
| 179412 | } |
| 179413 | |
| 179414 | static void fts5SetupPrefixIter( |
| 179415 | Fts5Index *p, /* Index to read from */ |
| 179416 | int bDesc, /* True for "ORDER BY rowid DESC" */ |
| 179417 | const u8 *pToken, /* Buffer containing prefix to match */ |
| 179418 | int nToken, /* Size of buffer pToken in bytes */ |
| @@ -178386,10 +179420,20 @@ | |
| 179420 | Fts5IndexIter **ppIter /* OUT: New iterator */ |
| 179421 | ){ |
| 179422 | Fts5Structure *pStruct; |
| 179423 | Fts5Buffer *aBuf; |
| 179424 | const int nBuf = 32; |
| 179425 | |
| 179426 | void (*xMerge)(Fts5Index*, Fts5Buffer*, Fts5Buffer*); |
| 179427 | int (*xAppend)(Fts5Index*, i64, Fts5IndexIter*, Fts5Colset*, Fts5Buffer*); |
| 179428 | if( p->pConfig->eDetail==FTS5_DETAIL_NONE ){ |
| 179429 | xMerge = fts5MergeRowidLists; |
| 179430 | xAppend = fts5AppendRowid; |
| 179431 | }else{ |
| 179432 | xMerge = fts5MergePrefixLists; |
| 179433 | xAppend = fts5AppendPoslist; |
| 179434 | } |
| 179435 | |
| 179436 | aBuf = (Fts5Buffer*)fts5IdxMalloc(p, sizeof(Fts5Buffer)*nBuf); |
| 179437 | pStruct = fts5StructureRead(p); |
| 179438 | |
| 179439 | if( aBuf && pStruct ){ |
| @@ -178419,25 +179463,25 @@ | |
| 179463 | assert( i<nBuf ); |
| 179464 | if( aBuf[i].n==0 ){ |
| 179465 | fts5BufferSwap(&doclist, &aBuf[i]); |
| 179466 | fts5BufferZero(&doclist); |
| 179467 | }else{ |
| 179468 | xMerge(p, &doclist, &aBuf[i]); |
| 179469 | fts5BufferZero(&aBuf[i]); |
| 179470 | } |
| 179471 | } |
| 179472 | iLastRowid = 0; |
| 179473 | } |
| 179474 | |
| 179475 | if( !xAppend(p, iRowid-iLastRowid, p1, pColset, &doclist) ){ |
| 179476 | iLastRowid = iRowid; |
| 179477 | } |
| 179478 | } |
| 179479 | |
| 179480 | for(i=0; i<nBuf; i++){ |
| 179481 | if( p->rc==SQLITE_OK ){ |
| 179482 | xMerge(p, &doclist, &aBuf[i]); |
| 179483 | } |
| 179484 | fts5BufferFree(&aBuf[i]); |
| 179485 | } |
| 179486 | fts5MultiIterFree(p, p1); |
| 179487 | |
| @@ -178463,11 +179507,11 @@ | |
| 179507 | static int sqlite3Fts5IndexBeginWrite(Fts5Index *p, int bDelete, i64 iRowid){ |
| 179508 | assert( p->rc==SQLITE_OK ); |
| 179509 | |
| 179510 | /* Allocate the hash table if it has not already been allocated */ |
| 179511 | if( p->pHash==0 ){ |
| 179512 | p->rc = sqlite3Fts5HashNew(p->pConfig, &p->pHash, &p->nPendingData); |
| 179513 | } |
| 179514 | |
| 179515 | /* Flush the hash table to disk if required */ |
| 179516 | if( iRowid<p->iWriteRowid |
| 179517 | || (iRowid==p->iWriteRowid && p->bDelete==0) |
| @@ -178584,11 +179628,15 @@ | |
| 179628 | /* |
| 179629 | ** Argument p points to a buffer containing utf-8 text that is n bytes in |
| 179630 | ** size. Return the number of bytes in the nChar character prefix of the |
| 179631 | ** buffer, or 0 if there are less than nChar characters in total. |
| 179632 | */ |
| 179633 | static int sqlite3Fts5IndexCharlenToBytelen( |
| 179634 | const char *p, |
| 179635 | int nByte, |
| 179636 | int nChar |
| 179637 | ){ |
| 179638 | int n = 0; |
| 179639 | int i; |
| 179640 | for(i=0; i<nChar; i++){ |
| 179641 | if( n>=nByte ) return 0; /* Input contains fewer than nChar chars */ |
| 179642 | if( (unsigned char)p[n++]>=0xc0 ){ |
| @@ -178641,11 +179689,12 @@ | |
| 179689 | rc = sqlite3Fts5HashWrite( |
| 179690 | p->pHash, p->iWriteRowid, iCol, iPos, FTS5_MAIN_PREFIX, pToken, nToken |
| 179691 | ); |
| 179692 | |
| 179693 | for(i=0; i<pConfig->nPrefix && rc==SQLITE_OK; i++){ |
| 179694 | const int nChar = pConfig->aPrefix[i]; |
| 179695 | int nByte = sqlite3Fts5IndexCharlenToBytelen(pToken, nToken, nChar); |
| 179696 | if( nByte ){ |
| 179697 | rc = sqlite3Fts5HashWrite(p->pHash, |
| 179698 | p->iWriteRowid, iCol, iPos, (char)(FTS5_MAIN_PREFIX+i+1), pToken, |
| 179699 | nByte |
| 179700 | ); |
| @@ -178819,13 +179868,20 @@ | |
| 179868 | const u8 **pp, /* OUT: Pointer to position-list data */ |
| 179869 | int *pn, /* OUT: Size of position-list in bytes */ |
| 179870 | i64 *piRowid /* OUT: Current rowid */ |
| 179871 | ){ |
| 179872 | Fts5SegIter *pSeg = &pIter->aSeg[ pIter->aFirst[1].iFirst ]; |
| 179873 | int eDetail = pIter->pIndex->pConfig->eDetail; |
| 179874 | |
| 179875 | assert( pIter->pIndex->rc==SQLITE_OK ); |
| 179876 | *piRowid = pSeg->iRowid; |
| 179877 | if( eDetail==FTS5_DETAIL_NONE ){ |
| 179878 | *pn = pSeg->nPos; |
| 179879 | }else |
| 179880 | if( eDetail==FTS5_DETAIL_FULL |
| 179881 | && pSeg->iLeafOffset+pSeg->nPos<=pSeg->pLeaf->szLeaf |
| 179882 | ){ |
| 179883 | u8 *pPos = &pSeg->pLeaf->p[pSeg->iLeafOffset]; |
| 179884 | if( pColset==0 || pIter->bFiltered ){ |
| 179885 | *pn = pSeg->nPos; |
| 179886 | *pp = pPos; |
| 179887 | }else if( pColset->nCol==1 ){ |
| @@ -178838,15 +179894,28 @@ | |
| 179894 | *pn = pIter->poslist.n; |
| 179895 | } |
| 179896 | }else{ |
| 179897 | fts5BufferZero(&pIter->poslist); |
| 179898 | fts5SegiterPoslist(pIter->pIndex, pSeg, pColset, &pIter->poslist); |
| 179899 | if( eDetail==FTS5_DETAIL_FULL ){ |
| 179900 | *pp = pIter->poslist.p; |
| 179901 | } |
| 179902 | *pn = pIter->poslist.n; |
| 179903 | } |
| 179904 | return fts5IndexReturn(pIter->pIndex); |
| 179905 | } |
| 179906 | |
| 179907 | static int sqlite3Fts5IterCollist( |
| 179908 | Fts5IndexIter *pIter, |
| 179909 | const u8 **pp, /* OUT: Pointer to position-list data */ |
| 179910 | int *pn /* OUT: Size of position-list in bytes */ |
| 179911 | ){ |
| 179912 | assert( pIter->pIndex->pConfig->eDetail==FTS5_DETAIL_COLUMNS ); |
| 179913 | *pp = pIter->poslist.p; |
| 179914 | *pn = pIter->poslist.n; |
| 179915 | return SQLITE_OK; |
| 179916 | } |
| 179917 | |
| 179918 | /* |
| 179919 | ** This function is similar to sqlite3Fts5IterPoslist(), except that it |
| 179920 | ** copies the position list into the buffer supplied as the second |
| 179921 | ** argument. |
| @@ -178957,11 +180026,11 @@ | |
| 180026 | */ |
| 180027 | |
| 180028 | /* |
| 180029 | ** Return a simple checksum value based on the arguments. |
| 180030 | */ |
| 180031 | static u64 sqlite3Fts5IndexEntryCksum( |
| 180032 | i64 iRowid, |
| 180033 | int iCol, |
| 180034 | int iPos, |
| 180035 | int iIdx, |
| 180036 | const char *pTerm, |
| @@ -179027,34 +180096,41 @@ | |
| 180096 | const char *z, /* Index key to query for */ |
| 180097 | int n, /* Size of index key in bytes */ |
| 180098 | int flags, /* Flags for Fts5IndexQuery */ |
| 180099 | u64 *pCksum /* IN/OUT: Checksum value */ |
| 180100 | ){ |
| 180101 | int eDetail = p->pConfig->eDetail; |
| 180102 | u64 cksum = *pCksum; |
| 180103 | Fts5IndexIter *pIdxIter = 0; |
| 180104 | Fts5Buffer buf = {0, 0, 0}; |
| 180105 | int rc = sqlite3Fts5IndexQuery(p, z, n, flags, 0, &pIdxIter); |
| 180106 | |
| 180107 | while( rc==SQLITE_OK && 0==sqlite3Fts5IterEof(pIdxIter) ){ |
| 180108 | i64 rowid = sqlite3Fts5IterRowid(pIdxIter); |
| 180109 | |
| 180110 | if( eDetail==FTS5_DETAIL_NONE ){ |
| 180111 | cksum ^= sqlite3Fts5IndexEntryCksum(rowid, 0, 0, iIdx, z, n); |
| 180112 | }else{ |
| 180113 | rc = sqlite3Fts5IterPoslistBuffer(pIdxIter, &buf); |
| 180114 | if( rc==SQLITE_OK ){ |
| 180115 | Fts5PoslistReader sReader; |
| 180116 | for(sqlite3Fts5PoslistReaderInit(buf.p, buf.n, &sReader); |
| 180117 | sReader.bEof==0; |
| 180118 | sqlite3Fts5PoslistReaderNext(&sReader) |
| 180119 | ){ |
| 180120 | int iCol = FTS5_POS2COLUMN(sReader.iPos); |
| 180121 | int iOff = FTS5_POS2OFFSET(sReader.iPos); |
| 180122 | cksum ^= sqlite3Fts5IndexEntryCksum(rowid, iCol, iOff, iIdx, z, n); |
| 180123 | } |
| 180124 | } |
| 180125 | } |
| 180126 | if( rc==SQLITE_OK ){ |
| 180127 | rc = sqlite3Fts5IterNext(pIdxIter); |
| 180128 | } |
| 180129 | } |
| 180130 | sqlite3Fts5IterClose(pIdxIter); |
| 180131 | fts5BufferFree(&buf); |
| 180132 | |
| 180133 | *pCksum = cksum; |
| 180134 | return rc; |
| 180135 | } |
| 180136 | |
| @@ -179344,18 +180420,19 @@ | |
| 180420 | |
| 180421 | |
| 180422 | /* |
| 180423 | ** Run internal checks to ensure that the FTS index (a) is internally |
| 180424 | ** consistent and (b) contains entries for which the XOR of the checksums |
| 180425 | ** as calculated by sqlite3Fts5IndexEntryCksum() is cksum. |
| 180426 | ** |
| 180427 | ** Return SQLITE_CORRUPT if any of the internal checks fail, or if the |
| 180428 | ** checksum does not match. Return SQLITE_OK if all checks pass without |
| 180429 | ** error, or some other SQLite error code if another error (e.g. OOM) |
| 180430 | ** occurs. |
| 180431 | */ |
| 180432 | static int sqlite3Fts5IndexIntegrityCheck(Fts5Index *p, u64 cksum){ |
| 180433 | int eDetail = p->pConfig->eDetail; |
| 180434 | u64 cksum2 = 0; /* Checksum based on contents of indexes */ |
| 180435 | Fts5Buffer poslist = {0,0,0}; /* Buffer used to hold a poslist */ |
| 180436 | Fts5IndexIter *pIter; /* Used to iterate through entire index */ |
| 180437 | Fts5Structure *pStruct; /* Index structure */ |
| 180438 | |
| @@ -179403,16 +180480,22 @@ | |
| 180480 | char *z = (char*)fts5MultiIterTerm(pIter, &n); |
| 180481 | |
| 180482 | /* If this is a new term, query for it. Update cksum3 with the results. */ |
| 180483 | fts5TestTerm(p, &term, z, n, cksum2, &cksum3); |
| 180484 | |
| 180485 | if( eDetail==FTS5_DETAIL_NONE ){ |
| 180486 | if( 0==fts5MultiIterIsEmpty(p, pIter) ){ |
| 180487 | cksum2 ^= sqlite3Fts5IndexEntryCksum(iRowid, 0, 0, -1, z, n); |
| 180488 | } |
| 180489 | }else{ |
| 180490 | poslist.n = 0; |
| 180491 | fts5SegiterPoslist(p, &pIter->aSeg[pIter->aFirst[1].iFirst], 0, &poslist); |
| 180492 | while( 0==sqlite3Fts5PoslistNext64(poslist.p, poslist.n, &iOff, &iPos) ){ |
| 180493 | int iCol = FTS5_POS2COLUMN(iPos); |
| 180494 | int iTokOff = FTS5_POS2OFFSET(iPos); |
| 180495 | cksum2 ^= sqlite3Fts5IndexEntryCksum(iRowid, iCol, iTokOff, -1, z, n); |
| 180496 | } |
| 180497 | } |
| 180498 | } |
| 180499 | fts5TestTerm(p, &term, 0, 0, cksum2, &cksum3); |
| 180500 | |
| 180501 | fts5MultiIterFree(p, pIter); |
| @@ -179424,38 +180507,10 @@ | |
| 180507 | #endif |
| 180508 | fts5BufferFree(&poslist); |
| 180509 | return fts5IndexReturn(p); |
| 180510 | } |
| 180511 | |
| 180512 | /************************************************************************* |
| 180513 | ************************************************************************** |
| 180514 | ** Below this point is the implementation of the fts5_decode() scalar |
| 180515 | ** function only. |
| 180516 | */ |
| @@ -180039,10 +181094,11 @@ | |
| 181094 | #define FTS5CSR_REQUIRE_DOCSIZE 0x02 |
| 181095 | #define FTS5CSR_REQUIRE_INST 0x04 |
| 181096 | #define FTS5CSR_EOF 0x08 |
| 181097 | #define FTS5CSR_FREE_ZRANK 0x10 |
| 181098 | #define FTS5CSR_REQUIRE_RESEEK 0x20 |
| 181099 | #define FTS5CSR_REQUIRE_POSLIST 0x40 |
| 181100 | |
| 181101 | #define BitFlagAllTest(x,y) (((x) & (y))==(y)) |
| 181102 | #define BitFlagTest(x,y) (((x) & (y))!=0) |
| 181103 | |
| 181104 | |
| @@ -180452,10 +181508,11 @@ | |
| 181508 | static void fts5CsrNewrow(Fts5Cursor *pCsr){ |
| 181509 | CsrFlagSet(pCsr, |
| 181510 | FTS5CSR_REQUIRE_CONTENT |
| 181511 | | FTS5CSR_REQUIRE_DOCSIZE |
| 181512 | | FTS5CSR_REQUIRE_INST |
| 181513 | | FTS5CSR_REQUIRE_POSLIST |
| 181514 | ); |
| 181515 | } |
| 181516 | |
| 181517 | static void fts5FreeCursorComponents(Fts5Cursor *pCsr){ |
| 181518 | Fts5Table *pTab = (Fts5Table*)(pCsr->base.pVtab); |
| @@ -180534,19 +181591,22 @@ | |
| 181591 | |
| 181592 | pSorter->iRowid = sqlite3_column_int64(pSorter->pStmt, 0); |
| 181593 | nBlob = sqlite3_column_bytes(pSorter->pStmt, 1); |
| 181594 | aBlob = a = sqlite3_column_blob(pSorter->pStmt, 1); |
| 181595 | |
| 181596 | /* nBlob==0 in detail=none mode. */ |
| 181597 | if( nBlob>0 ){ |
| 181598 | for(i=0; i<(pSorter->nIdx-1); i++){ |
| 181599 | int iVal; |
| 181600 | a += fts5GetVarint32(a, iVal); |
| 181601 | iOff += iVal; |
| 181602 | pSorter->aIdx[i] = iOff; |
| 181603 | } |
| 181604 | pSorter->aIdx[i] = &aBlob[nBlob] - a; |
| 181605 | pSorter->aPoslist = a; |
| 181606 | } |
| 181607 | |
| 181608 | fts5CsrNewrow(pCsr); |
| 181609 | } |
| 181610 | |
| 181611 | return rc; |
| 181612 | } |
| @@ -180980,10 +182040,11 @@ | |
| 182040 | assert( pCsr->iLastRowid==LARGEST_INT64 ); |
| 182041 | assert( pCsr->iFirstRowid==SMALLEST_INT64 ); |
| 182042 | pCsr->ePlan = FTS5_PLAN_SOURCE; |
| 182043 | pCsr->pExpr = pTab->pSortCsr->pExpr; |
| 182044 | rc = fts5CursorFirst(pTab, pCsr, bDesc); |
| 182045 | sqlite3Fts5ExprClearEof(pCsr->pExpr); |
| 182046 | }else if( pMatch ){ |
| 182047 | const char *zExpr = (const char*)sqlite3_value_text(apVal[0]); |
| 182048 | if( zExpr==0 ) zExpr = ""; |
| 182049 | |
| 182050 | rc = fts5CursorParseRank(pConfig, pCsr, pRank); |
| @@ -181409,10 +182470,12 @@ | |
| 182470 | fts5CheckTransactionState(pTab, FTS5_ROLLBACK, 0); |
| 182471 | rc = sqlite3Fts5StorageRollback(pTab->pStorage); |
| 182472 | return rc; |
| 182473 | } |
| 182474 | |
| 182475 | static int fts5CsrPoslist(Fts5Cursor*, int, const u8**, int*); |
| 182476 | |
| 182477 | static void *fts5ApiUserData(Fts5Context *pCtx){ |
| 182478 | Fts5Cursor *pCsr = (Fts5Cursor*)pCtx; |
| 182479 | return pCsr->pAux->pUserData; |
| 182480 | } |
| 182481 | |
| @@ -181458,21 +182521,76 @@ | |
| 182521 | static int fts5ApiPhraseSize(Fts5Context *pCtx, int iPhrase){ |
| 182522 | Fts5Cursor *pCsr = (Fts5Cursor*)pCtx; |
| 182523 | return sqlite3Fts5ExprPhraseSize(pCsr->pExpr, iPhrase); |
| 182524 | } |
| 182525 | |
| 182526 | static int fts5ApiColumnText( |
| 182527 | Fts5Context *pCtx, |
| 182528 | int iCol, |
| 182529 | const char **pz, |
| 182530 | int *pn |
| 182531 | ){ |
| 182532 | int rc = SQLITE_OK; |
| 182533 | Fts5Cursor *pCsr = (Fts5Cursor*)pCtx; |
| 182534 | if( fts5IsContentless((Fts5Table*)(pCsr->base.pVtab)) ){ |
| 182535 | *pz = 0; |
| 182536 | *pn = 0; |
| 182537 | }else{ |
| 182538 | rc = fts5SeekCursor(pCsr, 0); |
| 182539 | if( rc==SQLITE_OK ){ |
| 182540 | *pz = (const char*)sqlite3_column_text(pCsr->pStmt, iCol+1); |
| 182541 | *pn = sqlite3_column_bytes(pCsr->pStmt, iCol+1); |
| 182542 | } |
| 182543 | } |
| 182544 | return rc; |
| 182545 | } |
| 182546 | |
| 182547 | static int fts5CsrPoslist( |
| 182548 | Fts5Cursor *pCsr, |
| 182549 | int iPhrase, |
| 182550 | const u8 **pa, |
| 182551 | int *pn |
| 182552 | ){ |
| 182553 | Fts5Config *pConfig = ((Fts5Table*)(pCsr->base.pVtab))->pConfig; |
| 182554 | int rc = SQLITE_OK; |
| 182555 | int bLive = (pCsr->pSorter==0); |
| 182556 | |
| 182557 | if( CsrFlagTest(pCsr, FTS5CSR_REQUIRE_POSLIST) ){ |
| 182558 | |
| 182559 | if( pConfig->eDetail!=FTS5_DETAIL_FULL ){ |
| 182560 | Fts5PoslistPopulator *aPopulator; |
| 182561 | int i; |
| 182562 | aPopulator = sqlite3Fts5ExprClearPoslists(pCsr->pExpr, bLive); |
| 182563 | if( aPopulator==0 ) rc = SQLITE_NOMEM; |
| 182564 | for(i=0; i<pConfig->nCol && rc==SQLITE_OK; i++){ |
| 182565 | int n; const char *z; |
| 182566 | rc = fts5ApiColumnText((Fts5Context*)pCsr, i, &z, &n); |
| 182567 | if( rc==SQLITE_OK ){ |
| 182568 | rc = sqlite3Fts5ExprPopulatePoslists( |
| 182569 | pConfig, pCsr->pExpr, aPopulator, i, z, n |
| 182570 | ); |
| 182571 | } |
| 182572 | } |
| 182573 | sqlite3_free(aPopulator); |
| 182574 | |
| 182575 | if( pCsr->pSorter ){ |
| 182576 | sqlite3Fts5ExprCheckPoslists(pCsr->pExpr, pCsr->pSorter->iRowid); |
| 182577 | } |
| 182578 | } |
| 182579 | CsrFlagClear(pCsr, FTS5CSR_REQUIRE_POSLIST); |
| 182580 | } |
| 182581 | |
| 182582 | if( pCsr->pSorter && pConfig->eDetail==FTS5_DETAIL_FULL ){ |
| 182583 | Fts5Sorter *pSorter = pCsr->pSorter; |
| 182584 | int i1 = (iPhrase==0 ? 0 : pSorter->aIdx[iPhrase-1]); |
| 182585 | *pn = pSorter->aIdx[iPhrase] - i1; |
| 182586 | *pa = &pSorter->aPoslist[i1]; |
| 182587 | }else{ |
| 182588 | *pn = sqlite3Fts5ExprPoslist(pCsr->pExpr, iPhrase, pa); |
| 182589 | } |
| 182590 | |
| 182591 | return rc; |
| 182592 | } |
| 182593 | |
| 182594 | /* |
| 182595 | ** Ensure that the Fts5Cursor.nInstCount and aInst[] variables are populated |
| 182596 | ** correctly for the current view. Return SQLITE_OK if successful, or an |
| @@ -181493,47 +182611,50 @@ | |
| 182611 | if( aIter ){ |
| 182612 | int nInst = 0; /* Number instances seen so far */ |
| 182613 | int i; |
| 182614 | |
| 182615 | /* Initialize all iterators */ |
| 182616 | for(i=0; i<nIter && rc==SQLITE_OK; i++){ |
| 182617 | const u8 *a; |
| 182618 | int n; |
| 182619 | rc = fts5CsrPoslist(pCsr, i, &a, &n); |
| 182620 | sqlite3Fts5PoslistReaderInit(a, n, &aIter[i]); |
| 182621 | } |
| 182622 | |
| 182623 | if( rc==SQLITE_OK ){ |
| 182624 | while( 1 ){ |
| 182625 | int *aInst; |
| 182626 | int iBest = -1; |
| 182627 | for(i=0; i<nIter; i++){ |
| 182628 | if( (aIter[i].bEof==0) |
| 182629 | && (iBest<0 || aIter[i].iPos<aIter[iBest].iPos) |
| 182630 | ){ |
| 182631 | iBest = i; |
| 182632 | } |
| 182633 | } |
| 182634 | if( iBest<0 ) break; |
| 182635 | |
| 182636 | nInst++; |
| 182637 | if( nInst>=pCsr->nInstAlloc ){ |
| 182638 | pCsr->nInstAlloc = pCsr->nInstAlloc ? pCsr->nInstAlloc*2 : 32; |
| 182639 | aInst = (int*)sqlite3_realloc( |
| 182640 | pCsr->aInst, pCsr->nInstAlloc*sizeof(int)*3 |
| 182641 | ); |
| 182642 | if( aInst ){ |
| 182643 | pCsr->aInst = aInst; |
| 182644 | }else{ |
| 182645 | rc = SQLITE_NOMEM; |
| 182646 | break; |
| 182647 | } |
| 182648 | } |
| 182649 | |
| 182650 | aInst = &pCsr->aInst[3 * (nInst-1)]; |
| 182651 | aInst[0] = iBest; |
| 182652 | aInst[1] = FTS5_POS2COLUMN(aIter[iBest].iPos); |
| 182653 | aInst[2] = FTS5_POS2OFFSET(aIter[iBest].iPos); |
| 182654 | sqlite3Fts5PoslistReaderNext(&aIter[iBest]); |
| 182655 | } |
| 182656 | } |
| 182657 | |
| 182658 | pCsr->nInstCount = nInst; |
| 182659 | CsrFlagClear(pCsr, FTS5CSR_REQUIRE_INST); |
| 182660 | } |
| @@ -181562,10 +182683,16 @@ | |
| 182683 | if( CsrFlagTest(pCsr, FTS5CSR_REQUIRE_INST)==0 |
| 182684 | || SQLITE_OK==(rc = fts5CacheInstArray(pCsr)) |
| 182685 | ){ |
| 182686 | if( iIdx<0 || iIdx>=pCsr->nInstCount ){ |
| 182687 | rc = SQLITE_RANGE; |
| 182688 | #if 0 |
| 182689 | }else if( fts5IsOffsetless((Fts5Table*)pCsr->base.pVtab) ){ |
| 182690 | *piPhrase = pCsr->aInst[iIdx*3]; |
| 182691 | *piCol = pCsr->aInst[iIdx*3 + 2]; |
| 182692 | *piOff = -1; |
| 182693 | #endif |
| 182694 | }else{ |
| 182695 | *piPhrase = pCsr->aInst[iIdx*3]; |
| 182696 | *piCol = pCsr->aInst[iIdx*3 + 1]; |
| 182697 | *piOff = pCsr->aInst[iIdx*3 + 2]; |
| 182698 | } |
| @@ -181575,31 +182702,10 @@ | |
| 182702 | |
| 182703 | static sqlite3_int64 fts5ApiRowid(Fts5Context *pCtx){ |
| 182704 | return fts5CursorRowid((Fts5Cursor*)pCtx); |
| 182705 | } |
| 182706 | |
| 182707 | static int fts5ColumnSizeCb( |
| 182708 | void *pContext, /* Pointer to int */ |
| 182709 | int tflags, |
| 182710 | const char *pToken, /* Buffer containing token */ |
| 182711 | int nToken, /* Size of token in bytes */ |
| @@ -181740,23 +182846,94 @@ | |
| 182846 | } |
| 182847 | *piOff += (iVal-2); |
| 182848 | } |
| 182849 | } |
| 182850 | |
| 182851 | static int fts5ApiPhraseFirst( |
| 182852 | Fts5Context *pCtx, |
| 182853 | int iPhrase, |
| 182854 | Fts5PhraseIter *pIter, |
| 182855 | int *piCol, int *piOff |
| 182856 | ){ |
| 182857 | Fts5Cursor *pCsr = (Fts5Cursor*)pCtx; |
| 182858 | int n; |
| 182859 | int rc = fts5CsrPoslist(pCsr, iPhrase, &pIter->a, &n); |
| 182860 | if( rc==SQLITE_OK ){ |
| 182861 | pIter->b = &pIter->a[n]; |
| 182862 | *piCol = 0; |
| 182863 | *piOff = 0; |
| 182864 | fts5ApiPhraseNext(pCtx, pIter, piCol, piOff); |
| 182865 | } |
| 182866 | return rc; |
| 182867 | } |
| 182868 | |
| 182869 | static void fts5ApiPhraseNextColumn( |
| 182870 | Fts5Context *pCtx, |
| 182871 | Fts5PhraseIter *pIter, |
| 182872 | int *piCol |
| 182873 | ){ |
| 182874 | Fts5Cursor *pCsr = (Fts5Cursor*)pCtx; |
| 182875 | Fts5Config *pConfig = ((Fts5Table*)(pCsr->base.pVtab))->pConfig; |
| 182876 | |
| 182877 | if( pConfig->eDetail==FTS5_DETAIL_COLUMNS ){ |
| 182878 | if( pIter->a>=pIter->b ){ |
| 182879 | *piCol = -1; |
| 182880 | }else{ |
| 182881 | int iIncr; |
| 182882 | pIter->a += fts5GetVarint32(&pIter->a[0], iIncr); |
| 182883 | *piCol += (iIncr-2); |
| 182884 | } |
| 182885 | }else{ |
| 182886 | while( 1 ){ |
| 182887 | int dummy; |
| 182888 | if( pIter->a>=pIter->b ){ |
| 182889 | *piCol = -1; |
| 182890 | return; |
| 182891 | } |
| 182892 | if( pIter->a[0]==0x01 ) break; |
| 182893 | pIter->a += fts5GetVarint32(pIter->a, dummy); |
| 182894 | } |
| 182895 | pIter->a += 1 + fts5GetVarint32(&pIter->a[1], *piCol); |
| 182896 | } |
| 182897 | } |
| 182898 | |
| 182899 | static int fts5ApiPhraseFirstColumn( |
| 182900 | Fts5Context *pCtx, |
| 182901 | int iPhrase, |
| 182902 | Fts5PhraseIter *pIter, |
| 182903 | int *piCol |
| 182904 | ){ |
| 182905 | int rc = SQLITE_OK; |
| 182906 | Fts5Cursor *pCsr = (Fts5Cursor*)pCtx; |
| 182907 | Fts5Config *pConfig = ((Fts5Table*)(pCsr->base.pVtab))->pConfig; |
| 182908 | |
| 182909 | if( pConfig->eDetail==FTS5_DETAIL_COLUMNS ){ |
| 182910 | int n; |
| 182911 | rc = sqlite3Fts5ExprPhraseCollist(pCsr->pExpr, iPhrase, &pIter->a, &n); |
| 182912 | if( rc==SQLITE_OK ){ |
| 182913 | pIter->b = &pIter->a[n]; |
| 182914 | *piCol = 0; |
| 182915 | fts5ApiPhraseNextColumn(pCtx, pIter, piCol); |
| 182916 | } |
| 182917 | }else{ |
| 182918 | int n; |
| 182919 | rc = fts5CsrPoslist(pCsr, iPhrase, &pIter->a, &n); |
| 182920 | if( rc==SQLITE_OK ){ |
| 182921 | pIter->b = &pIter->a[n]; |
| 182922 | if( n<=0 ){ |
| 182923 | *piCol = -1; |
| 182924 | }else if( pIter->a[0]==0x01 ){ |
| 182925 | pIter->a += 1 + fts5GetVarint32(&pIter->a[1], *piCol); |
| 182926 | }else{ |
| 182927 | *piCol = 0; |
| 182928 | } |
| 182929 | } |
| 182930 | } |
| 182931 | |
| 182932 | return rc; |
| 182933 | } |
| 182934 | |
| 182935 | |
| 182936 | static int fts5ApiQueryPhrase(Fts5Context*, int, void*, |
| 182937 | int(*)(const Fts5ExtensionApi*, Fts5Context*, void*) |
| 182938 | ); |
| 182939 | |
| @@ -181777,12 +182954,13 @@ | |
| 182954 | fts5ApiQueryPhrase, |
| 182955 | fts5ApiSetAuxdata, |
| 182956 | fts5ApiGetAuxdata, |
| 182957 | fts5ApiPhraseFirst, |
| 182958 | fts5ApiPhraseNext, |
| 182959 | fts5ApiPhraseFirstColumn, |
| 182960 | fts5ApiPhraseNextColumn, |
| 182961 | }; |
| 182962 | |
| 182963 | /* |
| 182964 | ** Implementation of API function xQueryPhrase(). |
| 182965 | */ |
| 182966 | static int fts5ApiQueryPhrase( |
| @@ -181911,24 +183089,50 @@ | |
| 183089 | int rc = SQLITE_OK; |
| 183090 | int nPhrase = sqlite3Fts5ExprPhraseCount(pCsr->pExpr); |
| 183091 | Fts5Buffer val; |
| 183092 | |
| 183093 | memset(&val, 0, sizeof(Fts5Buffer)); |
| 183094 | switch( ((Fts5Table*)(pCsr->base.pVtab))->pConfig->eDetail ){ |
| 183095 | case FTS5_DETAIL_FULL: |
| 183096 | |
| 183097 | /* Append the varints */ |
| 183098 | for(i=0; i<(nPhrase-1); i++){ |
| 183099 | const u8 *dummy; |
| 183100 | int nByte = sqlite3Fts5ExprPoslist(pCsr->pExpr, i, &dummy); |
| 183101 | sqlite3Fts5BufferAppendVarint(&rc, &val, nByte); |
| 183102 | } |
| 183103 | |
| 183104 | /* Append the position lists */ |
| 183105 | for(i=0; i<nPhrase; i++){ |
| 183106 | const u8 *pPoslist; |
| 183107 | int nPoslist; |
| 183108 | nPoslist = sqlite3Fts5ExprPoslist(pCsr->pExpr, i, &pPoslist); |
| 183109 | sqlite3Fts5BufferAppendBlob(&rc, &val, nPoslist, pPoslist); |
| 183110 | } |
| 183111 | break; |
| 183112 | |
| 183113 | case FTS5_DETAIL_COLUMNS: |
| 183114 | |
| 183115 | /* Append the varints */ |
| 183116 | for(i=0; rc==SQLITE_OK && i<(nPhrase-1); i++){ |
| 183117 | const u8 *dummy; |
| 183118 | int nByte; |
| 183119 | rc = sqlite3Fts5ExprPhraseCollist(pCsr->pExpr, i, &dummy, &nByte); |
| 183120 | sqlite3Fts5BufferAppendVarint(&rc, &val, nByte); |
| 183121 | } |
| 183122 | |
| 183123 | /* Append the position lists */ |
| 183124 | for(i=0; rc==SQLITE_OK && i<nPhrase; i++){ |
| 183125 | const u8 *pPoslist; |
| 183126 | int nPoslist; |
| 183127 | rc = sqlite3Fts5ExprPhraseCollist(pCsr->pExpr, i, &pPoslist, &nPoslist); |
| 183128 | sqlite3Fts5BufferAppendBlob(&rc, &val, nPoslist, pPoslist); |
| 183129 | } |
| 183130 | break; |
| 183131 | |
| 183132 | default: |
| 183133 | break; |
| 183134 | } |
| 183135 | |
| 183136 | sqlite3_result_blob(pCtx, val.p, val.n, sqlite3_free); |
| 183137 | return rc; |
| 183138 | } |
| @@ -182247,11 +183451,11 @@ | |
| 183451 | sqlite3_context *pCtx, /* Function call context */ |
| 183452 | int nArg, /* Number of args */ |
| 183453 | sqlite3_value **apVal /* Function arguments */ |
| 183454 | ){ |
| 183455 | assert( nArg==0 ); |
| 183456 | sqlite3_result_text(pCtx, "fts5: 2016-01-14 14:19:50 d17bc2c92f4d086280e49a3cc72993be7fee2da7", -1, SQLITE_TRANSIENT); |
| 183457 | } |
| 183458 | |
| 183459 | static int fts5Init(sqlite3 *db){ |
| 183460 | static const sqlite3_module fts5Mod = { |
| 183461 | /* iVersion */ 2, |
| @@ -183179,32 +184383,77 @@ | |
| 184383 | struct Fts5IntegrityCtx { |
| 184384 | i64 iRowid; |
| 184385 | int iCol; |
| 184386 | int szCol; |
| 184387 | u64 cksum; |
| 184388 | Fts5Termset *pTermset; |
| 184389 | Fts5Config *pConfig; |
| 184390 | }; |
| 184391 | |
| 184392 | |
| 184393 | /* |
| 184394 | ** Tokenization callback used by integrity check. |
| 184395 | */ |
| 184396 | static int fts5StorageIntegrityCallback( |
| 184397 | void *pContext, /* Pointer to Fts5IntegrityCtx object */ |
| 184398 | int tflags, |
| 184399 | const char *pToken, /* Buffer containing token */ |
| 184400 | int nToken, /* Size of token in bytes */ |
| 184401 | int iStart, /* Start offset of token */ |
| 184402 | int iEnd /* End offset of token */ |
| 184403 | ){ |
| 184404 | Fts5IntegrityCtx *pCtx = (Fts5IntegrityCtx*)pContext; |
| 184405 | Fts5Termset *pTermset = pCtx->pTermset; |
| 184406 | int bPresent; |
| 184407 | int ii; |
| 184408 | int rc = SQLITE_OK; |
| 184409 | int iPos; |
| 184410 | int iCol; |
| 184411 | |
| 184412 | if( (tflags & FTS5_TOKEN_COLOCATED)==0 || pCtx->szCol==0 ){ |
| 184413 | pCtx->szCol++; |
| 184414 | } |
| 184415 | |
| 184416 | switch( pCtx->pConfig->eDetail ){ |
| 184417 | case FTS5_DETAIL_FULL: |
| 184418 | iPos = pCtx->szCol-1; |
| 184419 | iCol = pCtx->iCol; |
| 184420 | break; |
| 184421 | |
| 184422 | case FTS5_DETAIL_COLUMNS: |
| 184423 | iPos = pCtx->iCol; |
| 184424 | iCol = 0; |
| 184425 | break; |
| 184426 | |
| 184427 | default: |
| 184428 | assert( pCtx->pConfig->eDetail==FTS5_DETAIL_NONE ); |
| 184429 | iPos = 0; |
| 184430 | iCol = 0; |
| 184431 | break; |
| 184432 | } |
| 184433 | |
| 184434 | rc = sqlite3Fts5TermsetAdd(pTermset, 0, pToken, nToken, &bPresent); |
| 184435 | if( rc==SQLITE_OK && bPresent==0 ){ |
| 184436 | pCtx->cksum ^= sqlite3Fts5IndexEntryCksum( |
| 184437 | pCtx->iRowid, iCol, iPos, 0, pToken, nToken |
| 184438 | ); |
| 184439 | } |
| 184440 | |
| 184441 | for(ii=0; rc==SQLITE_OK && ii<pCtx->pConfig->nPrefix; ii++){ |
| 184442 | const int nChar = pCtx->pConfig->aPrefix[ii]; |
| 184443 | int nByte = sqlite3Fts5IndexCharlenToBytelen(pToken, nToken, nChar); |
| 184444 | if( nByte ){ |
| 184445 | rc = sqlite3Fts5TermsetAdd(pTermset, ii+1, pToken, nByte, &bPresent); |
| 184446 | if( bPresent==0 ){ |
| 184447 | pCtx->cksum ^= sqlite3Fts5IndexEntryCksum( |
| 184448 | pCtx->iRowid, iCol, iPos, ii+1, pToken, nByte |
| 184449 | ); |
| 184450 | } |
| 184451 | } |
| 184452 | } |
| 184453 | |
| 184454 | return rc; |
| 184455 | } |
| 184456 | |
| 184457 | /* |
| 184458 | ** Check that the contents of the FTS index match that of the %_content |
| 184459 | ** table. Return SQLITE_OK if they do, or SQLITE_CORRUPT if not. Return |
| @@ -183235,27 +184484,42 @@ | |
| 184484 | int i; |
| 184485 | ctx.iRowid = sqlite3_column_int64(pScan, 0); |
| 184486 | ctx.szCol = 0; |
| 184487 | if( pConfig->bColumnsize ){ |
| 184488 | rc = sqlite3Fts5StorageDocsize(p, ctx.iRowid, aColSize); |
| 184489 | } |
| 184490 | if( rc==SQLITE_OK && pConfig->eDetail==FTS5_DETAIL_NONE ){ |
| 184491 | rc = sqlite3Fts5TermsetNew(&ctx.pTermset); |
| 184492 | } |
| 184493 | for(i=0; rc==SQLITE_OK && i<pConfig->nCol; i++){ |
| 184494 | if( pConfig->abUnindexed[i] ) continue; |
| 184495 | ctx.iCol = i; |
| 184496 | ctx.szCol = 0; |
| 184497 | if( pConfig->eDetail==FTS5_DETAIL_COLUMNS ){ |
| 184498 | rc = sqlite3Fts5TermsetNew(&ctx.pTermset); |
| 184499 | } |
| 184500 | if( rc==SQLITE_OK ){ |
| 184501 | rc = sqlite3Fts5Tokenize(pConfig, |
| 184502 | FTS5_TOKENIZE_DOCUMENT, |
| 184503 | (const char*)sqlite3_column_text(pScan, i+1), |
| 184504 | sqlite3_column_bytes(pScan, i+1), |
| 184505 | (void*)&ctx, |
| 184506 | fts5StorageIntegrityCallback |
| 184507 | ); |
| 184508 | } |
| 184509 | if( rc==SQLITE_OK && pConfig->bColumnsize && ctx.szCol!=aColSize[i] ){ |
| 184510 | rc = FTS5_CORRUPT; |
| 184511 | } |
| 184512 | aTotalSize[i] += ctx.szCol; |
| 184513 | if( pConfig->eDetail==FTS5_DETAIL_COLUMNS ){ |
| 184514 | sqlite3Fts5TermsetFree(ctx.pTermset); |
| 184515 | ctx.pTermset = 0; |
| 184516 | } |
| 184517 | } |
| 184518 | sqlite3Fts5TermsetFree(ctx.pTermset); |
| 184519 | ctx.pTermset = 0; |
| 184520 | |
| 184521 | if( rc!=SQLITE_OK ) break; |
| 184522 | } |
| 184523 | rc2 = sqlite3_reset(pScan); |
| 184524 | if( rc==SQLITE_OK ) rc = rc2; |
| 184525 | } |
| @@ -185777,11 +187041,11 @@ | |
| 187041 | |
| 187042 | pCsr->rowid++; |
| 187043 | |
| 187044 | if( pTab->eType==FTS5_VOCAB_COL ){ |
| 187045 | for(pCsr->iCol++; pCsr->iCol<nCol; pCsr->iCol++){ |
| 187046 | if( pCsr->aDoc[pCsr->iCol] ) break; |
| 187047 | } |
| 187048 | } |
| 187049 | |
| 187050 | if( pTab->eType==FTS5_VOCAB_ROW || pCsr->iCol>=nCol ){ |
| 187051 | if( sqlite3Fts5IterEof(pCsr->pIter) ){ |
| @@ -185810,28 +187074,56 @@ | |
| 187074 | i64 dummy; |
| 187075 | const u8 *pPos; int nPos; /* Position list */ |
| 187076 | i64 iPos = 0; /* 64-bit position read from poslist */ |
| 187077 | int iOff = 0; /* Current offset within position list */ |
| 187078 | |
| 187079 | switch( pCsr->pConfig->eDetail ){ |
| 187080 | case FTS5_DETAIL_FULL: |
| 187081 | rc = sqlite3Fts5IterPoslist(pCsr->pIter, 0, &pPos, &nPos, &dummy); |
| 187082 | if( rc==SQLITE_OK ){ |
| 187083 | if( pTab->eType==FTS5_VOCAB_ROW ){ |
| 187084 | while( 0==sqlite3Fts5PoslistNext64(pPos, nPos, &iOff, &iPos) ){ |
| 187085 | pCsr->aCnt[0]++; |
| 187086 | } |
| 187087 | pCsr->aDoc[0]++; |
| 187088 | }else{ |
| 187089 | int iCol = -1; |
| 187090 | while( 0==sqlite3Fts5PoslistNext64(pPos, nPos, &iOff, &iPos) ){ |
| 187091 | int ii = FTS5_POS2COLUMN(iPos); |
| 187092 | pCsr->aCnt[ii]++; |
| 187093 | if( iCol!=ii ){ |
| 187094 | pCsr->aDoc[ii]++; |
| 187095 | iCol = ii; |
| 187096 | } |
| 187097 | } |
| 187098 | } |
| 187099 | } |
| 187100 | break; |
| 187101 | |
| 187102 | case FTS5_DETAIL_COLUMNS: |
| 187103 | if( pTab->eType==FTS5_VOCAB_ROW ){ |
| 187104 | pCsr->aDoc[0]++; |
| 187105 | }else{ |
| 187106 | Fts5Buffer buf = {0, 0, 0}; |
| 187107 | rc = sqlite3Fts5IterPoslistBuffer(pCsr->pIter, &buf); |
| 187108 | if( rc==SQLITE_OK ){ |
| 187109 | while( 0==sqlite3Fts5PoslistNext64(buf.p, buf.n, &iOff,&iPos) ){ |
| 187110 | assert_nc( iPos>=0 && iPos<nCol ); |
| 187111 | if( iPos<nCol ) pCsr->aDoc[iPos]++; |
| 187112 | } |
| 187113 | } |
| 187114 | sqlite3Fts5BufferFree(&buf); |
| 187115 | } |
| 187116 | break; |
| 187117 | |
| 187118 | default: |
| 187119 | assert( pCsr->pConfig->eDetail==FTS5_DETAIL_NONE ); |
| 187120 | pCsr->aDoc[0]++; |
| 187121 | break; |
| 187122 | } |
| 187123 | |
| 187124 | if( rc==SQLITE_OK ){ |
| 187125 | rc = sqlite3Fts5IterNextScan(pCsr->pIter); |
| 187126 | } |
| 187127 | |
| 187128 | if( rc==SQLITE_OK ){ |
| 187129 | zTerm = sqlite3Fts5IterTerm(pCsr->pIter, &nTerm); |
| @@ -185843,11 +187135,11 @@ | |
| 187135 | } |
| 187136 | } |
| 187137 | } |
| 187138 | |
| 187139 | if( pCsr->bEof==0 && pTab->eType==FTS5_VOCAB_COL ){ |
| 187140 | while( pCsr->aDoc[pCsr->iCol]==0 ) pCsr->iCol++; |
| 187141 | assert( pCsr->iCol<pCsr->pConfig->nCol ); |
| 187142 | } |
| 187143 | return rc; |
| 187144 | } |
| 187145 | |
| @@ -185923,34 +187215,40 @@ | |
| 187215 | sqlite3_vtab_cursor *pCursor, /* Cursor to retrieve value from */ |
| 187216 | sqlite3_context *pCtx, /* Context for sqlite3_result_xxx() calls */ |
| 187217 | int iCol /* Index of column to read value from */ |
| 187218 | ){ |
| 187219 | Fts5VocabCursor *pCsr = (Fts5VocabCursor*)pCursor; |
| 187220 | int eDetail = pCsr->pConfig->eDetail; |
| 187221 | int eType = ((Fts5VocabTable*)(pCursor->pVtab))->eType; |
| 187222 | i64 iVal = 0; |
| 187223 | |
| 187224 | if( iCol==0 ){ |
| 187225 | sqlite3_result_text( |
| 187226 | pCtx, (const char*)pCsr->term.p, pCsr->term.n, SQLITE_TRANSIENT |
| 187227 | ); |
| 187228 | }else if( eType==FTS5_VOCAB_COL ){ |
| 187229 | assert( iCol==1 || iCol==2 || iCol==3 ); |
| 187230 | if( iCol==1 ){ |
| 187231 | if( eDetail!=FTS5_DETAIL_NONE ){ |
| 187232 | const char *z = pCsr->pConfig->azCol[pCsr->iCol]; |
| 187233 | sqlite3_result_text(pCtx, z, -1, SQLITE_STATIC); |
| 187234 | } |
| 187235 | }else if( iCol==2 ){ |
| 187236 | iVal = pCsr->aDoc[pCsr->iCol]; |
| 187237 | }else{ |
| 187238 | iVal = pCsr->aCnt[pCsr->iCol]; |
| 187239 | } |
| 187240 | }else{ |
| 187241 | assert( iCol==1 || iCol==2 ); |
| 187242 | if( iCol==1 ){ |
| 187243 | iVal = pCsr->aDoc[0]; |
| 187244 | }else{ |
| 187245 | iVal = pCsr->aCnt[0]; |
| 187246 | } |
| 187247 | } |
| 187248 | |
| 187249 | if( iVal>0 ) sqlite3_result_int64(pCtx, iVal); |
| 187250 | return SQLITE_OK; |
| 187251 | } |
| 187252 | |
| 187253 | /* |
| 187254 | ** This is the xRowid method. The SQLite core calls this routine to |
| 187255 |
+73
-9
| --- src/sqlite3.h | ||
| +++ src/sqlite3.h | ||
| @@ -109,13 +109,13 @@ | ||
| 109 | 109 | ** |
| 110 | 110 | ** See also: [sqlite3_libversion()], |
| 111 | 111 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 112 | 112 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 113 | 113 | */ |
| 114 | -#define SQLITE_VERSION "3.10.0" | |
| 115 | -#define SQLITE_VERSION_NUMBER 3010000 | |
| 116 | -#define SQLITE_SOURCE_ID "2016-01-06 11:01:07 fd0a50f0797d154fefff724624f00548b5320566" | |
| 114 | +#define SQLITE_VERSION "3.11.0" | |
| 115 | +#define SQLITE_VERSION_NUMBER 3011000 | |
| 116 | +#define SQLITE_SOURCE_ID "2016-01-14 14:19:50 d17bc2c92f4d086280e49a3cc72993be7fee2da7" | |
| 117 | 117 | |
| 118 | 118 | /* |
| 119 | 119 | ** CAPI3REF: Run-Time Library Version Numbers |
| 120 | 120 | ** KEYWORDS: sqlite3_version, sqlite3_sourceid |
| 121 | 121 | ** |
| @@ -792,12 +792,17 @@ | ||
| 792 | 792 | ** improve performance on some systems. |
| 793 | 793 | ** |
| 794 | 794 | ** <li>[[SQLITE_FCNTL_FILE_POINTER]] |
| 795 | 795 | ** The [SQLITE_FCNTL_FILE_POINTER] opcode is used to obtain a pointer |
| 796 | 796 | ** to the [sqlite3_file] object associated with a particular database |
| 797 | -** connection. See the [sqlite3_file_control()] documentation for | |
| 798 | -** additional information. | |
| 797 | +** connection. See also [SQLITE_FCNTL_JOURNAL_POINTER]. | |
| 798 | +** | |
| 799 | +** <li>[[SQLITE_FCNTL_JOURNAL_POINTER]] | |
| 800 | +** The [SQLITE_FCNTL_JOURNAL_POINTER] opcode is used to obtain a pointer | |
| 801 | +** to the [sqlite3_file] object associated with the journal file (either | |
| 802 | +** the [rollback journal] or the [write-ahead log]) for a particular database | |
| 803 | +** connection. See also [SQLITE_FCNTL_FILE_POINTER]. | |
| 799 | 804 | ** |
| 800 | 805 | ** <li>[[SQLITE_FCNTL_SYNC_OMITTED]] |
| 801 | 806 | ** No longer in use. |
| 802 | 807 | ** |
| 803 | 808 | ** <li>[[SQLITE_FCNTL_SYNC]] |
| @@ -1008,10 +1013,11 @@ | ||
| 1008 | 1013 | #define SQLITE_FCNTL_WIN32_SET_HANDLE 23 |
| 1009 | 1014 | #define SQLITE_FCNTL_WAL_BLOCK 24 |
| 1010 | 1015 | #define SQLITE_FCNTL_ZIPVFS 25 |
| 1011 | 1016 | #define SQLITE_FCNTL_RBU 26 |
| 1012 | 1017 | #define SQLITE_FCNTL_VFS_POINTER 27 |
| 1018 | +#define SQLITE_FCNTL_JOURNAL_POINTER 28 | |
| 1013 | 1019 | |
| 1014 | 1020 | /* deprecated names */ |
| 1015 | 1021 | #define SQLITE_GET_LOCKPROXYFILE SQLITE_FCNTL_GET_LOCKPROXYFILE |
| 1016 | 1022 | #define SQLITE_SET_LOCKPROXYFILE SQLITE_FCNTL_SET_LOCKPROXYFILE |
| 1017 | 1023 | #define SQLITE_LAST_ERRNO SQLITE_FCNTL_LAST_ERRNO |
| @@ -8185,10 +8191,13 @@ | ||
| 8185 | 8191 | ** If parameter iCol is greater than or equal to the number of columns |
| 8186 | 8192 | ** in the table, SQLITE_RANGE is returned. Or, if an error occurs (e.g. |
| 8187 | 8193 | ** an OOM condition or IO error), an appropriate SQLite error code is |
| 8188 | 8194 | ** returned. |
| 8189 | 8195 | ** |
| 8196 | +** This function may be quite inefficient if used with an FTS5 table | |
| 8197 | +** created with the "columnsize=0" option. | |
| 8198 | +** | |
| 8190 | 8199 | ** xColumnText: |
| 8191 | 8200 | ** This function attempts to retrieve the text of column iCol of the |
| 8192 | 8201 | ** current document. If successful, (*pz) is set to point to a buffer |
| 8193 | 8202 | ** containing the text in utf-8 encoding, (*pn) is set to the size in bytes |
| 8194 | 8203 | ** (not characters) of the buffer and SQLITE_OK is returned. Otherwise, |
| @@ -8205,18 +8214,32 @@ | ||
| 8205 | 8214 | ** xInstCount: |
| 8206 | 8215 | ** Set *pnInst to the total number of occurrences of all phrases within |
| 8207 | 8216 | ** the query within the current row. Return SQLITE_OK if successful, or |
| 8208 | 8217 | ** an error code (i.e. SQLITE_NOMEM) if an error occurs. |
| 8209 | 8218 | ** |
| 8219 | +** This API can be quite slow if used with an FTS5 table created with the | |
| 8220 | +** "detail=none" or "detail=column" option. If the FTS5 table is created | |
| 8221 | +** with either "detail=none" or "detail=column" and "content=" option | |
| 8222 | +** (i.e. if it is a contentless table), then this API always returns 0. | |
| 8223 | +** | |
| 8210 | 8224 | ** xInst: |
| 8211 | 8225 | ** Query for the details of phrase match iIdx within the current row. |
| 8212 | 8226 | ** Phrase matches are numbered starting from zero, so the iIdx argument |
| 8213 | 8227 | ** should be greater than or equal to zero and smaller than the value |
| 8214 | 8228 | ** output by xInstCount(). |
| 8229 | +** | |
| 8230 | +** Usually, output parameter *piPhrase is set to the phrase number, *piCol | |
| 8231 | +** to the column in which it occurs and *piOff the token offset of the | |
| 8232 | +** first token of the phrase. The exception is if the table was created | |
| 8233 | +** with the offsets=0 option specified. In this case *piOff is always | |
| 8234 | +** set to -1. | |
| 8215 | 8235 | ** |
| 8216 | 8236 | ** Returns SQLITE_OK if successful, or an error code (i.e. SQLITE_NOMEM) |
| 8217 | 8237 | ** if an error occurs. |
| 8238 | +** | |
| 8239 | +** This API can be quite slow if used with an FTS5 table created with the | |
| 8240 | +** "detail=none" or "detail=column" option. | |
| 8218 | 8241 | ** |
| 8219 | 8242 | ** xRowid: |
| 8220 | 8243 | ** Returns the rowid of the current row. |
| 8221 | 8244 | ** |
| 8222 | 8245 | ** xTokenize: |
| @@ -8297,25 +8320,63 @@ | ||
| 8297 | 8320 | ** through instances of phrase iPhrase, use the following code: |
| 8298 | 8321 | ** |
| 8299 | 8322 | ** Fts5PhraseIter iter; |
| 8300 | 8323 | ** int iCol, iOff; |
| 8301 | 8324 | ** for(pApi->xPhraseFirst(pFts, iPhrase, &iter, &iCol, &iOff); |
| 8302 | -** iOff>=0; | |
| 8325 | +** iCol>=0; | |
| 8303 | 8326 | ** pApi->xPhraseNext(pFts, &iter, &iCol, &iOff) |
| 8304 | 8327 | ** ){ |
| 8305 | 8328 | ** // An instance of phrase iPhrase at offset iOff of column iCol |
| 8306 | 8329 | ** } |
| 8307 | 8330 | ** |
| 8308 | 8331 | ** The Fts5PhraseIter structure is defined above. Applications should not |
| 8309 | 8332 | ** modify this structure directly - it should only be used as shown above |
| 8310 | -** with the xPhraseFirst() and xPhraseNext() API methods. | |
| 8333 | +** with the xPhraseFirst() and xPhraseNext() API methods (and by | |
| 8334 | +** xPhraseFirstColumn() and xPhraseNextColumn() as illustrated below). | |
| 8335 | +** | |
| 8336 | +** This API can be quite slow if used with an FTS5 table created with the | |
| 8337 | +** "detail=none" or "detail=column" option. If the FTS5 table is created | |
| 8338 | +** with either "detail=none" or "detail=column" and "content=" option | |
| 8339 | +** (i.e. if it is a contentless table), then this API always iterates | |
| 8340 | +** through an empty set (all calls to xPhraseFirst() set iCol to -1). | |
| 8311 | 8341 | ** |
| 8312 | 8342 | ** xPhraseNext() |
| 8313 | 8343 | ** See xPhraseFirst above. |
| 8344 | +** | |
| 8345 | +** xPhraseFirstColumn() | |
| 8346 | +** This function and xPhraseNextColumn() are similar to the xPhraseFirst() | |
| 8347 | +** and xPhraseNext() APIs described above. The difference is that instead | |
| 8348 | +** of iterating through all instances of a phrase in the current row, these | |
| 8349 | +** APIs are used to iterate through the set of columns in the current row | |
| 8350 | +** that contain one or more instances of a specified phrase. For example: | |
| 8351 | +** | |
| 8352 | +** Fts5PhraseIter iter; | |
| 8353 | +** int iCol; | |
| 8354 | +** for(pApi->xPhraseFirstColumn(pFts, iPhrase, &iter, &iCol); | |
| 8355 | +** iCol>=0; | |
| 8356 | +** pApi->xPhraseNextColumn(pFts, &iter, &iCol) | |
| 8357 | +** ){ | |
| 8358 | +** // Column iCol contains at least one instance of phrase iPhrase | |
| 8359 | +** } | |
| 8360 | +** | |
| 8361 | +** This API can be quite slow if used with an FTS5 table created with the | |
| 8362 | +** "detail=none" option. If the FTS5 table is created with either | |
| 8363 | +** "detail=none" "content=" option (i.e. if it is a contentless table), | |
| 8364 | +** then this API always iterates through an empty set (all calls to | |
| 8365 | +** xPhraseFirstColumn() set iCol to -1). | |
| 8366 | +** | |
| 8367 | +** The information accessed using this API and its companion | |
| 8368 | +** xPhraseFirstColumn() may also be obtained using xPhraseFirst/xPhraseNext | |
| 8369 | +** (or xInst/xInstCount). The chief advantage of this API is that it is | |
| 8370 | +** significantly more efficient than those alternatives when used with | |
| 8371 | +** "detail=column" tables. | |
| 8372 | +** | |
| 8373 | +** xPhraseNextColumn() | |
| 8374 | +** See xPhraseFirstColumn above. | |
| 8314 | 8375 | */ |
| 8315 | 8376 | struct Fts5ExtensionApi { |
| 8316 | - int iVersion; /* Currently always set to 1 */ | |
| 8377 | + int iVersion; /* Currently always set to 3 */ | |
| 8317 | 8378 | |
| 8318 | 8379 | void *(*xUserData)(Fts5Context*); |
| 8319 | 8380 | |
| 8320 | 8381 | int (*xColumnCount)(Fts5Context*); |
| 8321 | 8382 | int (*xRowCount)(Fts5Context*, sqlite3_int64 *pnRow); |
| @@ -8341,12 +8402,15 @@ | ||
| 8341 | 8402 | int(*)(const Fts5ExtensionApi*,Fts5Context*,void*) |
| 8342 | 8403 | ); |
| 8343 | 8404 | int (*xSetAuxdata)(Fts5Context*, void *pAux, void(*xDelete)(void*)); |
| 8344 | 8405 | void *(*xGetAuxdata)(Fts5Context*, int bClear); |
| 8345 | 8406 | |
| 8346 | - void (*xPhraseFirst)(Fts5Context*, int iPhrase, Fts5PhraseIter*, int*, int*); | |
| 8407 | + int (*xPhraseFirst)(Fts5Context*, int iPhrase, Fts5PhraseIter*, int*, int*); | |
| 8347 | 8408 | void (*xPhraseNext)(Fts5Context*, Fts5PhraseIter*, int *piCol, int *piOff); |
| 8409 | + | |
| 8410 | + int (*xPhraseFirstColumn)(Fts5Context*, int iPhrase, Fts5PhraseIter*, int*); | |
| 8411 | + void (*xPhraseNextColumn)(Fts5Context*, Fts5PhraseIter*, int *piCol); | |
| 8348 | 8412 | }; |
| 8349 | 8413 | |
| 8350 | 8414 | /* |
| 8351 | 8415 | ** CUSTOM AUXILIARY FUNCTIONS |
| 8352 | 8416 | *************************************************************************/ |
| 8353 | 8417 |
| --- src/sqlite3.h | |
| +++ src/sqlite3.h | |
| @@ -109,13 +109,13 @@ | |
| 109 | ** |
| 110 | ** See also: [sqlite3_libversion()], |
| 111 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 112 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 113 | */ |
| 114 | #define SQLITE_VERSION "3.10.0" |
| 115 | #define SQLITE_VERSION_NUMBER 3010000 |
| 116 | #define SQLITE_SOURCE_ID "2016-01-06 11:01:07 fd0a50f0797d154fefff724624f00548b5320566" |
| 117 | |
| 118 | /* |
| 119 | ** CAPI3REF: Run-Time Library Version Numbers |
| 120 | ** KEYWORDS: sqlite3_version, sqlite3_sourceid |
| 121 | ** |
| @@ -792,12 +792,17 @@ | |
| 792 | ** improve performance on some systems. |
| 793 | ** |
| 794 | ** <li>[[SQLITE_FCNTL_FILE_POINTER]] |
| 795 | ** The [SQLITE_FCNTL_FILE_POINTER] opcode is used to obtain a pointer |
| 796 | ** to the [sqlite3_file] object associated with a particular database |
| 797 | ** connection. See the [sqlite3_file_control()] documentation for |
| 798 | ** additional information. |
| 799 | ** |
| 800 | ** <li>[[SQLITE_FCNTL_SYNC_OMITTED]] |
| 801 | ** No longer in use. |
| 802 | ** |
| 803 | ** <li>[[SQLITE_FCNTL_SYNC]] |
| @@ -1008,10 +1013,11 @@ | |
| 1008 | #define SQLITE_FCNTL_WIN32_SET_HANDLE 23 |
| 1009 | #define SQLITE_FCNTL_WAL_BLOCK 24 |
| 1010 | #define SQLITE_FCNTL_ZIPVFS 25 |
| 1011 | #define SQLITE_FCNTL_RBU 26 |
| 1012 | #define SQLITE_FCNTL_VFS_POINTER 27 |
| 1013 | |
| 1014 | /* deprecated names */ |
| 1015 | #define SQLITE_GET_LOCKPROXYFILE SQLITE_FCNTL_GET_LOCKPROXYFILE |
| 1016 | #define SQLITE_SET_LOCKPROXYFILE SQLITE_FCNTL_SET_LOCKPROXYFILE |
| 1017 | #define SQLITE_LAST_ERRNO SQLITE_FCNTL_LAST_ERRNO |
| @@ -8185,10 +8191,13 @@ | |
| 8185 | ** If parameter iCol is greater than or equal to the number of columns |
| 8186 | ** in the table, SQLITE_RANGE is returned. Or, if an error occurs (e.g. |
| 8187 | ** an OOM condition or IO error), an appropriate SQLite error code is |
| 8188 | ** returned. |
| 8189 | ** |
| 8190 | ** xColumnText: |
| 8191 | ** This function attempts to retrieve the text of column iCol of the |
| 8192 | ** current document. If successful, (*pz) is set to point to a buffer |
| 8193 | ** containing the text in utf-8 encoding, (*pn) is set to the size in bytes |
| 8194 | ** (not characters) of the buffer and SQLITE_OK is returned. Otherwise, |
| @@ -8205,18 +8214,32 @@ | |
| 8205 | ** xInstCount: |
| 8206 | ** Set *pnInst to the total number of occurrences of all phrases within |
| 8207 | ** the query within the current row. Return SQLITE_OK if successful, or |
| 8208 | ** an error code (i.e. SQLITE_NOMEM) if an error occurs. |
| 8209 | ** |
| 8210 | ** xInst: |
| 8211 | ** Query for the details of phrase match iIdx within the current row. |
| 8212 | ** Phrase matches are numbered starting from zero, so the iIdx argument |
| 8213 | ** should be greater than or equal to zero and smaller than the value |
| 8214 | ** output by xInstCount(). |
| 8215 | ** |
| 8216 | ** Returns SQLITE_OK if successful, or an error code (i.e. SQLITE_NOMEM) |
| 8217 | ** if an error occurs. |
| 8218 | ** |
| 8219 | ** xRowid: |
| 8220 | ** Returns the rowid of the current row. |
| 8221 | ** |
| 8222 | ** xTokenize: |
| @@ -8297,25 +8320,63 @@ | |
| 8297 | ** through instances of phrase iPhrase, use the following code: |
| 8298 | ** |
| 8299 | ** Fts5PhraseIter iter; |
| 8300 | ** int iCol, iOff; |
| 8301 | ** for(pApi->xPhraseFirst(pFts, iPhrase, &iter, &iCol, &iOff); |
| 8302 | ** iOff>=0; |
| 8303 | ** pApi->xPhraseNext(pFts, &iter, &iCol, &iOff) |
| 8304 | ** ){ |
| 8305 | ** // An instance of phrase iPhrase at offset iOff of column iCol |
| 8306 | ** } |
| 8307 | ** |
| 8308 | ** The Fts5PhraseIter structure is defined above. Applications should not |
| 8309 | ** modify this structure directly - it should only be used as shown above |
| 8310 | ** with the xPhraseFirst() and xPhraseNext() API methods. |
| 8311 | ** |
| 8312 | ** xPhraseNext() |
| 8313 | ** See xPhraseFirst above. |
| 8314 | */ |
| 8315 | struct Fts5ExtensionApi { |
| 8316 | int iVersion; /* Currently always set to 1 */ |
| 8317 | |
| 8318 | void *(*xUserData)(Fts5Context*); |
| 8319 | |
| 8320 | int (*xColumnCount)(Fts5Context*); |
| 8321 | int (*xRowCount)(Fts5Context*, sqlite3_int64 *pnRow); |
| @@ -8341,12 +8402,15 @@ | |
| 8341 | int(*)(const Fts5ExtensionApi*,Fts5Context*,void*) |
| 8342 | ); |
| 8343 | int (*xSetAuxdata)(Fts5Context*, void *pAux, void(*xDelete)(void*)); |
| 8344 | void *(*xGetAuxdata)(Fts5Context*, int bClear); |
| 8345 | |
| 8346 | void (*xPhraseFirst)(Fts5Context*, int iPhrase, Fts5PhraseIter*, int*, int*); |
| 8347 | void (*xPhraseNext)(Fts5Context*, Fts5PhraseIter*, int *piCol, int *piOff); |
| 8348 | }; |
| 8349 | |
| 8350 | /* |
| 8351 | ** CUSTOM AUXILIARY FUNCTIONS |
| 8352 | *************************************************************************/ |
| 8353 |
| --- src/sqlite3.h | |
| +++ src/sqlite3.h | |
| @@ -109,13 +109,13 @@ | |
| 109 | ** |
| 110 | ** See also: [sqlite3_libversion()], |
| 111 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 112 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 113 | */ |
| 114 | #define SQLITE_VERSION "3.11.0" |
| 115 | #define SQLITE_VERSION_NUMBER 3011000 |
| 116 | #define SQLITE_SOURCE_ID "2016-01-14 14:19:50 d17bc2c92f4d086280e49a3cc72993be7fee2da7" |
| 117 | |
| 118 | /* |
| 119 | ** CAPI3REF: Run-Time Library Version Numbers |
| 120 | ** KEYWORDS: sqlite3_version, sqlite3_sourceid |
| 121 | ** |
| @@ -792,12 +792,17 @@ | |
| 792 | ** improve performance on some systems. |
| 793 | ** |
| 794 | ** <li>[[SQLITE_FCNTL_FILE_POINTER]] |
| 795 | ** The [SQLITE_FCNTL_FILE_POINTER] opcode is used to obtain a pointer |
| 796 | ** to the [sqlite3_file] object associated with a particular database |
| 797 | ** connection. See also [SQLITE_FCNTL_JOURNAL_POINTER]. |
| 798 | ** |
| 799 | ** <li>[[SQLITE_FCNTL_JOURNAL_POINTER]] |
| 800 | ** The [SQLITE_FCNTL_JOURNAL_POINTER] opcode is used to obtain a pointer |
| 801 | ** to the [sqlite3_file] object associated with the journal file (either |
| 802 | ** the [rollback journal] or the [write-ahead log]) for a particular database |
| 803 | ** connection. See also [SQLITE_FCNTL_FILE_POINTER]. |
| 804 | ** |
| 805 | ** <li>[[SQLITE_FCNTL_SYNC_OMITTED]] |
| 806 | ** No longer in use. |
| 807 | ** |
| 808 | ** <li>[[SQLITE_FCNTL_SYNC]] |
| @@ -1008,10 +1013,11 @@ | |
| 1013 | #define SQLITE_FCNTL_WIN32_SET_HANDLE 23 |
| 1014 | #define SQLITE_FCNTL_WAL_BLOCK 24 |
| 1015 | #define SQLITE_FCNTL_ZIPVFS 25 |
| 1016 | #define SQLITE_FCNTL_RBU 26 |
| 1017 | #define SQLITE_FCNTL_VFS_POINTER 27 |
| 1018 | #define SQLITE_FCNTL_JOURNAL_POINTER 28 |
| 1019 | |
| 1020 | /* deprecated names */ |
| 1021 | #define SQLITE_GET_LOCKPROXYFILE SQLITE_FCNTL_GET_LOCKPROXYFILE |
| 1022 | #define SQLITE_SET_LOCKPROXYFILE SQLITE_FCNTL_SET_LOCKPROXYFILE |
| 1023 | #define SQLITE_LAST_ERRNO SQLITE_FCNTL_LAST_ERRNO |
| @@ -8185,10 +8191,13 @@ | |
| 8191 | ** If parameter iCol is greater than or equal to the number of columns |
| 8192 | ** in the table, SQLITE_RANGE is returned. Or, if an error occurs (e.g. |
| 8193 | ** an OOM condition or IO error), an appropriate SQLite error code is |
| 8194 | ** returned. |
| 8195 | ** |
| 8196 | ** This function may be quite inefficient if used with an FTS5 table |
| 8197 | ** created with the "columnsize=0" option. |
| 8198 | ** |
| 8199 | ** xColumnText: |
| 8200 | ** This function attempts to retrieve the text of column iCol of the |
| 8201 | ** current document. If successful, (*pz) is set to point to a buffer |
| 8202 | ** containing the text in utf-8 encoding, (*pn) is set to the size in bytes |
| 8203 | ** (not characters) of the buffer and SQLITE_OK is returned. Otherwise, |
| @@ -8205,18 +8214,32 @@ | |
| 8214 | ** xInstCount: |
| 8215 | ** Set *pnInst to the total number of occurrences of all phrases within |
| 8216 | ** the query within the current row. Return SQLITE_OK if successful, or |
| 8217 | ** an error code (i.e. SQLITE_NOMEM) if an error occurs. |
| 8218 | ** |
| 8219 | ** This API can be quite slow if used with an FTS5 table created with the |
| 8220 | ** "detail=none" or "detail=column" option. If the FTS5 table is created |
| 8221 | ** with either "detail=none" or "detail=column" and "content=" option |
| 8222 | ** (i.e. if it is a contentless table), then this API always returns 0. |
| 8223 | ** |
| 8224 | ** xInst: |
| 8225 | ** Query for the details of phrase match iIdx within the current row. |
| 8226 | ** Phrase matches are numbered starting from zero, so the iIdx argument |
| 8227 | ** should be greater than or equal to zero and smaller than the value |
| 8228 | ** output by xInstCount(). |
| 8229 | ** |
| 8230 | ** Usually, output parameter *piPhrase is set to the phrase number, *piCol |
| 8231 | ** to the column in which it occurs and *piOff the token offset of the |
| 8232 | ** first token of the phrase. The exception is if the table was created |
| 8233 | ** with the offsets=0 option specified. In this case *piOff is always |
| 8234 | ** set to -1. |
| 8235 | ** |
| 8236 | ** Returns SQLITE_OK if successful, or an error code (i.e. SQLITE_NOMEM) |
| 8237 | ** if an error occurs. |
| 8238 | ** |
| 8239 | ** This API can be quite slow if used with an FTS5 table created with the |
| 8240 | ** "detail=none" or "detail=column" option. |
| 8241 | ** |
| 8242 | ** xRowid: |
| 8243 | ** Returns the rowid of the current row. |
| 8244 | ** |
| 8245 | ** xTokenize: |
| @@ -8297,25 +8320,63 @@ | |
| 8320 | ** through instances of phrase iPhrase, use the following code: |
| 8321 | ** |
| 8322 | ** Fts5PhraseIter iter; |
| 8323 | ** int iCol, iOff; |
| 8324 | ** for(pApi->xPhraseFirst(pFts, iPhrase, &iter, &iCol, &iOff); |
| 8325 | ** iCol>=0; |
| 8326 | ** pApi->xPhraseNext(pFts, &iter, &iCol, &iOff) |
| 8327 | ** ){ |
| 8328 | ** // An instance of phrase iPhrase at offset iOff of column iCol |
| 8329 | ** } |
| 8330 | ** |
| 8331 | ** The Fts5PhraseIter structure is defined above. Applications should not |
| 8332 | ** modify this structure directly - it should only be used as shown above |
| 8333 | ** with the xPhraseFirst() and xPhraseNext() API methods (and by |
| 8334 | ** xPhraseFirstColumn() and xPhraseNextColumn() as illustrated below). |
| 8335 | ** |
| 8336 | ** This API can be quite slow if used with an FTS5 table created with the |
| 8337 | ** "detail=none" or "detail=column" option. If the FTS5 table is created |
| 8338 | ** with either "detail=none" or "detail=column" and "content=" option |
| 8339 | ** (i.e. if it is a contentless table), then this API always iterates |
| 8340 | ** through an empty set (all calls to xPhraseFirst() set iCol to -1). |
| 8341 | ** |
| 8342 | ** xPhraseNext() |
| 8343 | ** See xPhraseFirst above. |
| 8344 | ** |
| 8345 | ** xPhraseFirstColumn() |
| 8346 | ** This function and xPhraseNextColumn() are similar to the xPhraseFirst() |
| 8347 | ** and xPhraseNext() APIs described above. The difference is that instead |
| 8348 | ** of iterating through all instances of a phrase in the current row, these |
| 8349 | ** APIs are used to iterate through the set of columns in the current row |
| 8350 | ** that contain one or more instances of a specified phrase. For example: |
| 8351 | ** |
| 8352 | ** Fts5PhraseIter iter; |
| 8353 | ** int iCol; |
| 8354 | ** for(pApi->xPhraseFirstColumn(pFts, iPhrase, &iter, &iCol); |
| 8355 | ** iCol>=0; |
| 8356 | ** pApi->xPhraseNextColumn(pFts, &iter, &iCol) |
| 8357 | ** ){ |
| 8358 | ** // Column iCol contains at least one instance of phrase iPhrase |
| 8359 | ** } |
| 8360 | ** |
| 8361 | ** This API can be quite slow if used with an FTS5 table created with the |
| 8362 | ** "detail=none" option. If the FTS5 table is created with either |
| 8363 | ** "detail=none" "content=" option (i.e. if it is a contentless table), |
| 8364 | ** then this API always iterates through an empty set (all calls to |
| 8365 | ** xPhraseFirstColumn() set iCol to -1). |
| 8366 | ** |
| 8367 | ** The information accessed using this API and its companion |
| 8368 | ** xPhraseFirstColumn() may also be obtained using xPhraseFirst/xPhraseNext |
| 8369 | ** (or xInst/xInstCount). The chief advantage of this API is that it is |
| 8370 | ** significantly more efficient than those alternatives when used with |
| 8371 | ** "detail=column" tables. |
| 8372 | ** |
| 8373 | ** xPhraseNextColumn() |
| 8374 | ** See xPhraseFirstColumn above. |
| 8375 | */ |
| 8376 | struct Fts5ExtensionApi { |
| 8377 | int iVersion; /* Currently always set to 3 */ |
| 8378 | |
| 8379 | void *(*xUserData)(Fts5Context*); |
| 8380 | |
| 8381 | int (*xColumnCount)(Fts5Context*); |
| 8382 | int (*xRowCount)(Fts5Context*, sqlite3_int64 *pnRow); |
| @@ -8341,12 +8402,15 @@ | |
| 8402 | int(*)(const Fts5ExtensionApi*,Fts5Context*,void*) |
| 8403 | ); |
| 8404 | int (*xSetAuxdata)(Fts5Context*, void *pAux, void(*xDelete)(void*)); |
| 8405 | void *(*xGetAuxdata)(Fts5Context*, int bClear); |
| 8406 | |
| 8407 | int (*xPhraseFirst)(Fts5Context*, int iPhrase, Fts5PhraseIter*, int*, int*); |
| 8408 | void (*xPhraseNext)(Fts5Context*, Fts5PhraseIter*, int *piCol, int *piOff); |
| 8409 | |
| 8410 | int (*xPhraseFirstColumn)(Fts5Context*, int iPhrase, Fts5PhraseIter*, int*); |
| 8411 | void (*xPhraseNextColumn)(Fts5Context*, Fts5PhraseIter*, int *piCol); |
| 8412 | }; |
| 8413 | |
| 8414 | /* |
| 8415 | ** CUSTOM AUXILIARY FUNCTIONS |
| 8416 | *************************************************************************/ |
| 8417 |