| Index: third_party/sqlite/src/src/wal.c
|
| diff --git a/third_party/sqlite/src/src/wal.c b/third_party/sqlite/src/src/wal.c
|
| index f38e24a961a37b367f996849a9d2b33a970065a1..09f605fe57624271afff039007c021ed65485458 100644
|
| --- a/third_party/sqlite/src/src/wal.c
|
| +++ b/third_party/sqlite/src/src/wal.c
|
| @@ -445,6 +445,7 @@ struct Wal {
|
| u8 padToSectorBoundary; /* Pad transactions out to the next sector */
|
| WalIndexHdr hdr; /* Wal-index header for current transaction */
|
| u32 minFrame; /* Ignore wal frames before this one */
|
| + u32 iReCksum; /* On commit, recalculate checksums from here */
|
| const char *zWalName; /* Name of WAL file */
|
| u32 nCkpt; /* Checkpoint sequence counter in the wal-header */
|
| #ifdef SQLITE_DEBUG
|
| @@ -545,7 +546,7 @@ static int walIndexPage(Wal *pWal, int iPage, volatile u32 **ppPage){
|
| apNew = (volatile u32 **)sqlite3_realloc64((void *)pWal->apWiData, nByte);
|
| if( !apNew ){
|
| *ppPage = 0;
|
| - return SQLITE_NOMEM;
|
| + return SQLITE_NOMEM_BKPT;
|
| }
|
| memset((void*)&apNew[pWal->nWiData], 0,
|
| sizeof(u32*)*(iPage+1-pWal->nWiData));
|
| @@ -557,7 +558,7 @@ static int walIndexPage(Wal *pWal, int iPage, volatile u32 **ppPage){
|
| if( pWal->apWiData[iPage]==0 ){
|
| if( pWal->exclusiveMode==WAL_HEAPMEMORY_MODE ){
|
| pWal->apWiData[iPage] = (u32 volatile *)sqlite3MallocZero(WALINDEX_PGSZ);
|
| - if( !pWal->apWiData[iPage] ) rc = SQLITE_NOMEM;
|
| + if( !pWal->apWiData[iPage] ) rc = SQLITE_NOMEM_BKPT;
|
| }else{
|
| rc = sqlite3OsShmMap(pWal->pDbFd, iPage, WALINDEX_PGSZ,
|
| pWal->writeLock, (void volatile **)&pWal->apWiData[iPage]
|
| @@ -698,14 +699,18 @@ static void walEncodeFrame(
|
| assert( WAL_FRAME_HDRSIZE==24 );
|
| sqlite3Put4byte(&aFrame[0], iPage);
|
| sqlite3Put4byte(&aFrame[4], nTruncate);
|
| - memcpy(&aFrame[8], pWal->hdr.aSalt, 8);
|
| + if( pWal->iReCksum==0 ){
|
| + memcpy(&aFrame[8], pWal->hdr.aSalt, 8);
|
|
|
| - nativeCksum = (pWal->hdr.bigEndCksum==SQLITE_BIGENDIAN);
|
| - walChecksumBytes(nativeCksum, aFrame, 8, aCksum, aCksum);
|
| - walChecksumBytes(nativeCksum, aData, pWal->szPage, aCksum, aCksum);
|
| + nativeCksum = (pWal->hdr.bigEndCksum==SQLITE_BIGENDIAN);
|
| + walChecksumBytes(nativeCksum, aFrame, 8, aCksum, aCksum);
|
| + walChecksumBytes(nativeCksum, aData, pWal->szPage, aCksum, aCksum);
|
|
|
| - sqlite3Put4byte(&aFrame[16], aCksum[0]);
|
| - sqlite3Put4byte(&aFrame[20], aCksum[1]);
|
| + sqlite3Put4byte(&aFrame[16], aCksum[0]);
|
| + sqlite3Put4byte(&aFrame[20], aCksum[1]);
|
| + }else{
|
| + memset(&aFrame[8], 0, 16);
|
| + }
|
| }
|
|
|
| /*
|
| @@ -1168,7 +1173,7 @@ static int walIndexRecover(Wal *pWal){
|
| szFrame = szPage + WAL_FRAME_HDRSIZE;
|
| aFrame = (u8 *)sqlite3_malloc64(szFrame);
|
| if( !aFrame ){
|
| - rc = SQLITE_NOMEM;
|
| + rc = SQLITE_NOMEM_BKPT;
|
| goto recovery_error;
|
| }
|
| aData = &aFrame[WAL_FRAME_HDRSIZE];
|
| @@ -1306,7 +1311,7 @@ int sqlite3WalOpen(
|
| *ppWal = 0;
|
| pRet = (Wal*)sqlite3MallocZero(sizeof(Wal) + pVfs->szOsFile);
|
| if( !pRet ){
|
| - return SQLITE_NOMEM;
|
| + return SQLITE_NOMEM_BKPT;
|
| }
|
|
|
| pRet->pVfs = pVfs;
|
| @@ -1570,7 +1575,7 @@ static int walIteratorInit(Wal *pWal, WalIterator **pp){
|
| + iLast*sizeof(ht_slot);
|
| p = (WalIterator *)sqlite3_malloc64(nByte);
|
| if( !p ){
|
| - return SQLITE_NOMEM;
|
| + return SQLITE_NOMEM_BKPT;
|
| }
|
| memset(p, 0, nByte);
|
| p->nSegment = nSegment;
|
| @@ -1582,7 +1587,7 @@ static int walIteratorInit(Wal *pWal, WalIterator **pp){
|
| sizeof(ht_slot) * (iLast>HASHTABLE_NPAGE?HASHTABLE_NPAGE:iLast)
|
| );
|
| if( !aTmp ){
|
| - rc = SQLITE_NOMEM;
|
| + rc = SQLITE_NOMEM_BKPT;
|
| }
|
|
|
| for(i=0; rc==SQLITE_OK && i<nSegment; i++){
|
| @@ -1718,6 +1723,7 @@ static void walRestartHdr(Wal *pWal, u32 salt1){
|
| */
|
| static int walCheckpoint(
|
| Wal *pWal, /* Wal connection */
|
| + sqlite3 *db, /* Check for interrupts on this handle */
|
| int eMode, /* One of PASSIVE, FULL or RESTART */
|
| int (*xBusy)(void*), /* Function to call when busy */
|
| void *pBusyArg, /* Context argument for xBusyHandler */
|
| @@ -1812,6 +1818,10 @@ static int walCheckpoint(
|
| while( rc==SQLITE_OK && 0==walIteratorNext(pIter, &iDbpage, &iFrame) ){
|
| i64 iOffset;
|
| assert( walFramePgno(pWal, iFrame)==iDbpage );
|
| + if( db->u1.isInterrupted ){
|
| + rc = db->mallocFailed ? SQLITE_NOMEM_BKPT : SQLITE_INTERRUPT;
|
| + break;
|
| + }
|
| if( iFrame<=nBackfill || iFrame>mxSafeFrame || iDbpage>mxPage ){
|
| continue;
|
| }
|
| @@ -1916,6 +1926,7 @@ static void walLimitSize(Wal *pWal, i64 nMax){
|
| */
|
| int sqlite3WalClose(
|
| Wal *pWal, /* Wal to close */
|
| + sqlite3 *db, /* For interrupt flag */
|
| int sync_flags, /* Flags to pass to OsSync() (or 0) */
|
| int nBuf,
|
| u8 *zBuf /* Buffer of at least nBuf bytes */
|
| @@ -1932,13 +1943,14 @@ int sqlite3WalClose(
|
| **
|
| ** The EXCLUSIVE lock is not released before returning.
|
| */
|
| - rc = sqlite3OsLock(pWal->pDbFd, SQLITE_LOCK_EXCLUSIVE);
|
| - if( rc==SQLITE_OK ){
|
| + if( zBuf!=0
|
| + && SQLITE_OK==(rc = sqlite3OsLock(pWal->pDbFd, SQLITE_LOCK_EXCLUSIVE))
|
| + ){
|
| if( pWal->exclusiveMode==WAL_NORMAL_MODE ){
|
| pWal->exclusiveMode = WAL_EXCLUSIVE_MODE;
|
| }
|
| - rc = sqlite3WalCheckpoint(
|
| - pWal, SQLITE_CHECKPOINT_PASSIVE, 0, 0, sync_flags, nBuf, zBuf, 0, 0
|
| + rc = sqlite3WalCheckpoint(pWal, db,
|
| + SQLITE_CHECKPOINT_PASSIVE, 0, 0, sync_flags, nBuf, zBuf, 0, 0
|
| );
|
| if( rc==SQLITE_OK ){
|
| int bPersist = -1;
|
| @@ -2367,6 +2379,84 @@ static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int cnt){
|
| return rc;
|
| }
|
|
|
| +#ifdef SQLITE_ENABLE_SNAPSHOT
|
| +/*
|
| +** Attempt to reduce the value of the WalCkptInfo.nBackfillAttempted
|
| +** variable so that older snapshots can be accessed. To do this, loop
|
| +** through all wal frames from nBackfillAttempted to (nBackfill+1),
|
| +** comparing their content to the corresponding page with the database
|
| +** file, if any. Set nBackfillAttempted to the frame number of the
|
| +** first frame for which the wal file content matches the db file.
|
| +**
|
| +** This is only really safe if the file-system is such that any page
|
| +** writes made by earlier checkpointers were atomic operations, which
|
| +** is not always true. It is also possible that nBackfillAttempted
|
| +** may be left set to a value larger than expected, if a wal frame
|
| +** contains content that duplicate of an earlier version of the same
|
| +** page.
|
| +**
|
| +** SQLITE_OK is returned if successful, or an SQLite error code if an
|
| +** error occurs. It is not an error if nBackfillAttempted cannot be
|
| +** decreased at all.
|
| +*/
|
| +int sqlite3WalSnapshotRecover(Wal *pWal){
|
| + int rc;
|
| +
|
| + assert( pWal->readLock>=0 );
|
| + rc = walLockExclusive(pWal, WAL_CKPT_LOCK, 1);
|
| + if( rc==SQLITE_OK ){
|
| + volatile WalCkptInfo *pInfo = walCkptInfo(pWal);
|
| + int szPage = (int)pWal->szPage;
|
| + i64 szDb; /* Size of db file in bytes */
|
| +
|
| + rc = sqlite3OsFileSize(pWal->pDbFd, &szDb);
|
| + if( rc==SQLITE_OK ){
|
| + void *pBuf1 = sqlite3_malloc(szPage);
|
| + void *pBuf2 = sqlite3_malloc(szPage);
|
| + if( pBuf1==0 || pBuf2==0 ){
|
| + rc = SQLITE_NOMEM;
|
| + }else{
|
| + u32 i = pInfo->nBackfillAttempted;
|
| + for(i=pInfo->nBackfillAttempted; i>pInfo->nBackfill; i--){
|
| + volatile ht_slot *dummy;
|
| + volatile u32 *aPgno; /* Array of page numbers */
|
| + u32 iZero; /* Frame corresponding to aPgno[0] */
|
| + u32 pgno; /* Page number in db file */
|
| + i64 iDbOff; /* Offset of db file entry */
|
| + i64 iWalOff; /* Offset of wal file entry */
|
| +
|
| + rc = walHashGet(pWal, walFramePage(i), &dummy, &aPgno, &iZero);
|
| + if( rc!=SQLITE_OK ) break;
|
| + pgno = aPgno[i-iZero];
|
| + iDbOff = (i64)(pgno-1) * szPage;
|
| +
|
| + if( iDbOff+szPage<=szDb ){
|
| + iWalOff = walFrameOffset(i, szPage) + WAL_FRAME_HDRSIZE;
|
| + rc = sqlite3OsRead(pWal->pWalFd, pBuf1, szPage, iWalOff);
|
| +
|
| + if( rc==SQLITE_OK ){
|
| + rc = sqlite3OsRead(pWal->pDbFd, pBuf2, szPage, iDbOff);
|
| + }
|
| +
|
| + if( rc!=SQLITE_OK || 0==memcmp(pBuf1, pBuf2, szPage) ){
|
| + break;
|
| + }
|
| + }
|
| +
|
| + pInfo->nBackfillAttempted = i-1;
|
| + }
|
| + }
|
| +
|
| + sqlite3_free(pBuf1);
|
| + sqlite3_free(pBuf2);
|
| + }
|
| + walUnlockExclusive(pWal, WAL_CKPT_LOCK, 1);
|
| + }
|
| +
|
| + return rc;
|
| +}
|
| +#endif /* SQLITE_ENABLE_SNAPSHOT */
|
| +
|
| /*
|
| ** Begin a read transaction on the database.
|
| **
|
| @@ -2429,7 +2519,11 @@ int sqlite3WalBeginReadTransaction(Wal *pWal, int *pChanged){
|
| ** has not yet set the pInfo->nBackfillAttempted variable to indicate
|
| ** its intent. To avoid the race condition this leads to, ensure that
|
| ** there is no checkpointer process by taking a shared CKPT lock
|
| - ** before checking pInfo->nBackfillAttempted. */
|
| + ** before checking pInfo->nBackfillAttempted.
|
| + **
|
| + ** TODO: Does the aReadMark[] lock prevent a checkpointer from doing
|
| + ** this already?
|
| + */
|
| rc = walLockShared(pWal, WAL_CKPT_LOCK);
|
|
|
| if( rc==SQLITE_OK ){
|
| @@ -2632,6 +2726,7 @@ int sqlite3WalBeginWriteTransaction(Wal *pWal){
|
| /* Cannot start a write transaction without first holding a read
|
| ** transaction. */
|
| assert( pWal->readLock>=0 );
|
| + assert( pWal->writeLock==0 && pWal->iReCksum==0 );
|
|
|
| if( pWal->readOnly ){
|
| return SQLITE_READONLY;
|
| @@ -2667,6 +2762,7 @@ int sqlite3WalEndWriteTransaction(Wal *pWal){
|
| if( pWal->writeLock ){
|
| walUnlockExclusive(pWal, WAL_WRITE_LOCK, 1);
|
| pWal->writeLock = 0;
|
| + pWal->iReCksum = 0;
|
| pWal->truncateOnCommit = 0;
|
| }
|
| return SQLITE_OK;
|
| @@ -2873,7 +2969,7 @@ static int walWriteOneFrame(
|
| void *pData; /* Data actually written */
|
| u8 aFrame[WAL_FRAME_HDRSIZE]; /* Buffer to assemble frame-header in */
|
| #if defined(SQLITE_HAS_CODEC)
|
| - if( (pData = sqlite3PagerCodec(pPage))==0 ) return SQLITE_NOMEM;
|
| + if( (pData = sqlite3PagerCodec(pPage))==0 ) return SQLITE_NOMEM_BKPT;
|
| #else
|
| pData = pPage->pData;
|
| #endif
|
| @@ -2885,6 +2981,59 @@ static int walWriteOneFrame(
|
| return rc;
|
| }
|
|
|
| +/*
|
| +** This function is called as part of committing a transaction within which
|
| +** one or more frames have been overwritten. It updates the checksums for
|
| +** all frames written to the wal file by the current transaction starting
|
| +** with the earliest to have been overwritten.
|
| +**
|
| +** SQLITE_OK is returned if successful, or an SQLite error code otherwise.
|
| +*/
|
| +static int walRewriteChecksums(Wal *pWal, u32 iLast){
|
| + const int szPage = pWal->szPage;/* Database page size */
|
| + int rc = SQLITE_OK; /* Return code */
|
| + u8 *aBuf; /* Buffer to load data from wal file into */
|
| + u8 aFrame[WAL_FRAME_HDRSIZE]; /* Buffer to assemble frame-headers in */
|
| + u32 iRead; /* Next frame to read from wal file */
|
| + i64 iCksumOff;
|
| +
|
| + aBuf = sqlite3_malloc(szPage + WAL_FRAME_HDRSIZE);
|
| + if( aBuf==0 ) return SQLITE_NOMEM_BKPT;
|
| +
|
| + /* Find the checksum values to use as input for the recalculating the
|
| + ** first checksum. If the first frame is frame 1 (implying that the current
|
| + ** transaction restarted the wal file), these values must be read from the
|
| + ** wal-file header. Otherwise, read them from the frame header of the
|
| + ** previous frame. */
|
| + assert( pWal->iReCksum>0 );
|
| + if( pWal->iReCksum==1 ){
|
| + iCksumOff = 24;
|
| + }else{
|
| + iCksumOff = walFrameOffset(pWal->iReCksum-1, szPage) + 16;
|
| + }
|
| + rc = sqlite3OsRead(pWal->pWalFd, aBuf, sizeof(u32)*2, iCksumOff);
|
| + pWal->hdr.aFrameCksum[0] = sqlite3Get4byte(aBuf);
|
| + pWal->hdr.aFrameCksum[1] = sqlite3Get4byte(&aBuf[sizeof(u32)]);
|
| +
|
| + iRead = pWal->iReCksum;
|
| + pWal->iReCksum = 0;
|
| + for(; rc==SQLITE_OK && iRead<=iLast; iRead++){
|
| + i64 iOff = walFrameOffset(iRead, szPage);
|
| + rc = sqlite3OsRead(pWal->pWalFd, aBuf, szPage+WAL_FRAME_HDRSIZE, iOff);
|
| + if( rc==SQLITE_OK ){
|
| + u32 iPgno, nDbSize;
|
| + iPgno = sqlite3Get4byte(aBuf);
|
| + nDbSize = sqlite3Get4byte(&aBuf[4]);
|
| +
|
| + walEncodeFrame(pWal, iPgno, nDbSize, &aBuf[WAL_FRAME_HDRSIZE], aFrame);
|
| + rc = sqlite3OsWrite(pWal->pWalFd, aFrame, sizeof(aFrame), iOff);
|
| + }
|
| + }
|
| +
|
| + sqlite3_free(aBuf);
|
| + return rc;
|
| +}
|
| +
|
| /*
|
| ** Write a set of frames to the log. The caller must hold the write-lock
|
| ** on the log file (obtained using sqlite3WalBeginWriteTransaction()).
|
| @@ -2905,6 +3054,8 @@ int sqlite3WalFrames(
|
| int szFrame; /* The size of a single frame */
|
| i64 iOffset; /* Next byte to write in WAL file */
|
| WalWriter w; /* The writer */
|
| + u32 iFirst = 0; /* First frame that may be overwritten */
|
| + WalIndexHdr *pLive; /* Pointer to shared header */
|
|
|
| assert( pList );
|
| assert( pWal->writeLock );
|
| @@ -2920,6 +3071,11 @@ int sqlite3WalFrames(
|
| }
|
| #endif
|
|
|
| + pLive = (WalIndexHdr*)walIndexHdr(pWal);
|
| + if( memcmp(&pWal->hdr, (void *)pLive, sizeof(WalIndexHdr))!=0 ){
|
| + iFirst = pLive->mxFrame+1;
|
| + }
|
| +
|
| /* See if it is possible to write these frames into the start of the
|
| ** log file, instead of appending to it at pWal->hdr.mxFrame.
|
| */
|
| @@ -2984,6 +3140,33 @@ int sqlite3WalFrames(
|
| /* Write all frames into the log file exactly once */
|
| for(p=pList; p; p=p->pDirty){
|
| int nDbSize; /* 0 normally. Positive == commit flag */
|
| +
|
| + /* Check if this page has already been written into the wal file by
|
| + ** the current transaction. If so, overwrite the existing frame and
|
| + ** set Wal.writeLock to WAL_WRITELOCK_RECKSUM - indicating that
|
| + ** checksums must be recomputed when the transaction is committed. */
|
| + if( iFirst && (p->pDirty || isCommit==0) ){
|
| + u32 iWrite = 0;
|
| + VVA_ONLY(rc =) sqlite3WalFindFrame(pWal, p->pgno, &iWrite);
|
| + assert( rc==SQLITE_OK || iWrite==0 );
|
| + if( iWrite>=iFirst ){
|
| + i64 iOff = walFrameOffset(iWrite, szPage) + WAL_FRAME_HDRSIZE;
|
| + void *pData;
|
| + if( pWal->iReCksum==0 || iWrite<pWal->iReCksum ){
|
| + pWal->iReCksum = iWrite;
|
| + }
|
| +#if defined(SQLITE_HAS_CODEC)
|
| + if( (pData = sqlite3PagerCodec(p))==0 ) return SQLITE_NOMEM;
|
| +#else
|
| + pData = p->pData;
|
| +#endif
|
| + rc = sqlite3OsWrite(pWal->pWalFd, pData, szPage, iOff);
|
| + if( rc ) return rc;
|
| + p->flags &= ~PGHDR_WAL_APPEND;
|
| + continue;
|
| + }
|
| + }
|
| +
|
| iFrame++;
|
| assert( iOffset==walFrameOffset(iFrame, szPage) );
|
| nDbSize = (isCommit && p->pDirty==0) ? nTruncate : 0;
|
| @@ -2991,6 +3174,13 @@ int sqlite3WalFrames(
|
| if( rc ) return rc;
|
| pLast = p;
|
| iOffset += szFrame;
|
| + p->flags |= PGHDR_WAL_APPEND;
|
| + }
|
| +
|
| + /* Recalculate checksums within the wal file if required. */
|
| + if( isCommit && pWal->iReCksum ){
|
| + rc = walRewriteChecksums(pWal, iFrame);
|
| + if( rc ) return rc;
|
| }
|
|
|
| /* If this is the end of a transaction, then we might need to pad
|
| @@ -3008,16 +3198,21 @@ int sqlite3WalFrames(
|
| ** past the sector boundary is written after the sync.
|
| */
|
| if( isCommit && (sync_flags & WAL_SYNC_TRANSACTIONS)!=0 ){
|
| + int bSync = 1;
|
| if( pWal->padToSectorBoundary ){
|
| int sectorSize = sqlite3SectorSize(pWal->pWalFd);
|
| w.iSyncPoint = ((iOffset+sectorSize-1)/sectorSize)*sectorSize;
|
| + bSync = (w.iSyncPoint==iOffset);
|
| + testcase( bSync );
|
| while( iOffset<w.iSyncPoint ){
|
| rc = walWriteOneFrame(&w, pLast, nTruncate, iOffset);
|
| if( rc ) return rc;
|
| iOffset += szFrame;
|
| nExtra++;
|
| }
|
| - }else{
|
| + }
|
| + if( bSync ){
|
| + assert( rc==SQLITE_OK );
|
| rc = sqlite3OsSync(w.pFd, sync_flags & SQLITE_SYNC_MASK);
|
| }
|
| }
|
| @@ -3042,6 +3237,7 @@ int sqlite3WalFrames(
|
| */
|
| iFrame = pWal->hdr.mxFrame;
|
| for(p=pList; p && rc==SQLITE_OK; p=p->pDirty){
|
| + if( (p->flags & PGHDR_WAL_APPEND)==0 ) continue;
|
| iFrame++;
|
| rc = walIndexAppend(pWal, iFrame, p->pgno);
|
| }
|
| @@ -3084,6 +3280,7 @@ int sqlite3WalFrames(
|
| */
|
| int sqlite3WalCheckpoint(
|
| Wal *pWal, /* Wal connection */
|
| + sqlite3 *db, /* Check this handle's interrupt flag */
|
| int eMode, /* PASSIVE, FULL, RESTART, or TRUNCATE */
|
| int (*xBusy)(void*), /* Function to call when busy */
|
| void *pBusyArg, /* Context argument for xBusyHandler */
|
| @@ -3154,10 +3351,11 @@ int sqlite3WalCheckpoint(
|
|
|
| /* Copy data from the log to the database file. */
|
| if( rc==SQLITE_OK ){
|
| +
|
| if( pWal->hdr.mxFrame && walPagesize(pWal)!=nBuf ){
|
| rc = SQLITE_CORRUPT_BKPT;
|
| }else{
|
| - rc = walCheckpoint(pWal, eMode2, xBusy2, pBusyArg, sync_flags, zBuf);
|
| + rc = walCheckpoint(pWal, db, eMode2, xBusy2, pBusyArg, sync_flags, zBuf);
|
| }
|
|
|
| /* If no error occurred, set the output variables. */
|
| @@ -3277,12 +3475,17 @@ int sqlite3WalHeapMemory(Wal *pWal){
|
| int sqlite3WalSnapshotGet(Wal *pWal, sqlite3_snapshot **ppSnapshot){
|
| int rc = SQLITE_OK;
|
| WalIndexHdr *pRet;
|
| + static const u32 aZero[4] = { 0, 0, 0, 0 };
|
|
|
| assert( pWal->readLock>=0 && pWal->writeLock==0 );
|
|
|
| + if( memcmp(&pWal->hdr.aFrameCksum[0],aZero,16)==0 ){
|
| + *ppSnapshot = 0;
|
| + return SQLITE_ERROR;
|
| + }
|
| pRet = (WalIndexHdr*)sqlite3_malloc(sizeof(WalIndexHdr));
|
| if( pRet==0 ){
|
| - rc = SQLITE_NOMEM;
|
| + rc = SQLITE_NOMEM_BKPT;
|
| }else{
|
| memcpy(pRet, &pWal->hdr, sizeof(WalIndexHdr));
|
| *ppSnapshot = (sqlite3_snapshot*)pRet;
|
| @@ -3296,6 +3499,23 @@ int sqlite3WalSnapshotGet(Wal *pWal, sqlite3_snapshot **ppSnapshot){
|
| void sqlite3WalSnapshotOpen(Wal *pWal, sqlite3_snapshot *pSnapshot){
|
| pWal->pSnapshot = (WalIndexHdr*)pSnapshot;
|
| }
|
| +
|
| +/*
|
| +** Return a +ve value if snapshot p1 is newer than p2. A -ve value if
|
| +** p1 is older than p2 and zero if p1 and p2 are the same snapshot.
|
| +*/
|
| +int sqlite3_snapshot_cmp(sqlite3_snapshot *p1, sqlite3_snapshot *p2){
|
| + WalIndexHdr *pHdr1 = (WalIndexHdr*)p1;
|
| + WalIndexHdr *pHdr2 = (WalIndexHdr*)p2;
|
| +
|
| + /* aSalt[0] is a copy of the value stored in the wal file header. It
|
| + ** is incremented each time the wal file is restarted. */
|
| + if( pHdr1->aSalt[0]<pHdr2->aSalt[0] ) return -1;
|
| + if( pHdr1->aSalt[0]>pHdr2->aSalt[0] ) return +1;
|
| + if( pHdr1->mxFrame<pHdr2->mxFrame ) return -1;
|
| + if( pHdr1->mxFrame>pHdr2->mxFrame ) return +1;
|
| + return 0;
|
| +}
|
| #endif /* SQLITE_ENABLE_SNAPSHOT */
|
|
|
| #ifdef SQLITE_ENABLE_ZIPVFS
|
|
|