On an UPDATE, try to overwrite an existing btree cell with the modified

content, if the old and new cell are the same size.  Use memcmp() first
to avoid dirtying pages that are unchanged.

FossilOrigin-Name: 5887d8beb502ad62689d31b850f46ab50831a1e9db36adf20d55ad45619d207e
This commit is contained in:
drh 2018-05-07 11:48:22 +00:00
commit e3c05a5597
4 changed files with 106 additions and 9 deletions

View File

@ -1,5 +1,5 @@
C Fix\sharmless\scompiler\swarnings\sassociated\swith\sthe\snew\sEXPLAIN\sQUERY\sPLAN\slogic.
D 2018-05-07T11:37:34.277
C On\san\sUPDATE,\stry\sto\soverwrite\san\sexisting\sbtree\scell\swith\sthe\smodified\ncontent,\sif\sthe\sold\sand\snew\scell\sare\sthe\ssame\ssize.\s\sUse\smemcmp()\sfirst\nto\savoid\sdirtying\spages\sthat\sare\sunchanged.
D 2018-05-07T11:48:22.641
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
F Makefile.in 5ce9343cba9c189046f1afe6d2bcc1f68079439febc05267b98aec6ecc752439
@ -434,7 +434,7 @@ F src/auth.c 6277d63837357549fe14e723490d6dc1a38768d71c795c5eb5c0f8a99f918f73
F src/backup.c faf17e60b43233c214aae6a8179d24503a61e83b
F src/bitvec.c 17ea48eff8ba979f1f5b04cc484c7bb2be632f33
F src/btmutex.c 8acc2f464ee76324bf13310df5692a262b801808984c1b79defb2503bbafadb6
F src/btree.c 79982b0779c27e5ccde8c3e540f14b4e82586d6ec0394a7eb08be72d20c3e894
F src/btree.c 6be0267a7da8ca04c466094ca9d9a8a26333e175cff794afe5138aeec0cdb7c2
F src/btree.h 0866c0a08255142ea0e754aabd211c843cab32045c978a592a43152405ed0c84
F src/btreeInt.h 620ab4c7235f43572cf3ac2ac8723cbdf68073be4d29da24897c7b77dda5fd96
F src/build.c 0c2be5839f22aa2938f217c6c6c2120d9fc96872a546a37541a8271541cb355e
@ -1131,7 +1131,7 @@ F test/oserror.test b32dc34f2363ef18532e3a0a7358e3e7e321974f
F test/ossfuzz.c c4c4547e2c92ac52f10038b073a03248251a23c1c559728f63a18aeca0e79f03
F test/ossshell.c f125c5bd16e537a2549aa579b328dd1c59905e7ab1338dfc210e755bb7b69f17
F test/ovfl.test 199c482696defceacee8c8e0e0ef36da62726b2f
F test/pager1.test f596d3bd53ce96e1d87d44d223d2ae6c8867dd782c425e5eb28b5721fa6aaa97
F test/pager1.test a32ce299ed01ffb06e84a3af467ae1f3389786b316f40c4359f442c79144736b
F test/pager2.test 67b8f40ae98112bcdba1f2b2d03ea83266418c71
F test/pager3.test 4e9a83d6ca0838d7c602c9eb93d1357562d9059c1e02ffb138a8271020838370
F test/pager4.test a122e9e6925d5b23b31e3dfef8c6a44bbf19590e
@ -1727,7 +1727,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
P b348d1193a7a3ed4d3e656b6cc95a41f87eae29222e0723850a5eb5ffabffd00
R d09bdc73f56d4d80e2806c4c66a4e25a
P 374d8e264487b0437a8d995ced1bc026a92d495a2d0568f65f033e9ebe11d0e2 3e11dc3183bc3e8ec49af244a8e8b3e07d12f7a2e59028b2bf64ce0ab589a91f
R 891ce8c2c78f1f487f80460b8c248fd0
T +closed 3e11dc3183bc3e8ec49af244a8e8b3e07d12f7a2e59028b2bf64ce0ab589a91f
U drh
Z 7e3b1491b97d0e22d20f39d7466da090
Z 7955ef5d032aea210d99e8bc24e906bc

View File

@ -1 +1 @@
374d8e264487b0437a8d995ced1bc026a92d495a2d0568f65f033e9ebe11d0e2
5887d8beb502ad62689d31b850f46ab50831a1e9db36adf20d55ad45619d207e

View File

