1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-08-07 02:42:48 +03:00

If a single page is written to the wal file more than once, have each subsequent copy overwrite the original frame.

FossilOrigin-Name: 5d113aef2c7d746e8eda88d4e36c04a39b0a11be
This commit is contained in:
dan
2016-01-09 16:39:29 +00:00
parent a829992969
commit d6f7c97952
8 changed files with 230 additions and 14 deletions

View File

@@ -462,6 +462,13 @@ struct Wal {
#define WAL_EXCLUSIVE_MODE 1
#define WAL_HEAPMEMORY_MODE 2
/*
** Values for Wal.writeLock.
*/
#define WAL_WRITELOCK_UNLOCKED 0
#define WAL_WRITELOCK_LOCKED 1
#define WAL_WRITELOCK_RECKSUM 2
/*
** Possible values for WAL.readOnly
*/
@@ -2885,6 +2892,60 @@ 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.
**
** Argument pLive is a pointer to the first wal-index header in shared
** memory (the copy readers will see if they open a read-transaction now,
** before the current commit is finished). This is safe to use because the
** caller holds the WRITER lock. The first frame to update the checksum
** for is (pLive->mxFrame+1). The last is argument iLast.
**
** SQLITE_OK is returned if successful, or an SQLite error code otherwise.
*/
static int walRewriteChecksums(Wal *pWal, WalIndexHdr *pLive, 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 */
aBuf = sqlite3_malloc(szPage + WAL_FRAME_HDRSIZE);
if( aBuf==0 ) return SQLITE_NOMEM;
/* Find the checksum values to use as input for the checksum of the
** first frame written by this transaction. If that frame is frame 1
** (implying that the current transaction restarted the wal file),
** these values must be read from the wal-file header. If the first
** frame to update the checksum of is not frame 1, then the initial
** checksum values can be copied from pLive. */
if( pLive->mxFrame==0 ){
rc = sqlite3OsRead(pWal->pWalFd, aBuf, sizeof(u32)*2, 24);
pWal->hdr.aFrameCksum[0] = sqlite3Get4byte(aBuf);
pWal->hdr.aFrameCksum[1] = sqlite3Get4byte(&aBuf[sizeof(u32)]);
}else{
memcpy(pWal->hdr.aFrameCksum, pLive->aFrameCksum, sizeof(u32)*2);
}
for(iRead=pLive->mxFrame+1; 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 +2966,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 +2983,13 @@ int sqlite3WalFrames(
}
#endif
pLive = (WalIndexHdr*)walIndexHdr(pWal);
if( memcmp(&pWal->hdr, (void *)pLive, sizeof(WalIndexHdr))!=0
&& (isCommit || sqlite3PagerSavepointCount(pList->pPager)==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 +3054,25 @@ 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;
rc = sqlite3WalFindFrame(pWal, p->pgno, &iWrite);
if( rc ) return rc;
if( iWrite>=iFirst ){
i64 iOff = walFrameOffset(iWrite, szPage) + WAL_FRAME_HDRSIZE;
pWal->writeLock = WAL_WRITELOCK_RECKSUM;
rc = sqlite3OsWrite(pWal->pWalFd, p->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 +3080,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->writeLock==WAL_WRITELOCK_RECKSUM ){
rc = walRewriteChecksums(pWal, pLive, iFrame);
if( rc ) return rc;
}
/* If this is the end of a transaction, then we might need to pad
@@ -3042,6 +3138,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);
}
@@ -3154,6 +3251,7 @@ 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{