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)