@ -8154,6 +8154,94 @@ static int balance(BtCursor *pCur){
return rc;
}
/* Overwrite content from pX into pDest. Only do the write if the
** content is different from what is already there.
*/
static int btreeOverwriteContent(
MemPage *pPage, /* MemPage on which writing will occur */
u8 *pDest, /* Pointer to the place to start writing */
const BtreePayload *pX, /* Source of data to write */
int iOffset, /* Offset of first byte to write */
int iAmt /* Number of bytes to be written */
){
int nData = pX->nData - iOffset;
if( nData<=0 ){
/* Overwritting with zeros */
int i;
for(i=0; i<iAmt && pDest[i]==0; i++){}
if( i<iAmt ){
int rc = sqlite3PagerWrite(pPage->pDbPage);
if( rc ) return rc;
memset(pDest + i, 0, iAmt - i);
}
}else{
if( nData<iAmt ){
/* Mixed read data and zeros at the end. Make a recursive call
** to write the zeros then fall through to write the real data */
int rc = btreeOverwriteContent(pPage, pDest+nData, pX, iOffset+nData,
iAmt-nData);
if( rc ) return rc;
iAmt = nData;
}
if( memcmp(pDest, ((u8*)pX->pData) + iOffset, iAmt)!=0 ){
int rc = sqlite3PagerWrite(pPage->pDbPage);
if( rc ) return rc;
memcpy(pDest, ((u8*)pX->pData) + iOffset, iAmt);
}
}
return SQLITE_OK;
}
/*
** Overwrite the cell that cursor pCur is pointing to with fresh content
** contained in pX.
*/
static int btreeOverwriteCell(BtCursor *pCur, const BtreePayload *pX){
int iOffset; /* Next byte of pX->pData to write */
int nTotal = pX->nData + pX->nZero; /* Total bytes of to write */
int rc; /* Return code */
MemPage *pPage = pCur->pPage; /* Page being written */
BtShared *pBt; /* Btree */
Pgno ovflPgno; /* Next overflow page to write */
u32 ovflPageSize; /* Size to write on overflow page */
if( pCur->info.pPayload + pCur->info.nLocal > pPage->aDataEnd ){
return SQLITE_CORRUPT_BKPT;
}
/* Overwrite the local portion first */
rc = btreeOverwriteContent(pPage, pCur->info.pPayload, pX,
0, pCur->info.nLocal);
if( rc ) return rc;
if( pCur->info.nLocal==nTotal ) return SQLITE_OK;
/* Now overwrite the overflow pages */
iOffset = pCur->info.nLocal;
assert( nTotal>=0 );
assert( iOffset>=0 );
ovflPgno = get4byte(pCur->info.pPayload + iOffset);
pBt = pPage->pBt;
ovflPageSize = pBt->usableSize - 4;
do{
rc = btreeGetPage(pBt, ovflPgno, &pPage, 0);
if( rc ) return rc;
if( sqlite3PagerPageRefcount(pPage->pDbPage)!=1 ){
rc = SQLITE_CORRUPT_BKPT;
}else{
if( iOffset+ovflPageSize<(u32)nTotal ){
ovflPgno = get4byte(pPage->aData);
}else{
ovflPageSize = nTotal - iOffset;
}
rc = btreeOverwriteContent(pPage, pPage->aData+4, pX,
iOffset, ovflPageSize);
}
sqlite3PagerUnref(pPage->pDbPage);
if( rc ) return rc;
iOffset += ovflPageSize;
}while( iOffset<nTotal );
return SQLITE_OK;
}
/*
** Insert a new record into the BTree. The content of the new record
@ -8252,6 +8340,14 @@ int sqlite3BtreeInsert(
** new row onto the end, set the "loc" to avoid an unnecessary
** btreeMoveto() call */
if( (pCur->curFlags&BTCF_ValidNKey)!=0 && pX->nKey==pCur->info.nKey ){
/* The current is currently pointing to the entry that is to be
** overwritten */
assert( pX->nData>=0 && pX->nZero>=0 );
if( pCur->info.nSize!=0
&& pCur->info.nPayload==(u32)pX->nData+pX->nZero
){
return btreeOverwriteCell(pCur, pX);
}
loc = 0;
}else if( loc==0 ){
rc = sqlite3BtreeMovetoUnpacked(pCur, 0, pX->nKey, flags!=0, &loc);

View File

@ -1149,7 +1149,7 @@ do_test pager1-5.5.1 {
PRAGMA journal_mode = PERSIST;
CREATE TABLE t3(a, b);
INSERT INTO t3 SELECT randomblob(1500), randomblob(1500) FROM t1;
UPDATE t3 SET b = randomblob(1500);
UPDATE t3 SET b = randomblob(1501);
}
expr [file size test.db-journal] > 15000
} {1}