diff --git a/libstuff/sqlite3.c b/libstuff/sqlite3.c index d19872d22..eb4004e37 100644 --- a/libstuff/sqlite3.c +++ b/libstuff/sqlite3.c @@ -18,7 +18,7 @@ ** separate file. This file contains only code for the core SQLite library. ** ** The content in this amalgamation comes from Fossil check-in -** 45f6fccc2ed3e0b6d6f72cf3aeb849a77eac with changes in files: +** 012a04edd0ab7001f2635eeb766089201cbe with changes in files: ** ** */ @@ -476,10 +476,10 @@ extern "C" { */ #define SQLITE_VERSION "3.54.0" #define SQLITE_VERSION_NUMBER 3054000 -#define SQLITE_SOURCE_ID "2026-07-09 16:12:25 45f6fccc2ed3e0b6d6f72cf3aeb849a77eace5f8675f40d3fff-experimental" -#define SQLITE_SCM_BRANCH "unknown" -#define SQLITE_SCM_TAGS "unknown" -#define SQLITE_SCM_DATETIME "2026-07-09T16:12:25.329Z" +#define SQLITE_SOURCE_ID "2026-05-12 17:42:11 012a04edd0ab7001f2635eeb766089201cbe372d54e5008e7138a5dbe522bf06" +#define SQLITE_SCM_BRANCH "hctree-bedrock" +#define SQLITE_SCM_TAGS "" +#define SQLITE_SCM_DATETIME "2026-05-12T17:42:11.503Z" /* ** CAPI3REF: Run-Time Library Version Numbers @@ -2538,19 +2538,6 @@ struct sqlite3_mem_methods { ** recommended case) then the integer is always filled with zero, regardless ** if its initial value. ** -** -** [[SQLITE_CONFIG_SHAREDLOG_MAXSIZE]] -**
SQLITE_CONFIG_SHAREDLOG_MAXSIZE -**
This option is used to set the maximum size of a BEGIN CONCURRENT -** shared-log in bytes. The default value is 1GiB (1*1024*1024*1024). The -** argument to this configuration option must be a 64-bit signed integer -** (type sqlite3_int64) to use as the new limit. A negative parameter -** restores the default value, a value of zero disabled shared-logs -** altogether. There is no way to configure unlimited memory usage, but -** applications may instead configure a very large value (e.g. 1TiB). -** Shared-log entries are automatically discarded when their associated -** transactions are checkpointed, which prevents the shared-log from growing -** indefinitely in this case. */ #define SQLITE_CONFIG_SINGLETHREAD 1 /* nil */ #define SQLITE_CONFIG_MULTITHREAD 2 /* nil */ @@ -2582,7 +2569,6 @@ struct sqlite3_mem_methods { #define SQLITE_CONFIG_SORTERREF_SIZE 28 /* int nByte */ #define SQLITE_CONFIG_MEMDB_MAXSIZE 29 /* sqlite3_int64 */ #define SQLITE_CONFIG_ROWID_IN_VIEW 30 /* int* */ -#define SQLITE_CONFIG_SHAREDLOG_MAXSIZE 31 /* sqlite3_int64 */ /* ** CAPI3REF: Database Connection Configuration Options @@ -17206,11 +17192,9 @@ SQLITE_PRIVATE int sqlite3PagerUsePage(Pager*, Pgno); SQLITE_PRIVATE void sqlite3PagerEndConcurrent(Pager*); SQLITE_PRIVATE int sqlite3PagerBeginConcurrent(Pager*); SQLITE_PRIVATE void sqlite3PagerDropExclusiveLock(Pager*); -SQLITE_PRIVATE int sqlite3PagerUpgradeSnapshot(Pager *pPager, DbPage*, int); +SQLITE_PRIVATE int sqlite3PagerUpgradeSnapshot(Pager *pPager, DbPage*); SQLITE_PRIVATE void sqlite3PagerSetDbsize(Pager *pPager, Pgno); SQLITE_PRIVATE int sqlite3PagerIsWal(Pager*); -SQLITE_PRIVATE u64 sqlite3PagerWalCommitId(Pager *pPager); -SQLITE_PRIVATE u64 sqlite3PagerWalLiveId(Pager *pPager); #else # define sqlite3PagerEndConcurrent(x) # define sqlite3PagerUsePage(x, y) SQLITE_OK @@ -17516,12 +17500,6 @@ SQLITE_PRIVATE int sqlite3BtreeCursorHasMoved(BtCursor*); SQLITE_PRIVATE int sqlite3BtreeCursorRestore(BtCursor*, int*); SQLITE_PRIVATE int sqlite3BtreeDelete(BtCursor*, u8 flags); -#ifndef SQLITE_OMIT_CONCURRENT -SQLITE_PRIVATE void sqlite3BtreeCursorNoScan(BtCursor*); -#else -# define sqlite3BtreeCursorNoScan(x) -#endif - /* Allowed flags for sqlite3BtreeDelete() and sqlite3BtreeInsert() */ #define BTREE_SAVEPOSITION 0x02 /* Leave cursor pointing at NEXT or PREV */ #define BTREE_AUXDELETE 0x04 /* not the primary delete operation */ @@ -18466,7 +18444,6 @@ SQLITE_PRIVATE int sqlite3HctBtreeClearTableOfCursor(BtCursor*); SQLITE_PRIVATE int sqlite3HctBtreeCount(sqlite3*, BtCursor*, i64*); SQLITE_PRIVATE i64 sqlite3HctBtreeOffset(BtCursor*); SQLITE_PRIVATE int sqlite3HctBtreeIsEmpty(BtCursor*, int*); -SQLITE_PRIVATE void sqlite3HctBtreeCursorNoScan(BtCursor*); SQLITE_PRIVATE int sqlite3StockBtreeCursor(Btree*, Pgno, int, struct KeyInfo*, BtCursor*); SQLITE_PRIVATE sqlite3_uint64 sqlite3StockBtreeSeekCount(Btree*); SQLITE_PRIVATE Pgno sqlite3StockBtreeLastPage(Btree*); @@ -18546,7 +18523,6 @@ SQLITE_PRIVATE int sqlite3StockBtreeClearTableOfCursor(BtCursor*); SQLITE_PRIVATE int sqlite3StockBtreeCount(sqlite3*, BtCursor*, i64*); SQLITE_PRIVATE i64 sqlite3StockBtreeOffset(BtCursor*); SQLITE_PRIVATE int sqlite3StockBtreeIsEmpty(BtCursor*, int*); -SQLITE_PRIVATE void sqlite3StockBtreeCursorNoScan(BtCursor*); SQLITE_PRIVATE BtCursor *sqlite3StockBtreeFakeValidCursor(void); @@ -19232,16 +19208,6 @@ struct sqlite3 { #define CONCURRENT_OPEN 1 #define CONCURRENT_SCHEMA 2 -/* -** Maximum size in bytes of all allocations associated with a single -** BEGIN CONCURRENT shared-log. -*/ -#define SQLITE_DEFAULT_SHAREDLOG_MAXSIZE (1*1024*1024*1024) - -#ifndef SQLITE_OMIT_CONCURRENT -SQLITE_PRIVATE int sqlite3ConcurrentRegister(sqlite3 *db); -#endif - /* ** A macro to discover the encoding of a database. */ @@ -21781,9 +21747,6 @@ struct Sqlite3Config { int iOnceResetThreshold; /* When to reset OP_Once counters */ u32 szSorterRef; /* Min size in bytes to use sorter-refs */ unsigned int iPrngSeed; /* Alternative fixed seed for the PRNG */ -#ifndef SQLITE_OMIT_CONCURRENT - i64 nSharedLogMaxSize; /* Max number of all shared-log entries */ -#endif /* vvvv--- must be last ---vvv */ #ifdef SQLITE_DEBUG sqlite3_int64 aTune[SQLITE_NTUNE]; /* Tuning parameters */ @@ -24622,9 +24585,6 @@ SQLITE_PRIVATE SQLITE_WSD struct Sqlite3Config sqlite3Config = { 0x7ffffffe, /* iOnceResetThreshold */ SQLITE_DEFAULT_SORTERREF_SIZE, /* szSorterRef */ 0, /* iPrngSeed */ -#ifndef SQLITE_OMIT_CONCURRENT - SQLITE_DEFAULT_SHAREDLOG_MAXSIZE, /* nSharedLogMaxSize */ -#endif #ifdef SQLITE_DEBUG {0,0,0,0,0,0}, /* aTune */ #endif @@ -60357,9 +60317,6 @@ SQLITE_PRIVATE int sqlite3WalLockForCommit(Wal *pWal, PgHdr *pPg, Bitvec *pRead, /* Upgrade the state of the client to take into account changes written ** by other connections */ SQLITE_PRIVATE int sqlite3WalUpgradeSnapshot(Wal *pWal); - -SQLITE_PRIVATE u64 sqlite3WalCommitId(Wal *pWal); -SQLITE_PRIVATE u64 sqlite3WalLiveId(Wal *pWal); #endif /* SQLITE_OMIT_CONCURRENT */ #ifdef SQLITE_ENABLE_ZIPVFS @@ -62247,25 +62204,6 @@ SQLITE_PRIVATE void sqlite3PagerEndConcurrent(Pager *pPager){ SQLITE_PRIVATE int sqlite3PagerIsWal(Pager *pPager){ return pPager->pWal!=0; } - -/* !defined(SQLITE_OMIT_CONCURRENT) -** -** If in wal-mode, return a 64-bit value that identifies the snapshot currently -** in use. Otherwise, return 0. -*/ -SQLITE_PRIVATE u64 sqlite3PagerWalCommitId(Pager *pPager){ - return pPager->pWal ? sqlite3WalCommitId(pPager->pWal) : 0; -} - -/* !defined(SQLITE_OMIT_CONCURRENT) -** -** If in wal-mode, return a 64-bit value that identifies the snapshot -** currently at the head of the wal file. Otherwise, return 0. -*/ -SQLITE_PRIVATE u64 sqlite3PagerWalLiveId(Pager *pPager){ - return pPager->pWal ? sqlite3WalLiveId(pPager->pWal) : 0; -} - #endif /* SQLITE_OMIT_CONCURRENT */ /* @@ -65976,7 +65914,6 @@ SQLITE_PRIVATE int sqlite3PagerUsePage(Pager *pPager, Pgno pgno){ } return rc; } - #endif /* @@ -66981,10 +66918,9 @@ SQLITE_PRIVATE int sqlite3PagerExclusiveLock(Pager *pPager, PgHdr *pPage1, u32 * #ifndef SQLITE_OMIT_CONCURRENT /* ** This function is called as part of committing an CONCURRENT transaction. -** At this point the wal WRITER lock is held. If parameter bReset is 0, -** all pages in the cache except for page 1 are compatible with the snapshot -** at the head of the wal file. Or, if bReset is non-zero, the cache is -** cleared of all pages except for page 1. +** At this point the wal WRITER lock is held, and all pages in the cache +** except for page 1 are compatible with the snapshot at the head of the +** wal file. ** ** This function updates the in-memory data structures and reloads the ** contents of page 1 so that the client is operating on the snapshot @@ -66992,7 +66928,7 @@ SQLITE_PRIVATE int sqlite3PagerExclusiveLock(Pager *pPager, PgHdr *pPage1, u32 * ** ** SQLITE_OK is returned if successful, or an SQLite error code otherwise. */ -SQLITE_PRIVATE int sqlite3PagerUpgradeSnapshot(Pager *pPager, DbPage *pPage1, int bReset){ +SQLITE_PRIVATE int sqlite3PagerUpgradeSnapshot(Pager *pPager, DbPage *pPage1){ int rc; assert( pPager->pWal && pPager->pAllRead ); @@ -67000,9 +66936,7 @@ SQLITE_PRIVATE int sqlite3PagerUpgradeSnapshot(Pager *pPager, DbPage *pPage1, in if( rc==SQLITE_OK ){ rc = readDbPage(pPage1); } - if( bReset && rc==SQLITE_OK ){ - sqlite3PcacheTruncate(pPager->pPCache, 1); - } + return rc; } @@ -73285,29 +73219,6 @@ SQLITE_PRIVATE int sqlite3WalUpgradeSnapshot(Wal *pWal){ } SEH_EXCEPT( rc = SQLITE_IOERR_IN_PAGE; ) return rc; } - -/* !defined(SQLITE_OMIT_CONCURRENT) -** -** Return a 64-bit value that identifies the snapshot currently in use. -*/ -SQLITE_PRIVATE u64 sqlite3WalCommitId(Wal *pWal){ - return ((u64)(pWal->hdr.aCksum[0]) << 32) + pWal->hdr.aCksum[1]; -} - -/* !defined(SQLITE_OMIT_CONCURRENT) -** -** Return a 64-bit value that identifies the snapshot currently at the -** head of the wal file. -*/ -SQLITE_PRIVATE u64 sqlite3WalLiveId(Wal *pWal){ - WalIndexHdr hdr; - assert( pWal->writeLock ); - SEH_TRY { - walIndexLoadHdr(pWal, &hdr); - } SEH_EXCEPT( memset(&hdr, 0, sizeof(hdr)); ) - return ((u64)(hdr.aCksum[0]) << 32) + hdr.aCksum[1]; -} - #endif /* SQLITE_OMIT_CONCURRENT */ /* @@ -73378,10 +73289,9 @@ SQLITE_PRIVATE int sqlite3WalUndo( } if( walidxGetFile(&pWal->hdr)!=iWal ){ assert( bConcurrent && isWalMode2(pWal) ); - assert( rc==SQLITE_OK ); - }else + return SQLITE_OK; + } #endif - { assert( walidxGetFile(&pWal->hdr)==iWal ); for(iFrame=iNew+1; ALWAYS(rc==SQLITE_OK) && iFrame<=iMax; iFrame++){ @@ -73406,7 +73316,6 @@ SQLITE_PRIVATE int sqlite3WalUndo( rc = xUndo(pUndoCtx, pgno); } if( iMax!=iNew ) walCleanupHash(pWal); - } } SEH_EXCEPT( rc = SQLITE_IOERR_IN_PAGE; ) pWal->iReCksum = 0; @@ -74674,15 +74583,6 @@ typedef struct BtLock BtLock; typedef struct CellInfo CellInfo; typedef struct BtreePtrmap BtreePtrmap; -typedef struct BtConcurrent BtConcurrent; -typedef struct BtReadIndex BtReadIndex; -typedef struct BtReadIntkey BtReadIntkey; -typedef struct BtSharedLog BtSharedLog; -typedef struct BtSharedLogEntry BtSharedLogEntry; -typedef struct BtWrite BtWrite; -typedef struct BtWriteIndex BtWriteIndex; -typedef struct BtWriteIntkey BtWriteIntkey; - /* ** This is a magic string that appears at the beginning of every ** SQLite database in order to identify the file as a real database. @@ -74774,201 +74674,6 @@ struct BtLock { #define READ_LOCK 1 #define WRITE_LOCK 2 -/* -** All connections to a single database file that use BEGIN CONCURRENT within -** the process share an instance of this object. Its purpose is to store -** a list of BtSharedLogEntry objects associated with the database file. -*/ -struct BtSharedLog { - sqlite3_mutex *mutex; /* Mutex protecting this object */ - int nRef; /* Number of users of this struct */ - char *zFullname; /* Path identifying this database */ - - BtSharedLogEntry *pFirst; - BtSharedLogEntry *pLast; - i64 nByte; /* Total size of all entries in bytes */ - - /* Linked list protected by SQLITE_MUTEX_STATIC_MAIN */ - BtSharedLog *pSharedNext; /* Next shared log in process */ -}; - -/* -** Each time a BEGIN CONCURRENT transaction is committed, an instance of -** the following object is added to the list maintained by the database -** file's BtSharedLog object. -** -** iBaseId: -** The sqlite3WalCommitId() value at the head of the wal file when this -** transaction was written. -** -** iThisId: -** The sqlite3WalCommitId() value at the head of the wal file after this -** transaction was written. i.e. the snapshot created by this write. -** -** iFirstFrame: -** First wal frame written by this transaction. If in a *-wal2 file, the -** 0x80000000 bit is set. -** -** nFrame: -** Total number of frames written by this transaction. -** -** aIntkey/nIntkey: -** Array of rowids modified by this transaction. -** -** aIndex/nIndex: -** Array of index keys modified by this transaction. -** -** nByte: -** Total size in bytes of all allocations belonging to this object -** (including itself). -*/ -struct BtSharedLogEntry { - u64 iBaseId; /* Snapshot this transaction was based on */ - u64 iThisId; /* Snapshot this transaction created */ - u32 iFirstFrame; /* First frame written by this transaction */ - u32 nFrame; /* Number of frames written by transaction */ - - BtWriteIntkey *aIntkey; /* Writes to intkeys */ - int nIntkey; /* Size of aIntkey[] */ - BtWriteIndex *aIndex; /* Writes to indexes */ - int nIndex; /* Size of aIndex[] in bytes */ - - i64 nByte; /* Total size in bytes */ - - BtSharedLogEntry *pLogNext; /* Transaction commited after this one */ -}; - -/* -** Any more savepoints than this and the BtConcurrent object is retired. -*/ -#define BTCONC_MAX_SAVEPOINT 8 - -/* -** An single object of this type is permanently part of each BtShared -** object. It stores things related to BEGIN CONCURRENT transactions -** and the in-process shared-log. -*/ -struct BtConcurrent { - BtSharedLog *pBtLog; - int eState; /* One of the BTCONC_STATE_XXX values */ - u64 iBase; /* WalCommitId() transaction is prepared on */ - - /* Reads performed by this transaction */ - BtReadIntkey *aReadIntkey; - BtReadIndex *aReadIndex; - int nReadIntkey; - int nReadIndex; - int nReadIntkeyAlloc; - int nReadIndexAlloc; - - /* Changes made by this transaction. */ - BtWrite *aWrite; - int nWrite; - int nWriteAlloc; - - /* An UnpackedRecord structure created pKeyInfo->nKeyField==nUnpackedField */ - UnpackedRecord *pUnpacked; - int nUnpackedField; - - int aSvpt[BTCONC_MAX_SAVEPOINT];/* Set nWrite=aSvpt[i] when savepoint i rb. */ - int nSvpt; /* Current number of open savepoints */ - - /* Total size of all allocations managed by this object. */ - i64 nAlloc; -}; - -/* -** NONE: No BEGIN CONCURRENT transaction is open. -** INUSE: BEGIN CONCURRENT transaction is open and object is accumulating -** reads and writes. -** RETIRED: BEGIN CONCURRENT transaction is open but object is not in use. -** Because it grew too large or some such reason. -*/ -#define BTCONC_STATE_NONE 0 -#define BTCONC_STATE_INUSE 1 -#define BTCONC_STATE_RETIRED 2 - -/* -** Each read of an intkey or index btree by a BEGIN CONCURRENT transaction is -** represented by an object of one of the following types. -*/ -struct BtReadIntkey { - Pgno iRoot; /* Root page of table read */ - i64 iMin; /* Smallest rowid scanned */ - i64 iMax; /* Largest rowid scanned */ -}; - -/* -** Each scan of an index is represented by an instance of the following -** structure. The start of the range is encoded in (aRecMin, nRecMin, -** drc_min) and the end of the range by (aRecMax, nRecMax, drc_max). -** According to the sort order defined by pKeyInfo: -** -** (aRecMin, nRecMin, drc_min) <= (aRecMax, nRecMax, drc_max) -*/ -struct BtReadIndex { - Pgno iRoot; /* Root page of index b-tree read */ - i16 drc_min; - i16 drc_max; - KeyInfo *pKeyInfo; /* KeyInfo structure for b-tree */ - int nRecMin; /* Size of aRecFirst[] in bytes */ - int nRecMax; /* Size of aRecLast[] in bytes */ - u8 *aRecMin; /* Record for this change */ - u8 *aRecMax; /* Record for this change */ -}; - -/* -** During a BEGIN CONCURRENT transaction, each insert or delete of a b-tree -** key is represented by an instance of this structure. -** -** If the transaction is succesfully committed, the information in an -** array of this type is used to populate arrays of BtWriteIntkey and -** BtWriteIndex structs, which are stored as part of the shared-log -** entry to use for future conflict detection. -*/ -struct BtWrite { - Pgno iRoot; /* Root page of btree this change affects */ - int bDel; /* True for a delete, false for insert */ - KeyInfo *pKeyInfo; /* KeyInfo for index-btree, NULL for intkey */ - i64 iKey; /* Key for intkey updates */ - int nRec; /* Size of aRec[] in bytes */ - u8 *aRec; /* Record for this change */ -}; - -struct BtWriteIntkey { - Pgno iRoot; /* Root page of table read */ - i64 iKey; /* Key written */ -}; - -struct BtWriteIndex { - Pgno iRoot; /* Root page of table read */ - int nRec; /* Size of aRec[] in bytes (or 0) */ - u8 *aRec; /* Pointer to record */ -}; - -/* Values for BtRead.eRead - how the scan was started. */ -#define BTCONC_READ_FIRST 1 -#define BTCONC_READ_MOVETO 2 -#define BTCONC_READ_LAST 3 -#define BTCONC_READ_COUNT 4 - -SQLITE_PRIVATE int sqlite3BcSerializeRecord( - UnpackedRecord *pRec, /* Record to serialize */ - u8 **ppRec, /* OUT: buffer containing serialization */ - int *pnRec /* OUT: size of (*ppRec) in bytes */ -); - -SQLITE_PRIVATE int sqlite3BtreeSortReadArrays(BtConcurrent *pBtConc); - -SQLITE_PRIVATE void sqlite3BcLogIndexConflict( - const char *zTab, - const char *zIdx, - BtWriteIndex *pWrite, - BtReadIndex *pRead -); -SQLITE_PRIVATE const char *sqlite3BcCurrentSql(sqlite3 *db); - - /* A Btree handle ** ** A database connection contains a pointer to an instance of @@ -75113,7 +74818,6 @@ struct BtShared { u8 *pTmpSpace; /* Temp space sufficient to hold a single cell */ #ifndef SQLITE_OMIT_CONCURRENT BtreePtrmap *pMap; - BtConcurrent conc; #endif int nPreformatSize; /* Size of last cell written by TransferRow() */ }; @@ -75198,11 +74902,6 @@ struct BtCursor { Btree *pBtree; /* The Btree to which this cursor belongs */ Pgno *aOverflow; /* Cache of overflow page locations */ void *pKey; /* Saved key that was cursor last known position */ -#ifndef SQLITE_OMIT_CONCURRENT - int eScanType; /* BTCONC_READ_FIRST, LAST or MOVETO */ - int iScanDir; /* +1 if Next(), -1 if Prev(), 0 if neither */ - int iScanIndex; /* Index of pBt->conc.aReadXXX[] array plus 1 */ -#endif /* All fields above are zeroed when the cursor is allocated. See ** sqlite3BtreeCursorZero(). Fields that follow must be manually ** initialized. */ @@ -75411,7 +75110,6 @@ struct IntegrityCk { # define get2byteAligned(x) ((x)[0]<<8 | (x)[1]) #endif - /************** End of btreeInt.h ********************************************/ /************** Continuing where we left off in btmutex.c ********************/ #ifndef SQLITE_OMIT_SHARED_CACHE @@ -75761,7 +75459,6 @@ SQLITE_PRIVATE void sqlite3BtreeLeaveCursor(BtCursor *pCur){ #define sqlite3BtreeCount sqlite3StockBtreeCount #define sqlite3BtreeOffset sqlite3StockBtreeOffset #define sqlite3BtreeIsEmpty sqlite3StockBtreeIsEmpty -#define sqlite3BtreeCursorNoScan sqlite3StockBtreeCursorNoScan #define sqlite3BtreeCursor sqlite3StockBtreeCursor #ifdef SQLITE_DEBUG #define sqlite3BtreeSeekCount sqlite3StockBtreeSeekCount @@ -76425,71 +76122,10 @@ static int btreePtrmapStore( /* !defined(SQLITE_OMIT_CONCURRENT) ** -** Free the contents of the array of BtWrite objects (but not the -** array itself). -*/ -static void btreeBcFreeWriteArray(BtWrite *aWrite, int nWrite){ - int ii; - for(ii=0; iiconc.eState==BTCONC_STATE_INUSE ){ - if( nSvpt>=BTCONC_MAX_SAVEPOINT ){ - /* More than 8 nested savepoints. No logical OCC this transaction. */ - pBt->conc.eState = BTCONC_STATE_RETIRED; - }else if( nSvpt>pBt->conc.nSvpt ){ - int ii; - for(ii=pBt->conc.nSvpt; iiconc.aSvpt[ii] = pBt->conc.nWrite; - } - pBt->conc.nSvpt = nSvpt; - } - } -} - -/* !defined(SQLITE_OMIT_CONCURRENT) -** -** If this is a BEGIN CONCURRENT transaction, update the BtShared.conc -** object to reflect the fact that savepoint iSvpt has just been -** released (if op==SAVEPOINT_RELEASE) or rolled back (if -** op==SAVEPOINT_ROLLBACK). -*/ -static void btreeBcSavepointEnd(BtShared *pBt, int op, int iSvpt){ - assert( op==SAVEPOINT_ROLLBACK || op==SAVEPOINT_RELEASE ); - assert( iSvpt>=0 || (iSvpt==-1 && op==SAVEPOINT_ROLLBACK) ); - assert( pBt->conc.nSvpt>=0 ); - - if( pBt->conc.eState==BTCONC_STATE_INUSE ){ - assert( iSvpt>=0 ); - if( iSvptconc.nSvpt ){ - if( op==SAVEPOINT_RELEASE ){ - pBt->conc.nSvpt = iSvpt; - }else{ - int nNew = pBt->conc.aSvpt[iSvpt]; - btreeBcFreeWriteArray(&pBt->conc.aWrite[nNew], pBt->conc.nWrite - nNew); - pBt->conc.nWrite = nNew; - pBt->conc.nSvpt = iSvpt+1; - } - } - } -} - -/* !defined(SQLITE_OMIT_CONCURRENT) -** -** Open savepoint iSvpt, if it is not already open. +** Open savepoint iSavepoint, if it is not already open. */ static int btreePtrmapBegin(BtShared *pBt, int nSvpt){ BtreePtrmap *pMap = pBt->pMap; - btreeBcSavepointBegin(pBt, nSvpt); if( pMap && nSvpt>pMap->nSvpt ){ int i; if( nSvpt>=pMap->nSvptAlloc ){ @@ -76519,7 +76155,6 @@ static int btreePtrmapBegin(BtShared *pBt, int nSvpt){ */ static void btreePtrmapEnd(BtShared *pBt, int op, int iSvpt){ BtreePtrmap *pMap = pBt->pMap; - btreeBcSavepointEnd(pBt, op, iSvpt); if( pMap ){ assert( op==SAVEPOINT_ROLLBACK || op==SAVEPOINT_RELEASE ); assert( iSvpt>=0 || (iSvpt==-1 && op==SAVEPOINT_ROLLBACK) ); @@ -76617,1507 +76252,12 @@ static void btreePtrmapCheck(BtShared *pBt, Pgno nPage){ } } - -/* !defined(SQLITE_OMIT_CONCURRENT) -** -** (*ppArray) points to an array of elements size szElem. (*pnArrayAlloc) -** is the current allocated size of the array, (*pnArray) is the currently -** used size. Ensure there is enough space to append an element. Return -** SQLITE_OK if successful, or SQLITE_NOMEM if an OOM occurs. -*/ -static int btreeBcGrowArray( - void **ppArray, - int *pnArray, - int *pnArrayAlloc, - int szElem -){ - if( (*pnArray)>=(*pnArrayAlloc) ){ - i64 nNew = (*pnArray)==0 ? 100 : ((*pnArray) * 2); - void *pNew = sqlite3_realloc64(*ppArray, nNew * szElem); - - if( pNew ){ - *ppArray = pNew; - *pnArrayAlloc = (int)nNew; - }else{ - return SQLITE_NOMEM_BKPT; - } - } - return SQLITE_OK; -} - -/* !defined(SQLITE_OMIT_CONCURRENT) -** -** A new entry is to be appended to the write-array of the BtConcurrent -** object passed as the only argument. Ensure sufficient space is available. -** Return SQLITE_NOMEM if an OOM occurs, or SQLITE_OK otherwise. -*/ -static int btreeBcGrowWriteArray(BtConcurrent *pBtConc){ - return btreeBcGrowArray( - (void**)&pBtConc->aWrite, - &pBtConc->nWrite, - &pBtConc->nWriteAlloc, - sizeof(BtWrite) - ); -} - - -/* !defined(SQLITE_OMIT_CONCURRENT) -** -** Global linked list of all shared-logs in the process. Protected by -** mutex SQLITE_MUTEX_STATIC_MAIN. -*/ -static BtSharedLog *pGlobalBtSharedLog = 0; - -/* !defined(SQLITE_OMIT_CONCURRENT) -** -** Start using BtShared.conc, if it is not already in use. -*/ -static int btreeBcBeginConcurrent(BtShared *pBt){ - int rc = SQLITE_OK; - assert( pBt->conc.eState==BTCONC_STATE_NONE - || pBt->conc.eState==BTCONC_STATE_INUSE - || pBt->conc.eState==BTCONC_STATE_RETIRED - ); - assert( pBt->conc.eState==BTCONC_STATE_NONE || pBt->conc.pBtLog!=0 ); - if( pBt->conc.eState==BTCONC_STATE_NONE - && sqlite3GlobalConfig.nSharedLogMaxSize!=0 - ){ - - /* If this BtShared does not yet have a connection to the global - ** BtSharedLog object for this database, establish one now. Creating - ** the BtSharedLog if it does not already exist. */ - if( pBt->conc.pBtLog==0 ){ - const char *zFull = sqlite3PagerFilename(pBt->pPager, 0); - BtSharedLog *p = 0; - sqlite3_mutex_enter( sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MAIN) ); - for(p=pGlobalBtSharedLog; p; p=p->pSharedNext){ - if( 0==strcmp(zFull, p->zFullname) ) break; - } - if( p ){ - p->nRef++; - }else{ - int nFull = sqlite3Strlen30(zFull) + 1; - p = (BtSharedLog*)sqlite3MallocZero(sizeof(BtSharedLog) + nFull); - if( p==0 ){ - rc = SQLITE_NOMEM_BKPT; - }else{ - p->zFullname = (char*)&p[1]; - memcpy(p->zFullname, zFull, nFull); - p->mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST); - if( p->mutex==0 ){ - sqlite3_free(p); - p = 0; - rc = SQLITE_NOMEM_BKPT; - }else{ - p->nRef = 1; - p->pSharedNext = pGlobalBtSharedLog; - pGlobalBtSharedLog = p; - } - } - } - sqlite3_mutex_leave( sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MAIN) ); - - pBt->conc.pBtLog = p; - } - - pBt->conc.eState = BTCONC_STATE_INUSE; - pBt->conc.iBase = sqlite3PagerWalCommitId(pBt->pPager); - pBt->conc.nSvpt = 0; - } - - return rc; -} - -/* !defined(SQLITE_OMIT_CONCURRENT) -** -** Free the BtSharedLogEntry object passed as the only argument. -*/ -static void btreeBcSharedLogEntryDelete(BtSharedLogEntry *pFree){ - int ii; - for(ii=0; iinIndex; ii++){ - BtWriteIndex *p = &pFree->aIndex[ii]; - sqlite3_free(p->aRec); - } - sqlite3_free(pFree); -} - -/* !defined(SQLITE_OMIT_CONCURRENT) -** -** Remove and free the oldest entry in the BtSharedLog indicated by the -** only argument. -*/ -static void btreeBcRemoveOldest(BtSharedLog *pBtLog){ - BtSharedLogEntry *pFree = pBtLog->pFirst; - - pBtLog->pFirst = pFree->pLogNext; - if( pBtLog->pFirst==0 ){ - assert( pBtLog->pLast==pFree ); - pBtLog->pLast = 0; - } - pBtLog->nByte -= pFree->nByte; - -#ifdef SQLITE_DEBUG - { - /* Check that BtSharedLog.nByte is the sum of the nByte member of - ** all log entries. */ - i64 nTotal = 0; - BtSharedLogEntry *pEntry = pBtLog->pFirst; - for( ; pEntry; pEntry=pEntry->pLogNext) nTotal += pEntry->nByte; - assert( nTotal==pBtLog->nByte ); - } -#endif - - btreeBcSharedLogEntryDelete(pFree); -} - -/* !defined(SQLITE_OMIT_CONCURRENT) -** -** Macro to implement an in-place sort of an array of objects. Arguments: -** -** Type: The type of the array. e.g. BtReadIntkey -** aObj: The array. -** nObj: The size of the array in objects. -** compare_pA_pB: An expression that evaluates to true if (*pA)<=(*pB) -** when evaluated, where pA and pB are pointers to -** members of the aObj array, type (Type*). -*/ -#define BT_MERGESORT_BODY(Type, aObj, nObj, compare_pA_pB) { \ - if( nObj>1 ){ \ - Type *aTemp; \ - Type *pSrc; \ - Type *pDst; \ - int width; \ - int i; \ - aTemp = sqlite3Malloc(sizeof(Type) * nObj); \ - if( aTemp==0 ){ \ - return SQLITE_NOMEM; \ - } \ - pSrc = aObj; \ - pDst = aTemp; \ - for(width=1; widthnObj ) mid = nObj; \ - if( right>nObj ) right = nObj; \ - p = left; \ - q = mid; \ - k = left; \ - while( piRootiRoot) - || (pA->iRoot==pB->iRoot && pA->iMin<=pB->iMin) - )); - if( nRead>1 ){ - BtReadIntkey *pIn; - BtReadIntkey *pOut = &aRead[0]; - BtReadIntkey * const pEof = &aRead[nRead]; - - for(pIn=&aRead[1]; pInpIn[-1].iRoot) - || (pIn[0].iRoot==pIn[-1].iRoot && pIn[0].iMin>=pIn[-1].iMin) - ); - - if( pIn->iRoot==pOut->iRoot - && (pIn->iMin<=pOut->iMax || (pIn->iMin==pOut->iMax+1)) - ){ - /* Range pIn overlaps with the previous range pOut. The second - ** part of the if() statement above cannot be written more simply - ** due to potential integer overflow. */ - if( pIn->iMax>pOut->iMax ){ - pOut->iMax = pIn->iMax; - } - }else{ - /* No overlap. Start a new range. */ - *(++pOut) = *pIn; - } - } - - *pnRead = (pOut - aRead) + 1; - } - - return SQLITE_OK; -} - -/* !defined(SQLITE_OMIT_CONCURRENT) -** -** Return the result of comparing the two records: -** -** (aLeft, nLeft, drc_left) - (aRight, nRight, drc_right) -** -** Argument pUnpacked is guaranteed to point to an UnpackedRecord structure -** large enough to decode one of the records. However, the values of -** UnpackedRecord.pKeyInfo and UnpackedRecord.nField must be set manually -** before it is used. -*/ -static int btreeBcRecordCompare( - UnpackedRecord *pUnpacked, - KeyInfo *pKeyInfo, - const u8 *aLeft, int nLeft, int drc_left, - const u8 *aRight, int nRight, int drc_right -){ - int res = 0; - if( aLeft==0 ){ - /* If the record is NULL, then the value depends on the drc. (drc>0) - ** implies the smallest possible value, (drc<0) implies the largest - ** possible valaue */ - res = drc_left * -1; - assert( drc_left!=0 ); - }else if( aRight==0 ){ - res = drc_right; - assert( drc_right!=0 ); - }else{ - pUnpacked->pKeyInfo = pKeyInfo; - pUnpacked->nField = pKeyInfo->nKeyField + 1; - - /* If one key is a prefix of the other, then the key with fewer fields - ** must be passed as pUnpacked. Otherwise sqlite3VdbeRecordCompare() - ** issues an SQLITE_CORRUPT log message. */ - if( nRight<=nLeft ){ - sqlite3VdbeRecordUnpack(nRight, aRight, pUnpacked); - res = sqlite3VdbeRecordCompare(nLeft, aLeft, pUnpacked); - }else{ - sqlite3VdbeRecordUnpack(nLeft, aLeft, pUnpacked); - res = -1 * sqlite3VdbeRecordCompare(nRight, aRight, pUnpacked); - } - if( res==0 ){ - res = (drc_right - drc_left); - } - } - - if( res<0 ){ - res = -1; - }else if( res>0 ){ - res = +1; - } - - assert( res>=-1 && res<=+1 ); - return res; -} - - -/* !defined(SQLITE_OMIT_CONCURRENT) -** -** Return the result of comparing the two BtReadIndex arguments: -** -** (*pA) - (*pB) -** -** Argument pUnpacked is guaranteed to point to an UnpackedRecord structure -** large enough to use with btreeBcRecordCompare(). -*/ -static int btreeBcReadIndexCmp( - UnpackedRecord *pUnpacked, - BtReadIndex *pA, - BtReadIndex *pB -){ - if( pA->iRootiRoot ) return -1; - if( pA->iRoot>pB->iRoot ) return +1; - - return btreeBcRecordCompare(pUnpacked, pA->pKeyInfo, - pA->aRecMin, pA->nRecMin, pA->drc_min, - pB->aRecMin, pB->nRecMin, pB->drc_min - ); -} - -/* !defined(SQLITE_OMIT_CONCURRENT) -** -** Sort the BtConcurrent.aReadIndex[] array by root page number, then -** by minimum record value. Then merge overlapping ranges. -** -** Return SQLITE_NOMEM if an OOM is encountered, or SQLITE_OK otherwise. -*/ -int btreeBcReadIndexSort(BtConcurrent *pBtConc){ - int nRead = pBtConc->nReadIndex; - BtReadIndex *aRead = pBtConc->aReadIndex; - - BT_MERGESORT_BODY(BtReadIndex, aRead, nRead, ( - btreeBcReadIndexCmp(pBtConc->pUnpacked, pA, pB)<=0 - )); - - /* Merge overlapping ranges */ - if( nRead>1 ){ - BtReadIndex *pIn; - BtReadIndex *pOut = &aRead[0]; - BtReadIndex * const pEof = &aRead[nRead]; - - for(pIn=&aRead[1]; pIniRoot==pOut->iRoot ){ - - /* Compare aRecMin of this range to the aRecMax of the previous. */ - int res = btreeBcRecordCompare( - pBtConc->pUnpacked, pIn->pKeyInfo, - pIn->aRecMin, pIn->nRecMin, pIn->drc_min, - pOut->aRecMax, pOut->nRecMax, pOut->drc_max - ); - - if( res<=0 ){ - - if( pIn->aRecMax!=pIn->aRecMin || pOut->aRecMax!=pOut->aRecMin ){ - /* This range overlaps with the previous one. This call is to - ** figure out if it is entirely encapsulated within the previous - ** range (if res<=0) or if it only partially overlaps and the - ** max of the previous range needs to be extended (res>0). - ** - ** This test is not required if both pOut and pIn are an exact - ** key searchs, not true ranges (if aRecMax==aRecMin). In that - ** case, pIn is always wholly encapsulated by pOut. */ - res = btreeBcRecordCompare( - pBtConc->pUnpacked, pIn->pKeyInfo, - pIn->aRecMax, pIn->nRecMax, pIn->drc_max, - pOut->aRecMax, pOut->nRecMax, pOut->drc_max - ); - } - - sqlite3KeyInfoUnref(pIn->pKeyInfo); - if( pIn->aRecMin!=pIn->aRecMax ) sqlite3_free(pIn->aRecMin); - - if( res>0 ){ - if( pOut->aRecMax!=pOut->aRecMin ) sqlite3_free(pOut->aRecMax); - pOut->aRecMax = pIn->aRecMax; - pOut->nRecMax = pIn->nRecMax; - pOut->drc_max = pIn->drc_max; - }else{ - sqlite3_free(pIn->aRecMax); - } - - continue; - } - } - - /* This range does not overlap with the previous. Copy it directly - ** to the output. */ - *(++pOut) = *pIn; - } - - pBtConc->nReadIndex = 1 + pOut - aRead; - } - - return SQLITE_OK; -} - -/* !defined(SQLITE_OMIT_CONCURRENT) -** -** Sort the aWrite[] array by root page and key value. -** -** Return SQLITE_NOMEM if an OOM is encountered, or SQLITE_OK otherwise. -*/ -static int btreeBcWriteIntkeySort( - BtWriteIntkey *aWrite, - int nWrite -){ - BT_MERGESORT_BODY(BtWriteIntkey, aWrite, nWrite, ( - pA->iRootiRoot - || (pA->iRoot==pB->iRoot && pA->iKey <= pB->iKey) - )); - return SQLITE_OK; -} - -/* !defined(SQLITE_OMIT_CONCURRENT) -** -** Helper function for btreeBcWriteIndexSort(). -*/ -static int btreeBcWriteIndexCmp( - UnpackedRecord *pUnpacked, - BtWrite *aWrite, - BtWriteIndex *pA, - BtWriteIndex *pB -){ - BtWrite *pWA = &aWrite[pA->iRoot]; - BtWrite *pWB = &aWrite[pB->iRoot]; - - if( pWA->iRootiRoot ) return -1; - if( pWA->iRoot>pWB->iRoot ) return +1; - - return btreeBcRecordCompare( - pUnpacked, pWA->pKeyInfo, pA->aRec, pA->nRec, 0, pB->aRec, pB->nRec, 0 - ); -} - -/* !defined(SQLITE_OMIT_CONCURRENT) -** -** Sort the aWriteIndex[] array, first in order of root page, then record. -** At this point the BtWriteIndex.iRoot values are actually indexes -** into the BtConcurrent.aWrite[] array. When sorting, this index is -** used to find the actual root page number and the required KeyInfo -** struct. -** -** After sorting, replace the BtWriteIndex.iRoot values with the actual -** value from the aWrite[] array. -** -** Return SQLITE_NOMEM if an OOM is encountered, or SQLITE_OK otherwise. -*/ -int btreeBcWriteIndexSort( - BtWriteIndex *aWriteIndex, - int nWriteIndex, - BtConcurrent *pBtConc -){ - int ii; - - BT_MERGESORT_BODY(BtWriteIndex, aWriteIndex, nWriteIndex, ( - btreeBcWriteIndexCmp(pBtConc->pUnpacked, pBtConc->aWrite, pA, pB)<=0 - )); - - /* Replace the aWriteIndex[ii].iRoot values with actual root page numbers */ - for(ii=0; iiaWrite[ aWriteIndex[ii].iRoot ].iRoot; - } - - return SQLITE_OK; -} - -/* -** Argument iRoot is the root page number of a b-tree within the database. -** Figure out which table or index this is using the schema managed by -** pBt. Return the name of the table. If pzIdx is not NULL and the root -** page belongs to an index, set (*pzIdx) to the name of the index. -** -** This is used to identify the specific table and index on which a -** conflict occurred in an sqlite3_log() message. -*/ -static const char *btreeBcRootToObject( - BtShared *pBt, /* Shared b-tree object */ - Pgno iRoot, /* Root page of table or index */ - const char **pzIdx /* OUT: Index name (or NULL) */ -){ - Schema *pSchema = pBt->pSchema; - const char *zRet = 0; - if( pSchema ){ - HashElem *pE = sqliteHashFirst(&pSchema->tblHash); - for( ; zRet==0 && pE; pE=sqliteHashNext(pE)){ - Table *pTab = (Table*)sqliteHashData(pE); - if( pTab->tnum==iRoot ){ - zRet = pTab->zName; - break; - }else{ - Index *pIdx; - for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ - if( pIdx->tnum==iRoot ){ - zRet = pTab->zName; - if( pzIdx ) *pzIdx = pIdx->zName; - } - } - } - } - } - - return zRet; -} - - -/* !defined(SQLITE_OMIT_CONCURRENT) -** -** Check if any of the reads accumulated in pBtConc conflict with the -** writes in the aWrite[] array. If so, return SQLITE_BUSY_SNAPSHOT. -** Otherwise, if no conflicts are found, return SQLITE_OK. -*/ -int btreeBcDetectIntkeyConflict( - BtShared *pBt, - BtWriteIntkey *aWrite, - int nWrite -){ - BtReadIntkey *aRead = pBt->conc.aReadIntkey; - int nRead = pBt->conc.nReadIntkey; - int iRead = 0; - int iWrite = 0; - - while( iRead iRootRead ){ - iRead++; - } - else{ - /* Same root page */ - i64 iKey = aWrite[iWrite].iKey; - - if( iKey < aRead[iRead].iMin ){ - iWrite++; - } - else if( iKey > aRead[iRead].iMax ){ - iRead++; - } - else{ - /* A conflict. Before returning, issue a log message. */ - const char *zTab = 0; - - zTab = btreeBcRootToObject(pBt, iRootRead, 0); - sqlite3_log(SQLITE_OK, - "cannot commit CONCURRENT transaction - conflict in table %s - " - "range (%lld,%lld) conflicts with write to rowid %lld", - zTab, aRead[iRead].iMin, aRead[iRead].iMax, iKey - ); - return SQLITE_BUSY_SNAPSHOT; - } - } - } - - return SQLITE_OK; /* No conflict */ -} - -/* !defined(SQLITE_OMIT_CONCURRENT) -** -** Test for conflicts between the index ranges accumulated in pBtConc -** and the write-keys in the aWriteIndex[] array. Return -** SQLITE_BUSY_SNAPSHOT if there is a conflict, or SQLITE_OK otherwise. -*/ -int btreeBcDetectIndexConflict( - BtShared *pBt, - BtWriteIndex *aWriteIndex, - int nWriteIndex -){ - BtConcurrent *pBtConc = &pBt->conc; - BtReadIndex *aReadIndex = pBtConc->aReadIndex; - int nReadIndex = pBtConc->nReadIndex; - int iRead = 0; - int iWrite = 0; - - while( iReadiRoot < pRead->iRoot ){ - iWrite++; - }else if( pWrite->iRoot > pRead->iRoot ){ - iRead++; - }else{ - /* Most index scans are not really scans, but exact lookups. In - ** this case pRead->aRecMin==pRead->aRecMax. */ - assert( (pRead->aRecMin!=pRead->aRecMax) || pRead->drc_min>=0 ); - - int cmp = btreeBcRecordCompare( - pBtConc->pUnpacked, - pRead->pKeyInfo, - pWrite->aRec, pWrite->nRec, 0, - pRead->aRecMin, pRead->nRecMin, - (pRead->drc_min>0 ? 0 : pRead->drc_min) - ); - - if( cmp < 0 ){ - /* Write key is less than aRecMin */ - iWrite++; - }else{ - /* Write key is greater than aRecMin */ - iRead++; - - /* If cmp==0, then this is definitely a conflict. Or, if cmp>0 and this - ** is a true range scan, not an exact lookup, compare the write-key to - ** BtReadIndex.aRedMax. If it is less than or equal, this is also a - ** conflict. */ - if( cmp>0 && (pRead->aRecMin!=pRead->aRecMax) ){ - cmp = btreeBcRecordCompare( - pBtConc->pUnpacked, - pRead->pKeyInfo, - pWrite->aRec, pWrite->nRec, 0, - pRead->aRecMax, pRead->nRecMax, pRead->drc_max - ); - } - - if( cmp<=0 ){ - /* Write key is between aRecMin and aRecMax - conflict! */ - const char *zIdx = 0; - const char *zTab = btreeBcRootToObject(pBt, pRead->iRoot, &zIdx); - sqlite3BcLogIndexConflict(zTab, zIdx, pWrite, pRead); - return SQLITE_BUSY_SNAPSHOT; - } - } - } - } - - return SQLITE_OK; -} - -/* !defined(SQLITE_OMIT_CONCURRENT) -*/ -static int btreeBcSharedLogEntry(BtShared *pBt, BtSharedLogEntry **ppOut){ - BtSharedLogEntry *pRet = 0; - int ii; - int nIntkey = 0; - int nIndex = 0; - i64 nByte = 0; - BtWrite *aWrite = pBt->conc.aWrite; - int rc = SQLITE_OK; - - /* Count the two different types of writes. */ - for(ii=0; iiconc.nWrite; ii++){ - if( aWrite[ii].pKeyInfo ) nIndex++; - } - nIntkey = pBt->conc.nWrite - nIndex; - - /* Allocate for the BtSharedLogEntry, and the two arrays. */ - nByte = sizeof(BtSharedLogEntry) - + nIntkey * sizeof(BtWriteIntkey) - + nIndex * sizeof(BtWriteIndex); - - pRet = (BtSharedLogEntry*)sqlite3MallocZero(nByte); - if( pRet==0 ){ - rc = SQLITE_NOMEM_BKPT; - }else{ - - pRet->nByte = nByte; - - /* Populate the aIntkey[] and aIndex[] arrays */ - pRet->aIntkey = (BtWriteIntkey*)&pRet[1]; - pRet->aIndex = (BtWriteIndex*)&pRet->aIntkey[nIntkey]; - for(ii=0; iiconc.nWrite; ii++){ - if( aWrite[ii].pKeyInfo ){ - BtWriteIndex *p = &pRet->aIndex[pRet->nIndex++]; - p->iRoot = ii; - p->nRec = aWrite[ii].nRec; - p->aRec = aWrite[ii].aRec; - aWrite[ii].aRec = 0; - pRet->nByte += (p->nRec + 8+9); - }else{ - BtWriteIntkey *p = &pRet->aIntkey[pRet->nIntkey++]; - p->iRoot = aWrite[ii].iRoot; - p->iKey = aWrite[ii].iKey; - } - } - - /* Sort the aIntkey[] array */ - rc = btreeBcWriteIntkeySort(pRet->aIntkey, pRet->nIntkey); - - /* Sort the aIndex[] array */ - if( rc==SQLITE_OK ){ - rc = btreeBcWriteIndexSort(pRet->aIndex, pRet->nIndex, &pBt->conc); - } - - if( rc!=SQLITE_OK ){ - btreeBcSharedLogEntryDelete(pRet); - pRet = 0; - } - } - - *ppOut = pRet; - return rc; -} - - -/* !defined(SQLITE_OMIT_CONCURRENT) -** -** A transaction has just been committed. If BtConcurrent info was collected -** for the transaction, add it to the BtSharedLog object. -*/ -static int btreeBcUpdateSharedLog( - BtShared *pBt, - u64 iBaseId, - u32 iFirstFrame, - u32 nFrame -){ - int rc = SQLITE_OK; - if( pBt->conc.eState==BTCONC_STATE_INUSE ){ - BtSharedLogEntry *pNew = 0; - BtSharedLog *pBtLog = pBt->conc.pBtLog; - - rc = btreeBcSharedLogEntry(pBt, &pNew); - if( rc==SQLITE_OK ){ - BtSharedLogEntry *pLast = 0; - - /* Set the two versions in the new log-entry object. */ - pNew->iBaseId = iBaseId; - pNew->iThisId = sqlite3PagerWalCommitId(pBt->pPager); - pNew->iFirstFrame = iFirstFrame; - pNew->nFrame = nFrame; - - /* Take the BtSharedLog mutex */ - sqlite3_mutex_enter(pBtLog->mutex); - - pLast = pBtLog->pLast; - if( pLast && pLast->iThisId!=iBaseId ){ - /* This new log entry does not immediately follow the previous - ** entry in the BtSharedLog object. This means that an external - ** process wrote to the db, or some connection in this process - ** wrote to the db but did not update the BtSharedLog for some - ** reason. Either way, discard the entire contents of the - ** BtSharedLog before adding pNew as the first and only entry. */ - while( pBtLog->pFirst ){ - btreeBcRemoveOldest(pBtLog); - } - } - - if( (iFirstFrame & 0x7FFFFFFF)==1 ){ - /* This was the first entry written into a wal file. *-wal if the - ** 0x80000000 bit of iFirst is clear, or *-wal2 if it is set. - ** Either way, remove all entries corresponding to that wal file - ** from the BtSharedLog. The initial snapshot belonging to each of - ** these entries is no longer available, so there can be no chance - ** of it being required. */ - const u32 m = 0x80000000; - u32 v = (iFirstFrame & m); - while( pBtLog->pFirst && (pBtLog->pFirst->iFirstFrame & m)==v ){ - btreeBcRemoveOldest(pBtLog); - } - } - - /* Link the new entry into the BtSharedLog object. */ - if( pBtLog->pLast ){ - pBtLog->pLast->pLogNext = pNew; - }else{ - pBtLog->pFirst = pNew; - } - pBtLog->pLast = pNew; - pBtLog->nByte += pNew->nByte; - - /* Free any old log-entries no longer required. TODO: Could do - ** this by querying wal.c to see what snapshots are still available - ** or in use. Maybe we can even lock the required BtSharedLogEntry - ** objects in memory when each concurrent transaction is opened. */ - while( pBtLog->nByte>sqlite3GlobalConfig.nSharedLogMaxSize ){ - btreeBcRemoveOldest(pBtLog); - } - - /* Release BtSharedLog mutex */ - sqlite3_mutex_leave(pBtLog->mutex); - } - } - - return rc; -} - -/* !defined(SQLITE_OMIT_CONCURRENT) -** -** Clear the state of pBt->conc. -*/ -static void btreeBcEndTransaction(sqlite3 *db, BtShared *pBt){ - if( pBt->conc.eState!=BTCONC_STATE_NONE ){ - - { - BtReadIndex *aRead = pBt->conc.aReadIndex; - int ii; - for(ii=0; iiconc.nReadIndex; ii++){ - BtReadIndex *p = &aRead[ii]; - if( p->aRecMin!=p->aRecMax ) sqlite3_free(p->aRecMin); - sqlite3_free(p->aRecMax); - sqlite3KeyInfoUnref(p->pKeyInfo); - } - sqlite3_free(aRead); - - sqlite3_free(pBt->conc.aReadIntkey); - - pBt->conc.aReadIndex = 0; - pBt->conc.nReadIndex = 0; - pBt->conc.nReadIndexAlloc = 0; - pBt->conc.aReadIntkey = 0; - pBt->conc.nReadIntkey = 0; - pBt->conc.nReadIntkeyAlloc = 0; - } - - { - btreeBcFreeWriteArray(pBt->conc.aWrite, pBt->conc.nWrite); - sqlite3_free(pBt->conc.aWrite); - pBt->conc.aWrite = 0; - pBt->conc.nWrite = 0; - pBt->conc.nWriteAlloc = 0; - } - - sqlite3DbFree(db, pBt->conc.pUnpacked); - pBt->conc.pUnpacked = 0; - pBt->conc.nUnpackedField = 0; - pBt->conc.eState = BTCONC_STATE_NONE; - } -} - -/* !defined(SQLITE_OMIT_CONCURRENT) -** -*/ -static void btreeBcDisconnect(BtShared *pBt){ - if( pBt->conc.pBtLog ){ - BtSharedLog *pFree = pBt->conc.pBtLog; - pBt->conc.pBtLog = 0; - - sqlite3_mutex_enter( sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MAIN) ); - pFree->nRef--; - if( pFree->nRef==0 ){ - BtSharedLog **pp = &pGlobalBtSharedLog; - while( *pp!=pFree ){ pp = &(*pp)->pSharedNext; } - *pp = (*pp)->pSharedNext; - }else{ - pFree = 0; - } - sqlite3_mutex_leave( sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MAIN) ); - - if( pFree ){ - while( pFree->pFirst ){ - btreeBcRemoveOldest(pFree); - } - sqlite3_mutex_free(pFree->mutex); - sqlite3_free(pFree); - } - } -} - -/* !defined(SQLITE_OMIT_CONCURRENT) -** -** Finish a scan. -*/ -static int btreeBcScanFinish(BtCursor *pCsr){ - int rc = SQLITE_OK; - BtConcurrent *pBtConc = &pCsr->pBt->conc; - if( pBtConc->eState==BTCONC_STATE_INUSE && pCsr->iScanIndex>0 ){ - - if( pCsr->pKeyInfo ){ - BtReadIndex *p = &pBtConc->aReadIndex[pCsr->iScanIndex-1]; - u8 *aRec = 0; - int nRec = 0; - - if( pCsr->eState==CURSOR_VALID || pCsr->eState==CURSOR_SKIPNEXT ){ - nRec = sqlite3BtreePayloadSize(pCsr); - aRec = sqlite3_malloc(nRec + 8+9); - if( !aRec ){ - rc = SQLITE_NOMEM_BKPT; - }else{ - rc = sqlite3BtreePayload(pCsr, 0, nRec, aRec); - } - }else if( pCsr->eState==CURSOR_REQUIRESEEK ){ - nRec = pCsr->nKey; - aRec = sqlite3_malloc(nRec + 8+9); - if( !aRec ){ - rc = SQLITE_NOMEM_BKPT; - }else{ - memcpy(aRec, pCsr->pKey, nRec); - } - } - if( rc!=SQLITE_OK ){ - sqlite3_free(aRec); - return rc; - } - - assert( pCsr->eScanType==BTCONC_READ_MOVETO||(!p->aRecMin&&!p->nRecMin)); - assert( p->aRecMax==0 && p->nRecMax==0 ); - - switch( pCsr->eScanType ){ - case BTCONC_READ_FIRST: { - p->aRecMax = aRec; - p->nRecMax = nRec; - break; - } - - case BTCONC_READ_LAST: { - p->aRecMin = aRec; - p->nRecMin = nRec; - break; - } - - default: { - assert( pCsr->eScanType==BTCONC_READ_MOVETO ); - /* The value used in the seek op is currently stored in p->aRecMin */ - if( pCsr->iScanDir==0 ){ - if( aRec==0 ){ - sqlite3_free(p->aRecMin); - p->aRecMin = 0; - p->nRecMin = 0; - p->drc_min = 0; - }else{ - pCsr->iScanDir = btreeBcRecordCompare( - pBtConc->pUnpacked, p->pKeyInfo, - aRec, nRec, 0, - p->aRecMin, p->nRecMin, p->drc_min - ); - } - } - - if( pCsr->iScanDir<0 ){ - p->aRecMax = p->aRecMin; - p->nRecMax = p->nRecMin; - p->drc_max = p->drc_min; - p->aRecMin = aRec; - p->nRecMin = nRec; - p->drc_min = 0; - }else{ - p->aRecMax = aRec; - p->nRecMax = nRec; - } - - break; - } - } - - if( p->aRecMin==0 ) p->drc_min = +1; - if( p->aRecMax==0 ) p->drc_max = -1; - - }else{ - BtReadIntkey *p = &pBtConc->aReadIntkey[pCsr->iScanIndex-1]; - - int bEof = (pCsr->eState==CURSOR_INVALID || pCsr->eState==CURSOR_FAULT); - i64 iKey = 0; - - if( bEof==0 ){ - if( pCsr->eState==CURSOR_VALID || pCsr->eState==CURSOR_SKIPNEXT ){ - iKey = sqlite3BtreeIntegerKey(pCsr); - }else{ - assert( pCsr->eState==CURSOR_REQUIRESEEK ); - iKey = pCsr->nKey; - } - } - - switch( pCsr->eScanType ){ - case BTCONC_READ_FIRST: { - p->iMin = SMALLEST_INT64; - p->iMax = bEof ? LARGEST_INT64 : iKey; - break; - } - - case BTCONC_READ_LAST: { - p->iMax = LARGEST_INT64; - if( bEof ){ - p->iMin = SMALLEST_INT64; - }else{ - p->iMin = iKey; - } - break; - } - - default: { - assert( pCsr->eScanType==BTCONC_READ_MOVETO ); - /* The value used in the seek op is currently stored in p->iMin */ - if( bEof==0 ){ - p->iMax = iKey; - }else{ - if( pCsr->iScanDir==0 ){ - p->iMin = SMALLEST_INT64; - p->iMax = LARGEST_INT64; - }else if( pCsr->iScanDir>0 ){ - p->iMax = LARGEST_INT64; - }else{ - p->iMax = p->iMin; - p->iMin = SMALLEST_INT64; - } - } - if( p->iMin>p->iMax ){ - SWAP(i64, p->iMin, p->iMax); - } - break; - } - } - - if( p->iMin==SMALLEST_INT64 && p->iMax==LARGEST_INT64 ){ - /* Full table scan. Issue a log message to record this. */ - const char *zTab = btreeBcRootToObject(pCsr->pBt, p->iRoot, 0); - sqlite3_log(SQLITE_OK, - "BEGIN CONCURRENT: logging full scan on table %s " - "(root=%d) (sql=\"%s\")", - zTab, p->iRoot, sqlite3BcCurrentSql(pCsr->pBtree->db) - ); - } - - } - pCsr->iScanIndex = 0; - pCsr->iScanDir = 0; - } - return rc; -} - -/* !defined(SQLITE_OMIT_CONCURRENT) -** -** Allocate and return a BtReadIntkey object. -*/ -static BtReadIntkey *btreeBcIntkeyRead( - BtConcurrent *pBtConc, - int *piScanIndex, - int *pRc -){ - int rc = btreeBcGrowArray( - (void**)&pBtConc->aReadIntkey, - &pBtConc->nReadIntkey, - &pBtConc->nReadIntkeyAlloc, - sizeof(BtReadIntkey) - ); - if( rc==SQLITE_OK ){ - pBtConc->nReadIntkey++; - *piScanIndex = pBtConc->nReadIntkey; - return &pBtConc->aReadIntkey[pBtConc->nReadIntkey-1]; - } - *pRc = rc; - return 0; -} - -/* !defined(SQLITE_OMIT_CONCURRENT) -** -** Allocate and return a BtReadIndex object. -*/ -static BtReadIndex *btreeBcIndexRead( - BtConcurrent *pBtConc, - int *piScanIndex, - int *pRc -){ - int rc = btreeBcGrowArray( - (void**)&pBtConc->aReadIndex, - &pBtConc->nReadIndex, - &pBtConc->nReadIndexAlloc, - sizeof(BtReadIndex) - ); - if( rc==SQLITE_OK ){ - pBtConc->nReadIndex++; - *piScanIndex = pBtConc->nReadIndex; - return &pBtConc->aReadIndex[pBtConc->nReadIndex-1]; - } - *pRc = rc; - return 0; -} - -/* !defined(SQLITE_OMIT_CONCURRENT) -** -** Ensure the UnpackedRecord structure at BtConcurrent.pUnpacked is -** large enough to unpack keys that are compared using pKeyInfo. -** Return SQLITE_NOMEM if an OOM error is encountered, or SQLITE_OK -** otherwise. -*/ -static int btreeBcUpdateUnpacked(BtConcurrent *pBtConc, KeyInfo *pKeyInfo){ - if( pKeyInfo->nKeyField>pBtConc->nUnpackedField ){ - sqlite3 *db = pKeyInfo->db; - sqlite3DbFree(db, pBtConc->pUnpacked ); - pBtConc->pUnpacked = sqlite3VdbeAllocUnpackedRecord(pKeyInfo); - if( pBtConc->pUnpacked ){ - pBtConc->nUnpackedField = pKeyInfo->nKeyField; - pBtConc->pUnpacked->default_rc = 0; - }else{ - pBtConc->nUnpackedField = 0; - return SQLITE_NOMEM; - } - } - return SQLITE_OK; -} - -/* !defined(SQLITE_OMIT_CONCURRENT) -** -*/ -static void btreeBcScanIsExact(BtCursor *pCsr){ - BtReadIndex *p = &pCsr->pBt->conc.aReadIndex[pCsr->iScanIndex-1]; - p->nRecMax = p->nRecMin; - p->aRecMax = p->aRecMin; - if( p->drc_min<0 ){ - p->drc_min = p->drc_min * -1; - } - p->drc_max = p->drc_min * -1; - pCsr->iScanIndex = 0; -} - -/* !defined(SQLITE_OMIT_CONCURRENT) -** -** Start a scan. -*/ -static int btreeBcScanStart( - BtCursor *pCsr, /* Cursor to start scan on */ - int eRead, /* BTCONC_READ_XXX value */ - i64 iKey, UnpackedRecord *pKey /* Key value for BTCONC_READ_MOVETO */ -){ - int rc = SQLITE_OK; - BtConcurrent *pBtConc = &pCsr->pBt->conc; - - rc = btreeBcScanFinish(pCsr); - if( rc==SQLITE_OK && pBtConc->eState==BTCONC_STATE_INUSE ){ - pCsr->iScanDir = 0; - pCsr->eScanType = eRead; - if( pCsr->pKeyInfo ){ - - /* Reserve a BtReadIndex structure from the array */ - BtReadIndex *pRead = btreeBcIndexRead(pBtConc, &pCsr->iScanIndex, &rc); - if( pRead==0 ) return rc; - memset(pRead, 0, sizeof(*pRead)); - - pRead->iRoot = pCsr->pgnoRoot; - pRead->pKeyInfo = sqlite3KeyInfoRef(pCsr->pKeyInfo); - rc = btreeBcUpdateUnpacked(pBtConc, pRead->pKeyInfo); - if( rc==SQLITE_OK && pKey ){ - int drc = 0; - assert( eRead==BTCONC_READ_MOVETO ); - rc = sqlite3BcSerializeRecord(pKey, &pRead->aRecMin, &pRead->nRecMin); - assert( pKey->default_rc>=-1 && pKey->default_rc<=+1 ); - assert( pCsr->pKeyInfo->nAllField>=pKey->nField ); - drc = pKey->default_rc * (pCsr->pKeyInfo->nAllField+1 - pKey->nField); - pRead->drc_min = (i16)drc; - - /* If the BTREE_SEEK_EQ flag is set, then the caller is only - ** interested in index entries that exactly match the supplied prefix. - ** In this case we can set the first and last keys of the scan - ** directly instead of tracking where the cursor finishes up. - */ - if( (pCsr->hints & BTREE_SEEK_EQ) && rc==SQLITE_OK ){ - btreeBcScanIsExact(pCsr); - } - } - - if( eRead==BTCONC_READ_COUNT ){ - pRead->drc_min = +1; - pRead->drc_max = -1; - } - - }else{ - BtReadIntkey *pRead = btreeBcIntkeyRead(pBtConc, &pCsr->iScanIndex, &rc); - if( pRead==0 ) return rc; - pRead->iRoot = pCsr->pgnoRoot; - - pRead->iMin = iKey; - pRead->iMax = 0; - if( eRead==BTCONC_READ_COUNT ){ - pRead->iMax = LARGEST_INT64; - pRead->iMin = SMALLEST_INT64; - } - } - - if( eRead==BTCONC_READ_COUNT ){ - pCsr->iScanIndex = 0; - pCsr->iScanDir = 0; - } - } - - return rc; -} - -/* !defined(SQLITE_OMIT_CONCURRENT) -** -** Append an insert to the BtConcurrent object, if it is in use. Return -** SQLITE_OK if successful, or SQLITE_NOMEM if an OOM occurs. -*/ -static int btreeBcInsert( - BtCursor *pCsr, /* Cursor to write to */ - const BtreePayload *pPay /* Payload to write */ -){ - BtConcurrent *pBtConc = &pCsr->pBt->conc; - int rc = SQLITE_OK; - - if( SQLITE_OK==(rc = btreeBcScanFinish(pCsr)) - && pBtConc->eState==BTCONC_STATE_INUSE - && SQLITE_OK==(rc = btreeBcGrowWriteArray(pBtConc)) - ){ - int nByte = (pPay->pKey? pPay->nKey : (pPay->nData + pPay->nZero)); - BtWrite *p = &pBtConc->aWrite[pBtConc->nWrite]; - memset(p, 0, sizeof(BtWrite)); - pBtConc->nWrite++; - p->iRoot = pCsr->pgnoRoot; - p->aRec = sqlite3_malloc(nByte); - if( p->aRec==0 ){ - rc = SQLITE_NOMEM_BKPT; - }else if( pCsr->pKeyInfo ){ - p->pKeyInfo = sqlite3KeyInfoRef(pCsr->pKeyInfo); - p->nRec = pPay->nKey; - memcpy(p->aRec, pPay->pKey, p->nRec); - rc = btreeBcUpdateUnpacked(pBtConc, p->pKeyInfo); - }else{ - p->iKey = pPay->nKey; - p->nRec = nByte; - memcpy(p->aRec, pPay->pData, pPay->nData); - if( pPay->nZero ){ - memset(&p->aRec[pPay->nData], 0, pPay->nZero); - } - } - } - - return rc; -} - -/* !defined(SQLITE_OMIT_CONCURRENT) -** -** The entry under pCur is to be deleted. If this is a BEGIN CONCURRENT -** transaction, add an entry for the delete to the BtConcurrent object. -** Return SQLITE_OK if successful, or an SQLite error code (SQLITE_NOMEM) -** if something goes wrong. -*/ -static int btreeBcDelete(BtCursor *pCsr){ - BtConcurrent *pBtConc = &pCsr->pBt->conc; - int rc; - - if( SQLITE_OK==(rc = btreeBcScanFinish(pCsr)) - && pBtConc->eState==BTCONC_STATE_INUSE - && SQLITE_OK==(rc = btreeBcGrowWriteArray(pBtConc)) - ){ - BtWrite *p = &pBtConc->aWrite[pBtConc->nWrite]; - - pBtConc->nWrite++; - memset(p, 0, sizeof(BtWrite)); - p->bDel = 1; - p->iRoot = pCsr->pgnoRoot; - if( pCsr->pKeyInfo ){ - p->pKeyInfo = sqlite3KeyInfoRef(pCsr->pKeyInfo); - p->nRec = sqlite3BtreePayloadSize(pCsr); - p->aRec = sqlite3_malloc(p->nRec + 8+9); - if( p->aRec==0 ){ - rc = SQLITE_NOMEM_BKPT; - }else{ - rc = sqlite3BtreePayload(pCsr, 0, p->nRec, p->aRec); - } - if( rc==SQLITE_OK ){ - rc = btreeBcUpdateUnpacked(pBtConc, p->pKeyInfo); - } - }else{ - p->iKey = sqlite3BtreeIntegerKey(pCsr); - } - } - - return rc; -} - -/* !defined(SQLITE_OMIT_CONCURRENT) -** -** Close all cursors open on the b-tree object. It is the responsibility -** of the caller to ensure none of the cursors will be used after -** this call. -*/ -static void btreeCloseAllCursors(BtShared *pBt){ - while( pBt->pCursor ){ - BtCursor *pCsr = pBt->pCursor; - sqlite3BtreeCloseCursor(pCsr); - sqlite3_free(pCsr); - } -} - -/* -** Return the size of a BtCursor object in bytes. -** -** This interfaces is needed so that users of cursors can preallocate -** sufficient storage to hold a cursor. The BtCursor object is opaque -** to users so they cannot do the sizeof() themselves - they must call -** this routine. -*/ -SQLITE_PRIVATE int sqlite3BtreeCursorSize(void){ - return ROUND8(sizeof(BtCursor)); -} - -/* !defined(SQLITE_OMIT_CONCURRENT) -** -** Allocate memory for and open a cursor. -*/ -static int btreeCursorOpen( - Btree *p, - Pgno iRoot, - int flags, - KeyInfo *pKeyInfo, - BtCursor **ppCsr -){ - BtCursor *pCsr = 0; - int rc = SQLITE_OK; - - pCsr = (BtCursor*)sqlite3MallocZero(sqlite3BtreeCursorSize()); - if( pCsr==0 ){ - rc = SQLITE_NOMEM_BKPT; - }else{ - rc = sqlite3BtreeCursor(p, iRoot, flags, pKeyInfo, pCsr); - if( rc!=SQLITE_OK ){ - sqlite3_free(pCsr); - pCsr = 0; - } - } - *ppCsr = pCsr; - return rc; -} - -/* Used by btreeBcTryLogicalCommit() */ -static int btreeMoveto(BtCursor*, const void*, i64, int, int*); - -/* -** Sort the pBtConc->aReadIntkey[] and aReadIndex[] arrays. -*/ -SQLITE_PRIVATE int sqlite3BtreeSortReadArrays(BtConcurrent *pBtConc){ - int rc = btreeBcReadIntkeySort(pBtConc->aReadIntkey, &pBtConc->nReadIntkey); - if( rc==SQLITE_OK ){ - rc = btreeBcReadIndexSort(pBtConc); - } - return rc; -} - -/* !defined(SQLITE_OMIT_CONCURRENT) -** -** Attempt to validate and write the transaction in BtShared.conc to the -** page cache. Return SQLITE_OK if successful. SQLITE_BUSY_SNAPSHOT if -** the transaction cannot be written because validation failed, or an -** SQLite error code if some other error occurred. -*/ -static int btreeBcTryLogicalCommit(Btree *p, int *pbLog){ - int rc = SQLITE_OK; - BtShared *pBt = p->pBt; - - BtConcurrent *pBtConc = &pBt->conc; - assert( pBtConc->eState==BTCONC_STATE_INUSE ); - pBtConc->eState = BTCONC_STATE_RETIRED; - - /* Sort the read arrays */ - rc = sqlite3BtreeSortReadArrays(pBtConc); - - if( rc==SQLITE_OK ){ - BtSharedLog *pBtLog = pBt->conc.pBtLog; - BtSharedLogEntry *pEntry; - u64 iLiveId = sqlite3PagerWalLiveId(pBt->pPager); - - sqlite3_mutex_enter(pBtLog->mutex); - - /* Skip past log entries corresponding to transactions that were - ** committed before the snapshot on which this transaction is based - ** was created. */ - for(pEntry=pBtLog->pFirst; pEntry; pEntry=pEntry->pLogNext){ - if( pEntry->iBaseId==pBt->conc.iBase ) break; - } - - if( pEntry==0 || pBtLog->pLast->iThisId!=iLiveId ){ - /* The shared-log does not contain all required entries. Either it - ** does not have entries back as far as the snapshot that this - ** snapshot was prepared against (pEntry==0), or the last entry - ** is older than the current live head of the wal file. Either - ** way, logical validation cannot run. */ - rc = SQLITE_BUSY_SNAPSHOT; - }else{ - /* We know the frame that page-level validation failed at. Since - ** page-level validation scans transactions in the order committed, - ** skip over BtSharedLogEntry structures until we come to the - ** one that wrote the conflicting frame. Everything before this - ** in the list is guaranteed not to conflict. */ - u32 iConf = p->db->aCommit[SQLITE_COMMIT_CONFLICT_FRAME]; - while( iConfiFirstFrame - || iConf>=(pEntry->iFirstFrame+pEntry->nFrame) - ){ - pEntry = pEntry->pLogNext; - assert( pEntry ); - } - *pbLog = 1; - } - - /* Loop through all remaining shared-log entries checking for - ** conflicts. If none are found, then the transaction may be - ** committed. This block sets rc to SQLITE_BUSY_SNAPSHOT if conflicts - ** are found. */ - for(; pEntry && rc==SQLITE_OK; pEntry=pEntry->pLogNext){ - rc = btreeBcDetectIntkeyConflict(pBt, pEntry->aIntkey, pEntry->nIntkey); - if( rc==SQLITE_OK ){ - rc = btreeBcDetectIndexConflict(pBt, pEntry->aIndex, pEntry->nIndex); - } - } - sqlite3_mutex_leave(pBtLog->mutex); - } - - if( rc==SQLITE_OK ){ - /* Update the snapshot to the head of the wal file. Drop the contents - ** of the page-cache at the same time. Then ensure that the database - ** size is set correctly at both the btree and pager level. */ - btreePtrmapDelete(pBt); - rc = sqlite3PagerUpgradeSnapshot(pBt->pPager, pBt->pPage1->pDbPage, 1); - if( rc==SQLITE_OK ){ - const u8 *aPg1 = (const u8*)pBt->pPage1->pDbPage->pData; - u32 dbSize = get4byte(&aPg1[28]); - sqlite3PagerSetDbsize(pBt->pPager, dbSize); - pBt->nPage = dbSize; - } - } - - /* If everything still looks ok, proceed with the commit. */ - if( rc==SQLITE_OK ){ - int ii; - - for(ii=0; iinWrite; ii++){ - BtWrite *pWrite = &pBtConc->aWrite[ii]; - BtCursor *pCsr = 0; - for(pCsr=pBt->pCursor; pCsr; pCsr=pCsr->pNext){ - if( pCsr->pgnoRoot==pWrite->iRoot ) break; - } - - if( pCsr==0 ){ - rc = btreeCursorOpen(p, pWrite->iRoot, - BTREE_WRCSR, pWrite->pKeyInfo, &pCsr - ); - } - - if( pCsr ){ - if( pWrite->bDel ){ - int res = 0; - if( pWrite->pKeyInfo ){ - rc = btreeMoveto(pCsr, pWrite->aRec, pWrite->nRec, 0, &res); - }else{ - rc = btreeMoveto(pCsr, 0, pWrite->iKey, 0, &res); - } - if( rc==SQLITE_OK && res==0 ){ - sqlite3BtreeDelete(pCsr, 0); - } - }else{ - BtreePayload pay; - memset(&pay, 0, sizeof(pay)); - if( pWrite->pKeyInfo ){ - UnpackedRecord *pUnpacked = pBtConc->pUnpacked; - pay.pKey = (const void*)pWrite->aRec; - pay.nKey = pWrite->nRec; - - /* For most indexes, this would not be necessary - - ** sqlite3BtreeInsert() would decode pay.pKey/pay.nKey - ** automatically. But that case doesn't work if this is - ** an UPDATE of a WITHOUT ROWID PRIMARY KEY index. In that - ** case sqlite3BtreeInsert() needs the unpacked record - - ** otherwise it inserts a second record instead of correctly - ** clobbering the first. */ - assert( pUnpacked->default_rc==0 ); - pUnpacked->pKeyInfo = pWrite->pKeyInfo; - pUnpacked->nField = pWrite->pKeyInfo->nKeyField + 1; - sqlite3VdbeRecordUnpack(pay.nKey, pay.pKey, pUnpacked); - pay.aMem = pUnpacked->aMem; - pay.nMem = pWrite->pKeyInfo->nKeyField; - - }else{ - pay.nKey = pWrite->iKey; - pay.pData = (const void*)pWrite->aRec; - pay.nData = pWrite->nRec; - } - rc = sqlite3BtreeInsert(pCsr, &pay, 0, 0); - } - } - } - - btreeCloseAllCursors(pBt); - } - - pBtConc->eState = BTCONC_STATE_INUSE; - return rc; -} - - #else /* SQLITE_OMIT_CONCURRENT */ # define btreePtrmapAllocate(x) SQLITE_OK # define btreePtrmapDelete(x) # define btreePtrmapBegin(x,y) SQLITE_OK # define btreePtrmapEnd(x,y,z) # define btreePtrmapCheck(y,z) - -# define btreeBcEndTransaction(db, pBt) -# define btreeBcDisconnect(pBt) -# define btreeBcScanFinish(pCsr) SQLITE_OK -# define btreeBcScanStart(pCsr,eRead,pKey,iKey) SQLITE_OK #endif /* SQLITE_OMIT_CONCURRENT */ static void releasePage(MemPage *pPage); /* Forward reference */ @@ -80568,7 +78708,6 @@ SQLITE_PRIVATE int sqlite3BtreeClose(Btree *p){ ** Clean out and delete the BtShared object. */ assert( !pBt->pCursor ); - btreeBcDisconnect(pBt); sqlite3PagerClose(pBt->pPager, p->db); if( pBt->xFreeSchema && pBt->pSchema ){ pBt->xFreeSchema(pBt->pSchema); @@ -81401,10 +79540,7 @@ static SQLITE_NOINLINE int btreeBeginTrans( trans_begun: #ifndef SQLITE_OMIT_CONCURRENT if( bConcurrent && rc==SQLITE_OK && sqlite3PagerIsWal(pBt->pPager) ){ - rc = btreeBcBeginConcurrent(pBt); - if( rc==SQLITE_OK ){ - rc = sqlite3PagerBeginConcurrent(pBt->pPager); - } + rc = sqlite3PagerBeginConcurrent(pBt->pPager); if( rc==SQLITE_OK && wrflag ){ rc = btreePtrmapAllocate(pBt); } @@ -81986,7 +80122,6 @@ static int btreeRelocateRange( ** (i.e. pgno>=iFirst), then discard it and allocate another. */ do { rc = allocateBtreePage(pBt, &pFree, &iNew, 0, 0); - if( rc!=SQLITE_OK ) break; if( iNew>=iFirst ){ assert( sqlite3PagerPageRefcount(pFree->pDbPage)==1 ); assert( iNew>iPg ); @@ -82031,7 +80166,7 @@ static int btreeFixUnlocked(Btree *p){ u32 nFree = get4byte(&p1[36]); assert( pBt->pMap ); - rc = sqlite3PagerUpgradeSnapshot(pPager, pPage1->pDbPage, 0); + rc = sqlite3PagerUpgradeSnapshot(pPager, pPage1->pDbPage); assert( p1==pPage1->aData ); if( rc==SQLITE_OK ){ @@ -82136,12 +80271,10 @@ static int btreeFixUnlocked(Btree *p){ SQLITE_PRIVATE int sqlite3BtreeCommitPhaseOne(Btree *p, const char *zSuperJrnl){ int rc = SQLITE_OK; if( p->inTrans==TRANS_WRITE ){ - u64 iBaseId = 0; BtShared *pBt = p->pBt; sqlite3BtreeEnter(p); #ifndef SQLITE_OMIT_CONCURRENT - iBaseId = sqlite3PagerWalLiveId(pBt->pPager); memset(p->aCommit, 0, sizeof(p->aCommit)); #endif #ifndef SQLITE_OMIT_AUTOVACUUM @@ -82174,11 +80307,6 @@ SQLITE_PRIVATE int sqlite3BtreeCommitPhaseOne(Btree *p, const char *zSuperJrnl){ p->aCommit[SQLITE_COMMIT_FIRSTFRAME] = iPrev+1; p->aCommit[SQLITE_COMMIT_NFRAME] = iCurrent-iPrev; - - rc = btreeBcUpdateSharedLog(pBt, iBaseId, - p->aCommit[SQLITE_COMMIT_FIRSTFRAME], - p->aCommit[SQLITE_COMMIT_NFRAME] - ); } #endif sqlite3BtreeLeave(p); @@ -82227,7 +80355,6 @@ static void btreeEndTransaction(Btree *p){ ** Also call PagerEndConcurrent() to ensure that the pager has discarded ** the record of all pages read within the transaction. */ btreePtrmapDelete(pBt); - btreeBcEndTransaction(db, pBt); sqlite3PagerEndConcurrent(pBt->pPager); btreeIntegrity(p); } @@ -82643,6 +80770,18 @@ SQLITE_PRIVATE int sqlite3BtreeCursor( } } +/* +** Return the size of a BtCursor object in bytes. +** +** This interfaces is needed so that users of cursors can preallocate +** sufficient storage to hold a cursor. The BtCursor object is opaque +** to users so they cannot do the sizeof() themselves - they must call +** this routine. +*/ +SQLITE_PRIVATE int sqlite3BtreeCursorSize(void){ + return ROUND8(sizeof(BtCursor)); +} + #ifdef SQLITE_DEBUG /* ** Return true if and only if the Btree object will be automatically @@ -82684,17 +80823,6 @@ SQLITE_PRIVATE int sqlite3BtreeCloseCursor(BtCursor *pCur){ BtShared *pBt = pCur->pBt; sqlite3BtreeEnter(pBtree); assert( pBt->pCursor!=0 ); - -#ifndef SQLITE_OMIT_CONCURRENT - if( SQLITE_OK!=btreeBcScanFinish(pCur) ){ - /* Allocation failed in btreeBcScanFinish(), but we have no way - ** to return the error to the user. So just disable the BtConcurrent - ** object. */ - assert( pBt->conc.eState==BTCONC_STATE_INUSE ); - pBt->conc.eState = BTCONC_STATE_RETIRED; - } -#endif - if( pBt->pCursor==pCur ){ pBt->pCursor = pCur->pNext; }else{ @@ -83552,10 +81680,6 @@ SQLITE_PRIVATE int sqlite3BtreeFirst(BtCursor *pCur, int *pRes){ assert( cursorOwnsBtShared(pCur) ); assert( sqlite3_mutex_held(pCur->pBtree->db->mutex) ); - - rc = btreeBcScanStart(pCur, BTCONC_READ_FIRST, 0, 0); - if( rc!=SQLITE_OK ) return rc; - rc = moveToRoot(pCur); if( rc==SQLITE_OK ){ assert( pCur->pPage->nCell>0 ); @@ -83636,13 +81760,6 @@ SQLITE_PRIVATE int sqlite3BtreeLast(BtCursor *pCur, int *pRes){ assert( cursorOwnsBtShared(pCur) ); assert( sqlite3_mutex_held(pCur->pBtree->db->mutex) ); -#ifndef SQLITE_OMIT_CONCURRENT - { - int rc = btreeBcScanStart(pCur, BTCONC_READ_LAST, 0, 0); - if( rc!=SQLITE_OK ) return rc; - } -#endif - /* If the cursor already points to the last entry, this is a no-op. */ if( CURSOR_VALID==pCur->eState && (pCur->curFlags & BTCF_AtLast)!=0 ){ assert( cursorIsAtLastEntry(pCur) || CORRUPT_DB ); @@ -83689,9 +81806,6 @@ SQLITE_PRIVATE int sqlite3BtreeTableMoveto( assert( pCur->pKeyInfo==0 ); assert( pCur->eState!=CURSOR_VALID || pCur->curIntKey!=0 ); - rc = btreeBcScanStart(pCur, BTCONC_READ_MOVETO, intKey, 0); - if( rc!=SQLITE_OK ) return rc; - /* If the cursor is already positioned at the point we are trying ** to move to, then just return without doing any work */ if( pCur->eState==CURSOR_VALID && (pCur->curFlags & BTCF_ValidNKey)!=0 ){ @@ -83821,33 +81935,6 @@ SQLITE_PRIVATE int sqlite3BtreeTableMoveto( return rc; } -#ifndef SQLITE_OMIT_CONCURRENT -/* !SQLITE_OMIT_CONCURRENT -** -** This is called on a cursor open on an intkey btree immediately after -** an unsuccessful seek. It tells the BC layer that the cursor was only -** used to search for the specific rowid, not the one it landed on. And -** so the OCC range lock is only required on the sought rowid, not on -** the range between the sought rowid and whereever it landed. -** -** e.g. if the table contains rowids 1, 3 and 5, and the user queries -** for rowid 4, the cursor will land on rowid 3. But we want the OCC -** range lock on 4-4, not 3-4. That's what this call is for. -*/ -SQLITE_PRIVATE void sqlite3BtreeCursorNoScan(BtCursor *pCsr){ - if( pCsr->iScanIndex>0 ){ - if( pCsr->pKeyInfo ){ - btreeBcScanIsExact(pCsr); - }else{ - BtReadIntkey *p = &pCsr->pBt->conc.aReadIntkey[pCsr->iScanIndex-1]; - p->iMax = p->iMin; - pCsr->iScanIndex = 0; - } - } -} -#endif - - /* ** Compare the "idx"-th cell on the page pPage against the key ** pointing to by pIdxKey using xRecordCompare. Return negative or @@ -83949,9 +82036,6 @@ SQLITE_PRIVATE int sqlite3BtreeIndexMoveto( assert( pRes ); assert( pCur->pKeyInfo!=0 ); - rc = btreeBcScanStart(pCur, BTCONC_READ_MOVETO, 0, pIdxKey); - if( rc!=SQLITE_OK ) return rc; - #ifdef SQLITE_DEBUG pCur->pBtree->nSeek++; /* Performance measurement during testing */ #endif @@ -84290,9 +82374,6 @@ SQLITE_PRIVATE int sqlite3BtreeNext(BtCursor *pCur, int flags){ UNUSED_PARAMETER( flags ); /* Used in COMDB2 but not native SQLite */ assert( cursorOwnsBtShared(pCur) ); assert( flags==0 || flags==1 ); -#ifndef SQLITE_OMIT_CONCURRENT - pCur->iScanDir = +1; -#endif pCur->info.nSize = 0; pCur->curFlags &= ~(BTCF_ValidNKey|BTCF_ValidOvfl); if( pCur->eState!=CURSOR_VALID ) return btreeNext(pCur); @@ -84384,9 +82465,6 @@ SQLITE_PRIVATE int sqlite3BtreePrevious(BtCursor *pCur, int flags){ assert( cursorOwnsBtShared(pCur) ); assert( flags==0 || flags==1 ); UNUSED_PARAMETER( flags ); /* Used in COMDB2 but not native SQLite */ -#ifndef SQLITE_OMIT_CONCURRENT - pCur->iScanDir = -1; -#endif pCur->curFlags &= ~(BTCF_AtLast|BTCF_ValidOvfl|BTCF_ValidNKey); pCur->info.nSize = 0; if( pCur->eState!=CURSOR_VALID @@ -87297,22 +85375,6 @@ static int btreeOverwriteCell(BtCursor *pCur, const BtreePayload *pX){ } } -#ifndef SQLITE_OMIT_CONCURRENT -# define BTCONC_DISABLE(pBtConc) \ - int eConcStateSave = pBtConc->eState; \ - pBtConc->eState = BTCONC_STATE_RETIRED - -# define BTCONC_RESTORE(pBtConc) pBtConc->eState = eConcStateSave - -# define SAVE_BTCONC \ - int eConcStateSave = pCur->pBt->conc.eState; \ - pCur->pBt->conc.eState = BTCONC_STATE_RETIRED - -# define RESTORE_BTCONC pCur->pBt->conc.eState = eConcStateSave -#else -# define SAVE_BTCONC -# define RESTORE_BTCONC -#endif /* ** Insert a new record into the BTree. The content of the new record @@ -87362,11 +85424,6 @@ SQLITE_PRIVATE int sqlite3BtreeInsert( assert( (flags & (BTREE_SAVEPOSITION|BTREE_APPEND|BTREE_PREFORMAT))==flags ); assert( (flags & BTREE_PREFORMAT)==0 || seekResult || pCur->pKeyInfo==0 ); -#ifndef SQLITE_OMIT_CONCURRENT - rc = btreeBcInsert(pCur, pX); - if( rc!=SQLITE_OK ) return rc; -#endif - /* Save the positions of any other cursors open on this table. ** ** In some cases, the call to btreeMoveto() below is a no-op. For @@ -87415,8 +85472,6 @@ SQLITE_PRIVATE int sqlite3BtreeInsert( assert( (flags & BTREE_PREFORMAT) || (pX->pKey==0)==(pCur->pKeyInfo==0) ); if( pCur->pKeyInfo==0 ){ - SAVE_BTCONC; - assert( pX->pKey==0 ); /* If this is an insert into a table b-tree, invalidate any incrblob ** cursors open on the row being replaced */ @@ -87447,7 +85502,6 @@ SQLITE_PRIVATE int sqlite3BtreeInsert( && pCur->info.nPayload==(u32)pX->nData+pX->nZero ){ /* New entry is the same size as the old. Do an overwrite */ - RESTORE_BTCONC; return btreeOverwriteCell(pCur, pX); } assert( loc==0 ); @@ -87458,12 +85512,9 @@ SQLITE_PRIVATE int sqlite3BtreeInsert( */ rc = sqlite3BtreeTableMoveto(pCur, pX->nKey, (flags & BTREE_APPEND)!=0, &loc); - RESTORE_BTCONC; if( rc ) return rc; } - RESTORE_BTCONC; }else{ - SAVE_BTCONC; /* This is an index or a WITHOUT ROWID table */ /* If BTREE_SAVEPOSITION is set, the cursor must already be pointing @@ -87489,10 +85540,8 @@ SQLITE_PRIVATE int sqlite3BtreeInsert( rc = btreeMoveto(pCur, pX->pKey, pX->nKey, (flags & BTREE_APPEND)!=0, &loc); } - RESTORE_BTCONC; if( rc ) return rc; } - RESTORE_BTCONC; /* If the cursor is currently pointing to an entry to be overwritten ** and the new content is the same as as the old, then use the @@ -87822,11 +85871,6 @@ SQLITE_PRIVATE int sqlite3BtreeDelete(BtCursor *pCur, u8 flags){ } assert( pCur->eState==CURSOR_VALID ); -#ifndef SQLITE_OMIT_CONCURRENT - rc = btreeBcDelete(pCur); - if( rc!=SQLITE_OK ) return rc; -#endif - iCellDepth = pCur->iPage; iCellIdx = pCur->ix; pPage = pCur->pPage; @@ -88469,9 +86513,6 @@ SQLITE_PRIVATE int sqlite3BtreeCount(sqlite3 *db, BtCursor *pCur, i64 *pnEntry){ i64 nEntry = 0; /* Value to return in *pnEntry */ int rc; /* Return code */ - rc = btreeBcScanStart(pCur, BTCONC_READ_COUNT, 0, 0); - if( rc!=SQLITE_OK ) return rc; - rc = moveToRoot(pCur); if( rc==SQLITE_EMPTY ){ *pnEntry = 0; @@ -89460,11 +87501,6 @@ SQLITE_PRIVATE int sqlite3BtreePutData(BtCursor *pCsr, u32 offset, u32 amt, void ** Mark this cursor as an incremental blob cursor. */ SQLITE_PRIVATE void sqlite3BtreeIncrblobCursor(BtCursor *pCur){ -#ifndef SQLITE_OMIT_CONCURRENT - if( pCur->curFlags & BTCF_WriteFlag ){ - pCur->pBt->conc.eState = BTCONC_STATE_RETIRED; - } -#endif pCur->curFlags |= BTCF_Incrblob; pCur->pBtree->hasIncrblobCur = 1; } @@ -89546,34 +87582,19 @@ SQLITE_PRIVATE int sqlite3HeaderSizeBtree(void){ return ROUND8(sizeof(MemPage)); SQLITE_PRIVATE int sqlite3BtreeExclusiveLock(Btree *p){ sqlite3 *db = p->db; int rc; - int bLog = 0; Pgno pgno = 0; BtShared *pBt = p->pBt; assert( p->inTrans==TRANS_WRITE && pBt->pPage1 ); - sqlite3BtreeEnter(p); - -#ifdef SQLITE_OMIT_CONCURRENT - rc = sqlite3PagerExclusiveLock(pBt->pPager, 0, 0); -#else memset(db->aCommit, 0, sizeof(db->aCommit)); + sqlite3BtreeEnter(p); rc = sqlite3PagerExclusiveLock(pBt->pPager, (db->eConcurrent==CONCURRENT_SCHEMA) ? 0 : pBt->pPage1->pDbPage, db->aCommit ); - - if( rc==SQLITE_BUSY_SNAPSHOT - && db->aCommit[SQLITE_COMMIT_CONFLICT_PGNO]>1 - && pBt->conc.eState==BTCONC_STATE_INUSE - && pBt->pCursor==0 - ){ - /* Page-level locking has detected a conflict. But it is not a - ** schema conflict (SQLITE_COMMIT_CONFLICT_PGNO>1) and the BtConcurrent - ** object is populated. So attempt a logical commit. */ - rc = btreeBcTryLogicalCommit(p, &bLog); - } - - if( bLog==0 - && (rc==SQLITE_BUSY_SNAPSHOT) +#ifdef SQLITE_OMIT_CONCURRENT + assert( db->aCommit[SQLITE_COMMIT_CONFLICT_PGNO]==0 ); +#else + if( (rc==SQLITE_BUSY_SNAPSHOT) && (pgno = db->aCommit[SQLITE_COMMIT_CONFLICT_PGNO]) ){ int iDb; @@ -89583,12 +87604,14 @@ SQLITE_PRIVATE int sqlite3BtreeExclusiveLock(Btree *p){ (void)sqlite3PagerGet(pBt->pPager, pgno, &pPg, 0); if( pPg ){ int bWrite = -1; - const char *zIdx = 0; + const char *zObj = 0; const char *zTab = 0; char zContent[17]; if( pPg ){ Pgno pgnoRoot = 0; + HashElem *pE; + Schema *pSchema; u8 *aData = (u8*)sqlite3PagerGetData(pPg); int i; for(i=0; i<8; i++){ @@ -89605,7 +87628,24 @@ SQLITE_PRIVATE int sqlite3BtreeExclusiveLock(Btree *p){ bWrite = sqlite3PagerIswriteable(pPg); sqlite3PagerUnref(pPg); - zTab = btreeBcRootToObject(pBt, pgnoRoot, &zIdx); + pSchema = sqlite3SchemaGet(p->db, p); + if( pSchema ){ + for(pE=sqliteHashFirst(&pSchema->tblHash); pE; pE=sqliteHashNext(pE)){ + Table *pTab = (Table *)sqliteHashData(pE); + if( pTab->tnum==pgnoRoot ){ + zObj = pTab->zName; + zTab = 0; + }else{ + Index *pIdx; + for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ + if( pIdx->tnum==pgnoRoot ){ + zObj = pIdx->zName; + zTab = pTab->zName; + } + } + } + } + } } sqlite3_log(SQLITE_OK, @@ -89614,8 +87654,8 @@ SQLITE_PRIVATE int sqlite3BtreeExclusiveLock(Btree *p){ "(%s page; part of db %s %s%s%s; content=%s...)", (int)pgno, (bWrite==0?"read-only":(bWrite>0?"read/write":"unknown")), - (zIdx ? "index" : "table"), - (zTab ? zTab : "UNKNOWN"), (zIdx ? "." : ""), (zIdx ? zIdx : ""), + (zTab ? "index" : "table"), + (zTab ? zTab : ""), (zTab ? "." : ""), (zObj ? zObj : "UNKNOWN"), zContent ); } @@ -89726,7 +87766,6 @@ SQLITE_API int sqlite3_commit_status( #undef sqlite3BtreeCount #undef sqlite3BtreeOffset #undef sqlite3BtreeIsEmpty -#undef sqlite3BtreeCursorNoScan #undef sqlite3BtreeCursor #ifdef SQLITE_DEBUG #undef sqlite3BtreeSeekCount @@ -90672,7 +88711,6 @@ struct BtCursorMethods { int(*xBtreeCount)(sqlite3*, BtCursor*, i64*); i64(*xBtreeOffset)(BtCursor*); int(*xBtreeIsEmpty)(BtCursor*, int*); - void(*xBtreeCursorNoScan)(BtCursor*); }; struct BtreeMethods { BtCursorMethods const *pCsrMethods; @@ -90820,9 +88858,6 @@ SQLITE_PRIVATE i64 sqlite3BtreeOffset(BtCursor *p){ SQLITE_PRIVATE int sqlite3BtreeIsEmpty(BtCursor *p, int *a){ return p->pMethods->xBtreeIsEmpty(p, a); } -SQLITE_PRIVATE void sqlite3BtreeCursorNoScan(BtCursor *p){ - p->pMethods->xBtreeCursorNoScan(p); -} SQLITE_PRIVATE Pgno sqlite3BtreeLastPage(Btree *p){ return p->pMethods->xBtreeLastPage(p); } @@ -90991,7 +89026,6 @@ static const BtCursorMethods hct_btcursor_methods = { .xBtreeCount = sqlite3HctBtreeCount, .xBtreeOffset = sqlite3HctBtreeOffset, .xBtreeIsEmpty = sqlite3HctBtreeIsEmpty, - .xBtreeCursorNoScan = sqlite3HctBtreeCursorNoScan, }; static const BtreeMethods hct_btree_methods = { .pCsrMethods = &hct_btcursor_methods, @@ -91077,7 +89111,6 @@ static const BtCursorMethods stock_btcursor_methods = { .xBtreeCount = sqlite3StockBtreeCount, .xBtreeOffset = sqlite3StockBtreeOffset, .xBtreeIsEmpty = sqlite3StockBtreeIsEmpty, - .xBtreeCursorNoScan = sqlite3StockBtreeCursorNoScan, }; static const BtreeMethods stock_btree_methods = { .pCsrMethods = &stock_btcursor_methods, @@ -95869,7 +93902,6 @@ SQLITE_PRIVATE int sqlite3HctBtreeClearTable(Btree *pBt, int iTable, i64 *pnChan sqlite3HctBtreeCloseCursor(pCsr); sqlite3HctTreeCsrClose(pTreeCsr); sqlite3DbFree(p->config.db, pRec); - sqlite3HctBufferFree(&buf); sqlite3_free(pCsr); } return rc; @@ -96360,12 +94392,6 @@ SQLITE_PRIVATE int sqlite3HctBtreeIsReadonly(Btree *p){ return 0; } -/* -** A no-scan cursor. -*/ -SQLITE_PRIVATE void sqlite3HctBtreeCursorNoScan(BtCursor *pCsr){ -} - #if !defined(SQLITE_OMIT_SHARED_CACHE) /* ** Return true if the Btree passed as the only argument is sharable. @@ -112843,14 +110869,9 @@ case OP_Found: { /* jump, in3, ncycle */ rc = sqlite3BtreeIndexMoveto(pC->uc.pCursor, pIdxKey, &pC->seekResult); sqlite3DbFreeNN(db, pIdxKey); } -#ifndef SQLITE_OMIT_CONCURRENT if( rc!=SQLITE_OK ){ goto abort_due_to_error; } - if( pOp->opcode==OP_NoConflict ){ - sqlite3BtreeCursorNoScan(pC->uc.pCursor); - } -#endif alreadyExists = (pC->seekResult==0); pC->nullRow = 1-alreadyExists; pC->deferredMoveto = 0; @@ -112980,9 +111001,6 @@ case OP_NotExists: /* jump, in3, ncycle */ VdbeBranchTaken(res!=0,2); pC->seekResult = res; if( res!=0 ){ -#ifndef SQLITE_OMIT_CONCURRENT - sqlite3BtreeCursorNoScan(pCrsr); -#endif assert( rc==SQLITE_OK ); if( pOp->p2==0 ){ rc = SQLITE_CORRUPT_BKPT; @@ -198580,9 +196598,6 @@ static int (*const sqlite3BuiltinExtensions[])(sqlite3*) = { #ifdef SQLITE_ENABLE_HCT sqlite3HctVtabInit, #endif -#ifndef SQLITE_OMIT_CONCURRENT - sqlite3ConcurrentRegister, -#endif }; #ifndef SQLITE_AMALGAMATION @@ -199249,18 +197264,6 @@ SQLITE_API int sqlite3_config(int op, ...){ break; } - case SQLITE_CONFIG_SHAREDLOG_MAXSIZE: { -#ifndef SQLITE_OMIT_CONCURRENT - i64 iVal = va_arg(ap, sqlite3_int64); - if( iVal<0 ){ - sqlite3GlobalConfig.nSharedLogMaxSize =SQLITE_DEFAULT_SHAREDLOG_MAXSIZE; - }else{ - sqlite3GlobalConfig.nSharedLogMaxSize = iVal; - } -#endif - break; - } - default: { rc = SQLITE_ERROR; break; @@ -274747,7 +272750,7 @@ static void fts5SourceIdFunc( ){ assert( nArg==0 ); UNUSED_PARAM2(nArg, apUnused); - sqlite3_result_text(pCtx, "fts5: 2026-07-09 16:12:25 45f6fccc2ed3e0b6d6f72cf3aeb849a77eace5f8675f40d3fff1a5eb80c909ac", -1, SQLITE_TRANSIENT); + sqlite3_result_text(pCtx, "fts5: 2026-05-12 17:42:11 012a04edd0ab7001f2635eeb766089201cbe372d54e5008e7138a5dbe522bf06", -1, SQLITE_TRANSIENT); } /* @@ -286240,9 +284243,11 @@ struct HctDatabaseStats { ** an attempt is made to commit a transaction after rcCommit is set to ** an error code, it fails with that error code. ** -** zCommitMsg: +** iRootCannotCommit: ** If rcCommit is set to SQLITE_BUSY_SNAPSHOT, this variable is set to -** a log message to issue when COMMIT is attempted. +** the root page number of the table or index on which the old key or range +** was read. It is used for a log message if an attempt is made to commit +** the transaction. */ struct HctDatabase { HctFile *pFile; @@ -286256,7 +284261,7 @@ struct HctDatabase { HctDbCsr *pScannerList; int rcCommit; - char *zCommitMsg; /* Log message to issue at COMMIT time */ + u64 iRootCannotCommit; /* These three are set at the start of each read transaction */ HctTMap *pTmap; /* Transaction map (non-NULL if trans open) */ @@ -287727,7 +285732,6 @@ static int hctDbCompareFPKey( sqlite3HctBufferFree(&buf); *pbGe = (res<=0); } - sqlite3HctBufferFree(&buf); return rc; } @@ -288118,11 +286122,8 @@ static int hctDbCsrScanFinish(HctDbCsr *pCsr){ } }else{ HctCsrIndexOp *pOp = pCsr->index.pCurrentOp; - + pCsr->index.pCurrentOp = 0; if( pOp ){ - pOp->pNextOp = pCsr->index.pOpList; - pCsr->index.pOpList = pOp; - pCsr->index.pCurrentOp = 0; if( pCsr->eDir!=BTREE_DIR_NONE ){ int nKey = 0; u8 *aCopy = 0; @@ -288150,6 +286151,9 @@ static int hctDbCsrScanFinish(HctDbCsr *pCsr){ pOp->nFirst = nKey; } } + + pOp->pNextOp = pCsr->index.pOpList; + pCsr->index.pOpList = pOp; } } } @@ -289032,7 +287036,16 @@ static int hctDbCsrSeekAndDescend( return rc; } -static void hctDbSetCannotCommit(HctDbCsr *pCsr); +/* +** If the cursor is currently pointing to a history entry, set the +** HctDatabase.iRootCannotCommit variable. +*/ +#define hctDbSetCannotCommit(pCsr) { \ + if( (pCsr)->nRange>0 && (pCsr)->pDb->rcCommit==SQLITE_OK ){ \ + (pCsr)->pDb->iRootCannotCommit = pCsr->iRoot; \ + (pCsr)->pDb->rcCommit = SQLITE_BUSY_SNAPSHOT; \ + } \ +} /* ** An integer is written into *pRes which is the result of @@ -291781,46 +289794,6 @@ static void hctDbLogReadConflict(HctDbCsr *pCsr){ sqlite3_free(zKey); } - -/* -** If the cursor is currently pointing to a history entry, set the -** HctDatabase.zCommitMsg and HctDatabase.rcCommit variables. -*/ -static void hctDbSetCannotCommit(HctDbCsr *pCsr){ - if( pCsr->nRange>0 - && pCsr->pDb->rcCommit==SQLITE_OK - && hctDbIsConcurrent(pCsr->pDb) - ){ - HctDatabase *pDb = pCsr->pDb; - const char *zType = 0; - const char *zTab = 0; - const char *zIdx = 0; - int cmp = 0; - int rc = SQLITE_OK; - - char *zKey = hctDbCsrKeyToText(pCsr); - HctDbRangeCsr *p = &pCsr->aRange[pCsr->nRange-1]; - HctDbKey *pKey = (pCsr->eDir==BTREE_DIR_FORWARD) ? &p->lowkey : &p->highkey; - - assert( pDb->zCommitMsg==0 ); - - hctDbFindObject(pCsr->pDb, (i64)pCsr->iRoot, &zType, &zTab, &zIdx); - cmp = hctDbCompareRangeCsrKey(&rc, pCsr->pDb, p, pKey, 0); - - pDb->zCommitMsg = sqlite3HctMprintf( - "read/write conflict on %s %s%s%s (root=%lld), key=%s, " - "conflicting=(%lld)%s (mytid=%lld)", - zType, zTab, zIdx?".":"", zIdx, - (i64)pCsr->iRoot, zKey, - pCsr->aRange[pCsr->nRange-1].iRangeTid, - (cmp==0 ? " (deleted)" : ""), - pCsr->pDb->iTid - ); - pDb->rcCommit = SQLITE_BUSY_SNAPSHOT; - sqlite3_free(zKey); - } -} - static int hctDbWriteWriteConflict( HctDatabase *pDb, HctDbWriter *p, @@ -293070,19 +291043,10 @@ SQLITE_PRIVATE int sqlite3HctDbEndWrite(HctDatabase *p, u64 iCid, int bRollback) } HctAtomicStore(pEntry, iVal); -#if 0 HCT_EXTRA_WR_LOGGING(p, ("mapping %lld to %lld (%s) (snapshot was %lld)", p->iTid, iVal & HCT_TID_MASK, (bRollback?"ROLLBACK":"COMMITTED"), p->iSnapshotId )); -#else - sqlite3_log( - bRollback ? SQLITE_BUSY_SNAPSHOT : SQLITE_NOTICE, - "mapping transaction %lld to %lld (%s) (snapshot was %lld)", - p->iTid, iVal & HCT_TID_MASK, (bRollback?"ROLLBACK":"COMMITTED"), - p->iSnapshotId - ); -#endif p->iTid = 0; return rc; @@ -293137,8 +291101,7 @@ SQLITE_PRIVATE int sqlite3HctDbEndRead(HctDatabase *pDb){ pDb->pPrevScannerList = pDb->pScannerList; pDb->pScannerList = 0; } - sqlite3_free(pDb->zCommitMsg); - pDb->zCommitMsg = 0; + pDb->iRootCannotCommit = 0; pDb->rcCommit = SQLITE_OK; if( pDb->pTmap ){ sqlite3HctTMapEnd(pTMapClient, pDb->iSnapshotId); @@ -294168,7 +292131,7 @@ static int hctDbValidateIndex(HctDatabase *pDb, HctDbCsr *pCsr){ } if( rc!=SQLITE_OK ) break; - if( pOp->pFirst!=0 && pOp->pLast==pOp->pFirst ){ + if( pOp->pLast==pOp->pFirst ){ assert( !sqlite3HctDbCsrEof(pCsr) ); rc = hctDbValidateEntry(pDb, pCsr); }else{ @@ -294241,8 +292204,19 @@ SQLITE_PRIVATE void sqlite3HctDbTMapScan(HctDatabase *pDb){ ** the SQL for) the transaction. */ SQLITE_PRIVATE int sqlite3HctDbPreValidate(HctDatabase *pDb){ - if( pDb->zCommitMsg ){ - sqlite3_log(SQLITE_BUSY_SNAPSHOT, "%s", pDb->zCommitMsg); + assert( (pDb->rcCommit==SQLITE_BUSY_SNAPSHOT)==(pDb->iRootCannotCommit!=0) ); + if( pDb->iRootCannotCommit ){ + + const char *zType = 0; + const char *zTab = 0; + const char *zIdx = 0; + hctDbFindObject(pDb, (i64)pDb->iRootCannotCommit, &zType, &zTab, &zIdx); + + sqlite3_log(SQLITE_BUSY_SNAPSHOT, + "read/write conflict on %s %s%s%s (root=%lld) " + "detected while preparing transaction", + zType, zTab, zIdx?".":"", zIdx, (i64)pDb->iRootCannotCommit + ); } return pDb->rcCommit; } @@ -298718,750 +296692,6 @@ SQLITE_PRIVATE void sqlite3HctLogFree(void *pLogList, int bUnlink){ /************** End of hct_log.c *********************************************/ -/************** Begin file btrecord.c ****************************************/ -/* -** 2026 February 13 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -** -** This file contains utility routines used by btree.c when compiled to -** support BEGIN CONCURRENT. -*/ - -/* #include "sqliteInt.h" */ -/* #include "vdbeInt.h" */ -/* #include "btreeInt.h" */ - -/* #include */ -/* #include */ - -#ifndef SQLITE_OMIT_CONCURRENT - -/* !SQLITE_OMIT_CONCURRENT -** -** Write the serialized data blob for the value stored in pMem into -** buf. It is assumed that the caller has allocated sufficient space. -** Return the number of bytes written. -*/ -static u32 bcRecordSerialPut(u8 *buf, Mem *pMem, u32 serial_type){ - u32 len; - - /* Integer and Real */ - if( serial_type<=7 && serial_type>0 ){ - u64 v; - u32 i; - if( serial_type==7 ){ - assert( sizeof(v)==sizeof(pMem->u.r) ); - memcpy(&v, &pMem->u.r, sizeof(v)); - swapMixedEndianFloat(v); - }else{ - v = pMem->u.i; - } - len = i = sqlite3SmallTypeSizes[serial_type]; - assert( i>0 ); - do{ - buf[--i] = (u8)(v&0xFF); - v >>= 8; - }while( i ); - return len; - } - - /* String or blob */ - if( serial_type>=12 ){ - assert( pMem->n + ((pMem->flags & MEM_Zero)?pMem->u.nZero:0) - == (int)sqlite3VdbeSerialTypeLen(serial_type) ); - len = pMem->n; - if( len>0 ) memcpy(buf, pMem->z, len); - if( (pMem->flags & MEM_Zero) && pMem->u.nZero>0 ){ - memset(&buf[len], 0, pMem->u.nZero); - len += pMem->u.nZero; - } - return len; - } - - /* NULL or constants 0 or 1 */ - return 0; -} - -/* !SQLITE_OMIT_CONCURRENT -** -** Return the serial-type for the value stored in pMem. Before returning, -** set (*pLen) to the size in bytes of the serialized form of the value. -** -** This routine might convert a large MEM_IntReal value into MEM_Real. -*/ -static u32 bcRecordSerialType(Mem *pMem, u32 *pLen){ - int flags = pMem->flags; - u32 n; - - assert( pLen!=0 ); - if( flags&MEM_Null ){ - *pLen = 0; - return 0; - } - if( flags&(MEM_Int|MEM_IntReal) ){ - /* Figure out whether to use 1, 2, 4, 6 or 8 bytes. */ -# define MAX_6BYTE ((((i64)0x00008000)<<32)-1) - i64 i = pMem->u.i; - u64 u; - if( i<0 ){ - u = ~i; - }else{ - u = i; - } - if( u<=127 ){ - if( (i&1)==i ){ - *pLen = 0; - return 8+(u32)u; - }else{ - *pLen = 1; - return 1; - } - } - if( u<=32767 ){ *pLen = 2; return 2; } - if( u<=8388607 ){ *pLen = 3; return 3; } - if( u<=2147483647 ){ *pLen = 4; return 4; } - if( u<=MAX_6BYTE ){ *pLen = 6; return 5; } - *pLen = 8; - return 6; - } - if( flags&MEM_Real ){ - *pLen = 8; - return 7; - } - assert( pMem->db->mallocFailed || flags&(MEM_Str|MEM_Blob) ); - assert( pMem->n>=0 ); - n = (u32)pMem->n; - if( flags & MEM_Zero ){ - n += pMem->u.nZero; - } - *pLen = n; - return ((n*2) + 12 + ((flags&MEM_Str)!=0)); -} - - -/* !SQLITE_OMIT_CONCURRENT -** -** Serialize the unpacked record in pRec into a buffer obtained from -** sqlite3_malloc(). If successful, (*ppRec) is set to point to the -** buffer and (*pnRec) to its size in bytes before returning SQLITE_OK. -** Or, if an OOM error occurs, return SQLITE_NOMEM. The final values -** of (*ppRec) and (*pnRec) are undefined in this case. -*/ -SQLITE_PRIVATE int sqlite3BcSerializeRecord( - UnpackedRecord *pRec, /* Record to serialize */ - u8 **ppRec, /* OUT: buffer containing serialization */ - int *pnRec /* OUT: size of (*ppRec) in bytes */ -){ - int ii; - int nData = 0; - int nHdr = 0; - u8 *pOut = 0; - int iOffHdr = 0; - int iOffData = 0; - - for(ii=0; iinField; ii++){ - u32 n; - u32 stype = bcRecordSerialType(&pRec->aMem[ii], &n); - assert( sqlite3VdbeSerialTypeLen(stype)==n ); - nData += n; - nHdr += sqlite3VarintLen(stype); - pRec->aMem[ii].uTemp = stype; - } - - if( nHdr<=126 ){ - /* The common case */ - nHdr += 1; - }else{ - /* Rare case of a really large header */ - int nVarint = sqlite3VarintLen(nHdr); - nHdr += nVarint; - if( nVarintnField; ii++){ - u32 stype = pRec->aMem[ii].uTemp; - iOffHdr += putVarint32(&pOut[iOffHdr], stype); - iOffData += bcRecordSerialPut(&pOut[iOffData], &pRec->aMem[ii], stype); - } - assert( iOffData==(nHdr+nData) ); - - *ppRec = pOut; - *pnRec = iOffData; - - return SQLITE_OK; -} - -/* !SQLITE_OMIT_CONCURRENT -** -** Helper function for bcRecordToText(). Return a buffer obtained from -** sqlite3_malloc() containing a nul-terminated string containing the hex -** form of blob aIn[], size nIn bytes. It is the responsibility of the -** caller to eventually free the buffer using sqlite3_free(). If an OOM -** occurs, NULL may be returned. -*/ -static char *bcHexEncode(const u8 *aIn, int nIn){ - char *zRet = sqlite3_malloc(nIn*2+1); - if( zRet ){ - static const char aDigit[] = "0123456789ABCDEF"; - int i; - for(i=0; i> 4) ]; - zRet[i*2+1] = aDigit[ (aIn[i] & 0xF) ]; - } - zRet[i*2] = '\0'; - } - return zRet; -} - - -/* !SQLITE_OMIT_CONCURRENT -** -** Buffer aRec[], which is nRec bytes in size, contains a serialized SQLite -** record. This function decodes the record and returns a nul-terminated -** string containing a human-readable version of the record. -** -** The value returned points to a buffer obtained from sqlite3_malloc(). It -** is the responsibility of the caller to eventually free this buffer using -** sqlite3_free(). If an OOM error occurs, NULL may be returned. -** -** If parameter delta is -ve, a "+" is appended to the text record. If it -** is +ve, a "-" is appended. -*/ -static char *bcRecordToText(const u8 *aRec, int nRec, int delta){ - char *zRet = 0; - const char *zSep = ""; - const u8 *pEndHdr; /* Points to one byte past record header */ - const u8 *pHdr; /* Current point in record header */ - const u8 *pBody; /* Current point in record data */ - u64 nHdr; /* Bytes in record header */ - const char *zDelta = 0; - - if( nRec>0 ){ - - pHdr = aRec + sqlite3GetVarint(aRec, &nHdr); - pBody = pEndHdr = &aRec[nHdr]; - while( pHdr0 ) zDelta = "-"; - return sqlite3_mprintf("(%z)%s", zRet, zDelta); -} - -/* !SQLITE_OMIT_CONCURRENT -** -** There has just been a conflict between pWrite and pRead on index zIdx, which -** is attached to table zTab. Issue a log message. -*/ -SQLITE_PRIVATE void sqlite3BcLogIndexConflict( - const char *zTab, - const char *zIdx, - BtWriteIndex *pWrite, - BtReadIndex *pRead -){ - sqlite3BeginBenignMalloc(); - { - char *zMin = bcRecordToText(pRead->aRecMin, pRead->nRecMin, pRead->drc_min); - char *zMax = bcRecordToText(pRead->aRecMax, pRead->nRecMax, pRead->drc_max); - char *zKey = bcRecordToText(pWrite->aRec, pWrite->nRec, 0); - sqlite3_log(SQLITE_OK, - "cannot commit CONCURRENT transaction - conflict in index %s.%s - " - "range (%s,%s) conflicts with write to key %s", - zTab, (zIdx ? zIdx : "pk"), - zMin, zMax, zKey - ); - sqlite3_free(zMin); - sqlite3_free(zMax); - sqlite3_free(zKey); - } - sqlite3EndBenignMalloc(); -} - -/* !SQLITE_OMIT_CONCURRENT -** -** If possible, return a pointer to a buffer containing the SQL statement -** currently being executed by handle db. -*/ -SQLITE_PRIVATE const char *sqlite3BcCurrentSql(sqlite3 *db){ - const char *zRet = "?"; - if( db->nVdbeRead==1 ){ - Vdbe *pIter; - for(pIter=db->pVdbe; pIter; pIter=pIter->pVNext){ - if( pIter->eVdbeState==VDBE_RUN_STATE && pIter->bIsReader ){ - zRet = pIter->zSql; - break; - } - } - } - return zRet; -} - -/************************************************************************* -** Start of virtual table "sqlite_concurrent" implementation. -*/ -#define CONC_SCHEMA "CREATE TABLE x(root, op, k1, k2, sortem HIDDEN)" -#define CONCURRENT_SORTEM 4 - -typedef struct ConcTable ConcTable; -typedef struct ConcCursor ConcCursor; -typedef struct ConcRow ConcRow; - -/* -** Each row returned from the sqlite_concurrent table is represented by -** an instance of this structure. Each call to the xFilter() method -** constructs a linked-list of these objects representing all the rows -** that will be returned by the virtual table scan. Then the xNext() -** method walks through the list. -*/ -struct ConcRow { - Pgno root; - const char *zOp; - char *zK1; - char *zK2; - ConcRow *pRowNext; -}; - -/* -** Cursor type for the sqlite_concurrent virtual table. -*/ -struct ConcCursor { - sqlite3_vtab_cursor base; /* Base class. Must be first */ - ConcRow *pRow; -}; - -/* -** Table type for the sqlite_concurrent virtual table. -*/ -struct ConcTable { - sqlite3_vtab base; /* Base class. Must be first */ - sqlite3 *db; /* The database */ -}; - -/* !SQLITE_OMIT_CONCURRENT -** -** Connect to the sqlite_concurrent eponymous table. -*/ -static int concConnect( - sqlite3 *db, - void *pAux, - int argc, const char *const*argv, - sqlite3_vtab **ppVtab, - char **pzErr -){ - ConcTable *pTab = 0; - int rc = SQLITE_OK; - (void)pAux; - (void)argc; - (void)argv; - (void)pzErr; - - sqlite3_vtab_config(db, SQLITE_VTAB_DIRECTONLY); - sqlite3_vtab_config(db, SQLITE_VTAB_USES_ALL_SCHEMAS); - rc = sqlite3_declare_vtab(db, CONC_SCHEMA); - if( rc==SQLITE_OK ){ - pTab = (ConcTable *)sqlite3_malloc64(sizeof(ConcTable)); - if( pTab==0 ) rc = SQLITE_NOMEM_BKPT; - } - - assert( rc==SQLITE_OK || pTab==0 ); - if( rc==SQLITE_OK ){ - memset(pTab, 0, sizeof(ConcTable)); - pTab->db = db; - } - - *ppVtab = (sqlite3_vtab*)pTab; - return rc; -} - -/* !SQLITE_OMIT_CONCURRENT -** -** Disconnect from sqlite_concurrent. -*/ -static int concDisconnect(sqlite3_vtab *pVtab){ - sqlite3_free(pVtab); - return SQLITE_OK; -} - -/* !SQLITE_OMIT_CONCURRENT -** -** xBestIndex method for sqlite_concurrent. There are two possible plans: -** -** idxNum==0 - full table scan. -** idxNum==1 - full table scan after possibly sorting and merging -** read ranges. -** -** idxNum==1 is used if there is an equality constraint on hidden column -** "sortem". In this case the RHS of the = is passed to xFilter(). xFilter() -** will sort and merge the read ranges if this parameter is non-zero. The -** SQL syntax is usually: -** -** SELECT * FROM sqlite_concurrent; -- full table scan -** SELECT * FROM sqlite_concurrent(1); -- sort + merge reads first -*/ -static int concBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){ - int ii; - for(ii=0; iinConstraint; ii++){ - struct sqlite3_index_constraint *p = &pIdxInfo->aConstraint[ii]; - if( p->iColumn!=CONCURRENT_SORTEM ) continue; - if( p->op!=SQLITE_INDEX_CONSTRAINT_EQ ) continue; - if( !p->usable ) return SQLITE_CONSTRAINT; - pIdxInfo->idxNum = 1; - pIdxInfo->aConstraintUsage[ii].argvIndex = 1; - pIdxInfo->aConstraintUsage[ii].omit = 1; - break; - } - return SQLITE_OK; -} - - -/* !SQLITE_OMIT_CONCURRENT -** -** Open a new cursor for sqlite_concurrent. -*/ -static int concOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){ - ConcCursor *pCsr; - - pCsr = (ConcCursor *)sqlite3_malloc64(sizeof(ConcCursor)); - if( pCsr==0 ){ - return SQLITE_NOMEM_BKPT; - }else{ - memset(pCsr, 0, sizeof(ConcCursor)); - } - - *ppCursor = (sqlite3_vtab_cursor *)pCsr; - return SQLITE_OK; -} - -/* !SQLITE_OMIT_CONCURRENT -** -** Close an sqlite_concurrent cursor. -*/ -static int concClose(sqlite3_vtab_cursor *pCursor){ - ConcCursor *pCsr = (ConcCursor *)pCursor; - ConcRow *pRow = 0; - ConcRow *pNext = 0; - for(pRow=pCsr->pRow; pRow; pRow=pNext){ - pNext = pRow->pRowNext; - sqlite3_free(pRow->zK1); - sqlite3_free(pRow->zK2); - sqlite3_free(pRow); - } - sqlite3_free(pCsr); - return SQLITE_OK; -} - -/* !SQLITE_OMIT_CONCURRENT -** -** Advance the sqlite_concurrent cursor to the next row. -*/ -static int concNext(sqlite3_vtab_cursor *pCursor){ - ConcCursor *pCsr = (ConcCursor *)pCursor; - ConcRow *pFree = pCsr->pRow; - assert( pFree ); - pCsr->pRow = pFree->pRowNext; - sqlite3_free(pFree->zK1); - sqlite3_free(pFree->zK2); - sqlite3_free(pFree); - return SQLITE_OK; -} - -/* !SQLITE_OMIT_CONCURRENT -** -** Return true if the sqlite_concurrent cursor is at EOF. -*/ -static int concEof(sqlite3_vtab_cursor *pCursor){ - ConcCursor *pCsr = (ConcCursor *)pCursor; - return pCsr->pRow==0; -} - -/* !SQLITE_OMIT_CONCURRENT -** -** xFilter method for sqlite_concurrent. -** -** In all cases this function populates the cursor with rows for each -** read and write currently accumulated by the datbase connection. -** -** idxNum may be either 0 or 1. If it is 1, then there is a single -** argument passed. If this is a non-zero integer, the reads are -** sorted before any rows are returned. -*/ -static int concFilter( - sqlite3_vtab_cursor *pCursor, - int idxNum, const char *idxStr, - int argc, sqlite3_value **argv -){ - ConcCursor *pCsr = (ConcCursor *)pCursor; - ConcTable *pTab = (ConcTable *)pCursor->pVtab; - sqlite3 *db = pTab->db; - BtConcurrent *pConc = &db->aDb[0].pBt->pBt->conc; - int rc = SQLITE_OK; - - assert( idxNum==0 || idxNum==1 ); - assert( idxNum==argc ); - - if( pConc->eState==BTCONC_STATE_INUSE ){ - if( idxNum==1 ){ - int bSort = sqlite3_value_int(argv[0]); - if( bSort ){ - rc = sqlite3BtreeSortReadArrays(pConc); - } - } - - if( rc==SQLITE_OK ){ - int ii; - - for(ii=0; rc==SQLITE_OK && iinWrite; ii++){ - BtWrite *pWrite = &pConc->aWrite[ii]; - ConcRow *pRow = (ConcRow*)sqlite3MallocZero(sizeof(ConcRow)); - if( pRow==0 ){ - rc = SQLITE_NOMEM_BKPT; - }else{ - pRow->root = pWrite->iRoot; - pRow->zOp = pWrite->bDel ? "delete" : "insert"; - if( pWrite->pKeyInfo ){ - pRow->zK1 = bcRecordToText(pWrite->aRec, pWrite->nRec, 0); - if( pRow->zK1==0 ) rc = SQLITE_NOMEM_BKPT; - }else{ - pRow->zK1 = sqlite3_mprintf("%lld", pWrite->iKey); - if( pRow->zK1==0 ){ - rc = SQLITE_NOMEM_BKPT; - }else if( pWrite->bDel==0 ){ - pRow->zK2 = bcRecordToText(pWrite->aRec, pWrite->nRec, 0); - if( pRow->zK2==0 ) rc = SQLITE_NOMEM_BKPT; - } - } - pRow->pRowNext = pCsr->pRow; - pCsr->pRow = pRow; - } - } - - for(ii=pConc->nReadIndex-1; rc==SQLITE_OK && ii>=0; ii--){ - BtReadIndex *p = &pConc->aReadIndex[ii]; - ConcRow *pRow = (ConcRow*)sqlite3MallocZero(sizeof(ConcRow)); - if( pRow==0 ){ - rc = SQLITE_NOMEM_BKPT; - }else{ - pRow->root = p->iRoot; - pRow->zOp = "read"; - pRow->zK1 = bcRecordToText(p->aRecMin, p->nRecMin, p->drc_min); - pRow->zK2 = bcRecordToText(p->aRecMax, p->nRecMax, p->drc_max); - pRow->pRowNext = pCsr->pRow; - pCsr->pRow = pRow; - if( pRow->zK1==0 || pRow->zK2==0 ){ - rc = SQLITE_NOMEM_BKPT; - } - } - } - - for(ii=pConc->nReadIntkey-1; rc==SQLITE_OK && ii>=0; ii--){ - BtReadIntkey *p = &pConc->aReadIntkey[ii]; - ConcRow *pRow = (ConcRow*)sqlite3MallocZero(sizeof(ConcRow)); - if( pRow==0 ){ - rc = SQLITE_NOMEM_BKPT; - }else{ - pRow->root = p->iRoot; - pRow->zOp = "read"; - pRow->zK1 = sqlite3_mprintf("%lld", p->iMin); - pRow->zK2 = sqlite3_mprintf("%lld", p->iMax); - pRow->pRowNext = pCsr->pRow; - pCsr->pRow = pRow; - if( pRow->zK1==0 || pRow->zK2==0 ){ - rc = SQLITE_NOMEM_BKPT; - } - } - } - } - } - - return rc; -} - -/* !SQLITE_OMIT_CONCURRENT -** -** xColumn method for sqlite_concurrent. -*/ -static int concColumn( - sqlite3_vtab_cursor *pCursor, - sqlite3_context *ctx, - int i -){ - ConcCursor *pCsr = (ConcCursor *)pCursor; - int rc = SQLITE_OK; - ConcRow *pRow = pCsr->pRow; - assert( pRow ); - switch( i ){ - case 0: { /* root */ - sqlite3_result_int64(ctx, (sqlite3_int64)pRow->root); - break; - } - case 1: { /* op */ - sqlite3_result_text(ctx, pRow->zOp, -1, SQLITE_TRANSIENT); - break; - } - case 2: { /* k1 */ - sqlite3_result_text(ctx, pRow->zK1, -1, SQLITE_TRANSIENT); - break; - } - case 3: { /* k2 */ - sqlite3_result_text(ctx, pRow->zK2, -1, SQLITE_TRANSIENT); - break; - } - } - return rc; -} - -/* !SQLITE_OMIT_CONCURRENT -** -** xRowid method for sqlite_concurrent. -*/ -static int concRowid(sqlite3_vtab_cursor *pCursor, sqlite_int64 *pRowid){ - *pRowid = 0; - return SQLITE_OK; -} - -/* !SQLITE_OMIT_CONCURRENT -** -** Register the sqlite_concurrent eponymous virtual table with database -** connection db. Return SQLITE_OK if successful, or an SQLite error code -** if an error occurs. -*/ -SQLITE_PRIVATE int sqlite3ConcurrentRegister(sqlite3 *db){ - static sqlite3_module conc_module = { - 2, /* iVersion */ - concConnect, /* xCreate */ - concConnect, /* xConnect */ - concBestIndex, /* xBestIndex */ - concDisconnect, /* xDisconnect */ - concDisconnect, /* xDestroy */ - concOpen, /* xOpen - open a cursor */ - concClose, /* xClose - close a cursor */ - concFilter, /* xFilter - configure scan constraints */ - concNext, /* xNext - advance a cursor */ - concEof, /* xEof - check for end of scan */ - concColumn, /* xColumn - read data */ - concRowid, /* xRowid - read data */ - 0, /* xUpdate */ - 0, /* xBegin */ - 0, /* xSync */ - 0, /* xCommit */ - 0, /* xRollback */ - 0, /* xFindMethod */ - 0, /* xRename */ - 0, /* xSavepoint */ - 0, /* xRelease */ - 0, /* xRollbackTo */ - 0, /* xShadowName */ - 0 /* xIntegrity */ - }; - return sqlite3_create_module(db, "sqlite_concurrent", &conc_module, 0); -} - -#endif /* !SQLITE_OMIT_CONCURRENT */ - -/************** End of btrecord.c ********************************************/ /* Return the source-id for this library */ SQLITE_API const char *sqlite3_sourceid(void){ return SQLITE_SOURCE_ID; } #endif /* SQLITE_AMALGAMATION */ diff --git a/libstuff/sqlite3.h b/libstuff/sqlite3.h index bd374d178..56f555862 100644 --- a/libstuff/sqlite3.h +++ b/libstuff/sqlite3.h @@ -148,10 +148,10 @@ extern "C" { */ #define SQLITE_VERSION "3.54.0" #define SQLITE_VERSION_NUMBER 3054000 -#define SQLITE_SOURCE_ID "2026-07-09 16:12:25 45f6fccc2ed3e0b6d6f72cf3aeb849a77eace5f8675f40d3fff-experimental" -#define SQLITE_SCM_BRANCH "unknown" -#define SQLITE_SCM_TAGS "unknown" -#define SQLITE_SCM_DATETIME "2026-07-09T16:12:25.329Z" +#define SQLITE_SOURCE_ID "2026-05-12 17:42:11 012a04edd0ab7001f2635eeb766089201cbe372d54e5008e7138a5dbe522bf06" +#define SQLITE_SCM_BRANCH "hctree-bedrock" +#define SQLITE_SCM_TAGS "" +#define SQLITE_SCM_DATETIME "2026-05-12T17:42:11.503Z" /* ** CAPI3REF: Run-Time Library Version Numbers @@ -2210,19 +2210,6 @@ struct sqlite3_mem_methods { ** recommended case) then the integer is always filled with zero, regardless ** if its initial value. ** -** -** [[SQLITE_CONFIG_SHAREDLOG_MAXSIZE]] -**
SQLITE_CONFIG_SHAREDLOG_MAXSIZE -**
This option is used to set the maximum size of a BEGIN CONCURRENT -** shared-log in bytes. The default value is 1GiB (1*1024*1024*1024). The -** argument to this configuration option must be a 64-bit signed integer -** (type sqlite3_int64) to use as the new limit. A negative parameter -** restores the default value, a value of zero disabled shared-logs -** altogether. There is no way to configure unlimited memory usage, but -** applications may instead configure a very large value (e.g. 1TiB). -** Shared-log entries are automatically discarded when their associated -** transactions are checkpointed, which prevents the shared-log from growing -** indefinitely in this case. */ #define SQLITE_CONFIG_SINGLETHREAD 1 /* nil */ #define SQLITE_CONFIG_MULTITHREAD 2 /* nil */ @@ -2254,7 +2241,6 @@ struct sqlite3_mem_methods { #define SQLITE_CONFIG_SORTERREF_SIZE 28 /* int nByte */ #define SQLITE_CONFIG_MEMDB_MAXSIZE 29 /* sqlite3_int64 */ #define SQLITE_CONFIG_ROWID_IN_VIEW 30 /* int* */ -#define SQLITE_CONFIG_SHAREDLOG_MAXSIZE 31 /* sqlite3_int64 */ /* ** CAPI3REF: Database Connection Configuration Options diff --git a/libstuff/sqlite3ext.h b/libstuff/sqlite3ext.h index 6c12ec88b..36c35b7b1 100644 --- a/libstuff/sqlite3ext.h +++ b/libstuff/sqlite3ext.h @@ -375,9 +375,6 @@ struct sqlite3_api_routines { void (*str_truncate)(sqlite3_str*,int); void (*str_free)(sqlite3_str*); int (*carray_bind)(sqlite3_stmt*,int,void*,int,int,void(*)(void*)); - int (*carray_bind_v2)(sqlite3_stmt*,int,void*,int,int,void(*)(void*),void*); - /* Version 3.54.0 and later */ - sqlite3_int64 (*incomplete)(const char*); }; /* @@ -720,9 +717,6 @@ typedef int (*sqlite3_loadext_entry)( #define sqlite3_str_truncate sqlite3_api->str_truncate #define sqlite3_str_free sqlite3_api->str_free #define sqlite3_carray_bind sqlite3_api->carray_bind -#define sqlite3_carray_bind_v2 sqlite3_api->carray_bind_v2 -/* Version 3.54.0 and later */ -#define sqlite3_incomplete sqlite3_api->incomplete #endif /* !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) */ #if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION)