Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ce876a49f5 | |||
| 21ff59fa46 |
@@ -1,5 +1,5 @@
|
||||
C Fix\sa\sproblem\sthat\scould\scause\sa\sspurious\sSQLITE_NOMEM\serror\swhen\sattempting\nto\sresume\san\sRBU\soperation\sif\sthe\sprevious\sclient\sfailed\sright\safter\ncompleting\sthe\sincremental\scheckpoint.\sAlso\sa\s"cannot\svacuum\swal\sdb"\serror\nthat\scould\soccur\swhen\sresuming\san\sRBU\svacuum\sif\san\serror\s(OOM\sor\sIO\serror)\noccurs\sduring\sthe\sincremental\scheckpoint.
|
||||
D 2017-01-17T10:41:42.780
|
||||
C Add\stest\scases\sfor\sthe\sinstrumentation\son\sthis\sbranch.\sFix\ssome\sOOM\shandling\nissues\sin\sthe\ssame.
|
||||
D 2017-01-19T11:52:13.715
|
||||
F Makefile.in 41bd4cad981487345c4a84081074bcdb876e4b2e
|
||||
F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434
|
||||
F Makefile.msc b8ca53350ae545e3562403d5da2a69cec79308da
|
||||
@@ -331,11 +331,11 @@ F src/analyze.c 317dbaf31c16050582b09bf4f323b4e0f1813251
|
||||
F src/attach.c 8c476f8bd5d2afe11d925f890d30e527e5b0ce43
|
||||
F src/auth.c 930b376a9c56998557367e6f7f8aaeac82a2a792
|
||||
F src/backup.c faf17e60b43233c214aae6a8179d24503a61e83b
|
||||
F src/bitvec.c 17ea48eff8ba979f1f5b04cc484c7bb2be632f33
|
||||
F src/bitvec.c 263395e75ac122a7138f0797172868be700cf22a
|
||||
F src/btmutex.c 0e9ce2d56159b89b9bc8e197e023ee11e39ff8ca
|
||||
F src/btree.c 69966fb2c574954cd3216f09407c9a02c52d3bd7
|
||||
F src/btree.h e6d352808956ec163a17f832193a3e198b3fb0ac
|
||||
F src/btreeInt.h 10c4b77c2fb399580babbcc7cf652ac10dba796e
|
||||
F src/btree.c 9e3d38d2d93384c6d8b765b39c0c80b6a8a6dacd
|
||||
F src/btree.h a25d8121baf921ed56cdf5bb65a37233b4fa059a
|
||||
F src/btreeInt.h d528379e5da9aa33ef046e3c210a5d82acd2740b
|
||||
F src/build.c 9e799f1edd910dfa8a0bc29bd390d35d310596af
|
||||
F src/callback.c 2e76147783386374bf01b227f752c81ec872d730
|
||||
F src/complete.c a3634ab1e687055cd002e11b8f43eb75c17da23e
|
||||
@@ -355,7 +355,7 @@ F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71
|
||||
F src/insert.c 05e47e2de7b712a3a4148cd469e5f60873f5ef13
|
||||
F src/legacy.c 75d3023be8f0d2b99d60f905090341a03358c58e
|
||||
F src/loadext.c 5d6642d141c07d366e43d359e94ec9de47add41d
|
||||
F src/main.c e207b81542d13b9f13d61e78ca441f9781f055b0
|
||||
F src/main.c 504a2a6cbd4596584fa8a5489bb5fa1c8a8ee226
|
||||
F src/malloc.c fc1b9f445290f2145da48fc08730c26e6082b640
|
||||
F src/mem0.c 6a55ebe57c46ca1a7d98da93aaa07f99f1059645
|
||||
F src/mem1.c 6919bcf12f221868ea066eec27e579fed95ce98b
|
||||
@@ -377,8 +377,8 @@ F src/os_setup.h 0dbaea40a7d36bf311613d31342e0b99e2536586
|
||||
F src/os_unix.c 30e2c43e4955db990e5b5a81e901f8aa74cc8820
|
||||
F src/os_win.c cf90abd4e50d9f56d2c20ce8e005aff55d7bd8e9
|
||||
F src/os_win.h 7b073010f1451abe501be30d12f6bc599824944a
|
||||
F src/pager.c 9dc72d23eebbdf992bd69f2ab954d0d3a27c7340
|
||||
F src/pager.h d1e944291030351f362a0a7da9b5c3e34e603e39
|
||||
F src/pager.c fcdfc9ef832281c49870ac703ecb317fbea93891
|
||||
F src/pager.h f15b4fe1c773ba428d9c983ddac342b30a99a8f4
|
||||
F src/parse.y 29153738a7322054359320eb00b5a4cd44389f20
|
||||
F src/pcache.c 51070ec9b8251bbf9c6ea3d35fd96a458752929e
|
||||
F src/pcache.h 2cedcd8407eb23017d92790b112186886e179490
|
||||
@@ -392,15 +392,15 @@ F src/resolve.c bb070cf5f23611c44ab7e4788803684e385fc3fb
|
||||
F src/rowset.c 7b7e7e479212e65b723bf40128c7b36dc5afdfac
|
||||
F src/select.c 3856db523b942062bca8722ba03b61c324ff94d6
|
||||
F src/shell.c 6095531aa900decdaa765e0f3993fba7153c92c1
|
||||
F src/sqlite.h.in e71655293c9bde26939496f3aac9d1821d2c07a2
|
||||
F src/sqlite.h.in b0e0ef521c53afeff0c5fbdacc166dd92f7963bb
|
||||
F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
|
||||
F src/sqlite3ext.h 8648034aa702469afb553231677306cc6492a1ae
|
||||
F src/sqliteInt.h ce3e07c720b0cebc8887ea86b3b128da0913c5d3
|
||||
F src/sqliteInt.h 7a4cf01ef33c0df033104b4caaf97c59497624f9
|
||||
F src/sqliteLimit.h c0373387c287c8d0932510b5547ecde31b5da247
|
||||
F src/status.c a9e66593dfb28a9e746cba7153f84d49c1ddc4b1
|
||||
F src/table.c 5226df15ab9179b9ed558d89575ea0ce37b03fc9
|
||||
F src/tclsqlite.c 418f5e5e0840425a7e5b33f3600dccd378a57549
|
||||
F src/test1.c 8a98191a1da8e100f77cdb5cc716df67d405028d
|
||||
F src/test1.c ef72568ab6eebf1691706e9b29ea2ab7fd5481d8
|
||||
F src/test2.c 3efb99ab7f1fc8d154933e02ae1378bac9637da5
|
||||
F src/test3.c d03f5b5da9a2410b7a91c64b0d3306ed28ab6fee
|
||||
F src/test4.c 18ec393bb4d0ad1de729f0b94da7267270f3d8e6
|
||||
@@ -1318,6 +1318,7 @@ F test/tkt3992.test f3e7d548ac26f763b47bc0f750da3d03c81071da
|
||||
F test/tkt3997.test a335fa41ca3985660a139df7b734a26ef53284bd
|
||||
F test/tkt4018.test 18dbc6617f7a4b90e938d1bd6d26ad18daafaf08
|
||||
F test/tokenize.test ce430a7aed48fc98301611429595883fdfcab5d7
|
||||
F test/tpages.test 20b7cae49b46fae6aacdd14a44f531a5a1fab33f
|
||||
F test/tpch01.test 04adbf8d8300fa60a222f28d901abd76e7be6dd4
|
||||
F test/trace.test 6f676313e3ebd2a50585036d2f212a3319dd5836
|
||||
F test/trace2.test f5cb67ad3bc09e0c58e8cca78dfd0b5639259983
|
||||
@@ -1547,7 +1548,7 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
|
||||
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
|
||||
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
|
||||
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
|
||||
P 7fd560c6d2ff470b755ad118287a0a8825b3009e
|
||||
R 3b5f909693e02732236a5ff9d24ebb43
|
||||
P 2a8f6c890cfa5f61b491ddf8f560b6ba8c6cdc32
|
||||
R ae523458281bb3a9a28803814e720ed6
|
||||
U dan
|
||||
Z 623656530433c4c90deab7fe9f3d0d79
|
||||
Z 5b5cdeb9ebac248f4328063c950d744e
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
681d96eb822e606da53700867191d4738bda20c8
|
||||
50ca94b919ccb462f523513fd7fe99957f0b9204
|
||||
@@ -293,6 +293,63 @@ u32 sqlite3BitvecSize(Bitvec *p){
|
||||
return p->iSize;
|
||||
}
|
||||
|
||||
int bitvecAppendArrayElem(int *pnAlloc, int *pnElem, u32 **paElem, u32 iNew){
|
||||
if( *pnElem==*pnAlloc ){
|
||||
int nNew = *pnAlloc ? (*pnAlloc)*2 : 128;
|
||||
u32 *aNew;
|
||||
aNew = sqlite3_realloc(*paElem, nNew*sizeof(u32));
|
||||
if( aNew==0 ){
|
||||
sqlite3_free(*paElem);
|
||||
*paElem = 0;
|
||||
return SQLITE_NOMEM;
|
||||
}
|
||||
*paElem = aNew;
|
||||
*pnAlloc = nNew;
|
||||
}
|
||||
|
||||
(*paElem)[(*pnElem)++] = iNew;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
#ifdef SQLITE_ENABLE_TRANSACTION_PAGES
|
||||
int bitvecToArray(Bitvec *p, int iOff, int *pnAlloc, int *pnElem, u32 **paElem){
|
||||
int rc = SQLITE_OK;
|
||||
int i;
|
||||
if( p->iDivisor ){
|
||||
for(i=0; rc==SQLITE_OK && i<BITVEC_NPTR; i++){
|
||||
if( p->u.apSub[i] ){
|
||||
int iOff2 = iOff + i*p->iDivisor;
|
||||
rc = bitvecToArray(p->u.apSub[i], iOff2, pnAlloc, pnElem, paElem);
|
||||
}
|
||||
}
|
||||
}else{
|
||||
if( p->iSize<=BITVEC_NBIT ){
|
||||
for(i=0; rc==SQLITE_OK && i<BITVEC_NBIT; i++){
|
||||
if( p->u.aBitmap[i/BITVEC_SZELEM] & (1<<(i&(BITVEC_SZELEM-1))) ){
|
||||
rc = bitvecAppendArrayElem(pnAlloc, pnElem, paElem, i+iOff);
|
||||
}
|
||||
}
|
||||
}else{
|
||||
for(i=0; rc==SQLITE_OK && i<BITVEC_NINT; i++){
|
||||
u32 iVal = p->u.aHash[i];
|
||||
if( iVal ){
|
||||
rc = bitvecAppendArrayElem(pnAlloc, pnElem, paElem, iVal-1+iOff);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int sqlite3BitvecToArray(Bitvec *p, int *pnElem, u32 **paElem){
|
||||
int nAlloc = 0;
|
||||
*pnElem = 0;
|
||||
*paElem = 0;
|
||||
return bitvecToArray(p, 1, &nAlloc, pnElem, paElem);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef SQLITE_UNTESTABLE
|
||||
/*
|
||||
** Let V[] be an array of unsigned characters sufficient to hold
|
||||
|
||||
+124
-10
@@ -2026,6 +2026,19 @@ static int getAndInitPage(
|
||||
releasePage(*ppPage);
|
||||
goto getAndInitPage_error;
|
||||
}
|
||||
|
||||
#ifdef SQLITE_ENABLE_TRANSACTION_PAGES
|
||||
if( pBt->inTransaction==TRANS_WRITE
|
||||
&& pgno<=sqlite3BitvecSize(pBt->pBtRead)
|
||||
){
|
||||
rc = sqlite3BitvecSet(pBt->pBtRead, pgno);
|
||||
if( rc!=SQLITE_OK ){
|
||||
releasePage(*ppPage);
|
||||
goto getAndInitPage_error;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return SQLITE_OK;
|
||||
|
||||
getAndInitPage_error:
|
||||
@@ -3106,6 +3119,27 @@ int sqlite3BtreeNewDb(Btree *p){
|
||||
return rc;
|
||||
}
|
||||
|
||||
#ifdef SQLITE_ENABLE_TRANSACTION_PAGES
|
||||
/*
|
||||
** If the b-tree is not currently in a write transaction, free the various
|
||||
** resources allocated for the sqlite3_transaction_pages() functionality.
|
||||
*/
|
||||
static void freeTransactionPagesBitvec(BtShared *pBt){
|
||||
if( pBt->inTransaction!=TRANS_WRITE ){
|
||||
sqlite3BitvecDestroy(pBt->pBtRead);
|
||||
sqlite3BitvecDestroy(pBt->pBtWrite);
|
||||
sqlite3BitvecDestroy(pBt->pBtAlloc);
|
||||
pBt->pBtAlloc = pBt->pBtRead = pBt->pBtWrite = 0;
|
||||
sqlite3_free(pBt->aiRead);
|
||||
sqlite3_free(pBt->aiWrite);
|
||||
pBt->aiRead = pBt->aiWrite = 0;
|
||||
pBt->nRead = pBt->nWrite = 0;
|
||||
}
|
||||
}
|
||||
#else
|
||||
# define freeTransactionPagesBitvec(x)
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Attempt to start a new transaction. A write-transaction
|
||||
** is started if the second argument is nonzero, otherwise a read-
|
||||
@@ -3191,6 +3225,20 @@ int sqlite3BtreeBeginTrans(Btree *p, int wrflag){
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_ENABLE_TRANSACTION_PAGES
|
||||
if( wrflag ){
|
||||
assert( pBt->pBtRead==0 && pBt->pBtWrite==0 && pBt->pBtAlloc==0 );
|
||||
assert( rc==SQLITE_OK );
|
||||
pBt->pBtRead = sqlite3BitvecCreate(pBt->nPage);
|
||||
pBt->pBtWrite = sqlite3BitvecCreate(pBt->nPage);
|
||||
pBt->pBtAlloc = sqlite3BitvecCreate(pBt->nPage);
|
||||
if( pBt->pBtRead==0 || pBt->pBtWrite==0 || pBt->pBtAlloc==0 ){
|
||||
rc = SQLITE_NOMEM;
|
||||
goto trans_begun;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Any read-only or read-write transaction implies a read-lock on
|
||||
** page 1. So if some other shared-cache client already has a write-lock
|
||||
** on page 1, the transaction cannot be opened. */
|
||||
@@ -3266,7 +3314,6 @@ int sqlite3BtreeBeginTrans(Btree *p, int wrflag){
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
trans_begun:
|
||||
if( rc==SQLITE_OK && wrflag ){
|
||||
/* This call makes sure that the pager has the correct number of
|
||||
@@ -3276,6 +3323,7 @@ trans_begun:
|
||||
rc = sqlite3PagerOpenSavepoint(pBt->pPager, p->db->nSavepoint);
|
||||
}
|
||||
|
||||
freeTransactionPagesBitvec(pBt);
|
||||
btreeIntegrity(p);
|
||||
sqlite3BtreeLeave(p);
|
||||
return rc;
|
||||
@@ -3790,6 +3838,7 @@ static void btreeEndTransaction(Btree *p){
|
||||
unlockBtreeIfUnused(pBt);
|
||||
}
|
||||
|
||||
freeTransactionPagesBitvec(pBt);
|
||||
btreeIntegrity(p);
|
||||
}
|
||||
|
||||
@@ -4417,6 +4466,24 @@ static int copyPayload(
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Call PagerWrite() on pager page pDbPage. And, if the page is currently
|
||||
** in the pBtRead bit vector, add it to pBtWrite as well.
|
||||
*/
|
||||
static int pagerWrite(BtShared *pBt, DbPage *pDbPage){
|
||||
Pgno pgno = sqlite3PagerPagenumber(pDbPage);
|
||||
int rc = SQLITE_OK;
|
||||
#ifdef SQLITE_ENABLE_TRANSACTION_PAGES
|
||||
if( sqlite3BitvecTestNotNull(pBt->pBtRead, pgno) ){
|
||||
rc = sqlite3BitvecSet(pBt->pBtWrite, pgno);
|
||||
}
|
||||
#endif
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3PagerWrite(pDbPage);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** This function is used to read or overwrite payload information
|
||||
** for the entry that the pCur cursor is pointing to. The eOp
|
||||
@@ -4493,7 +4560,14 @@ static int accessPayload(
|
||||
if( a+offset>pCur->info.nLocal ){
|
||||
a = pCur->info.nLocal - offset;
|
||||
}
|
||||
rc = copyPayload(&aPayload[offset], pBuf, a, (eOp & 0x01), pPage->pDbPage);
|
||||
#ifdef SQLITE_ENABLE_TRANSACTION_PAGES
|
||||
if( eOp & 0x01 ){
|
||||
rc = pagerWrite(pBt, pPage->pDbPage);
|
||||
}
|
||||
#endif
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = copyPayload(&aPayload[offset], pBuf, a, (eOp&0x01), pPage->pDbPage);
|
||||
}
|
||||
offset = 0;
|
||||
pBuf += a;
|
||||
amt -= a;
|
||||
@@ -5830,6 +5904,16 @@ static int allocateBtreePage(
|
||||
|
||||
assert( *pPgno!=PENDING_BYTE_PAGE(pBt) );
|
||||
|
||||
#ifdef SQLITE_ENABLE_TRANSACTION_PAGES
|
||||
if( rc==SQLITE_OK && *pPgno<sqlite3BitvecSize(pBt->pBtAlloc) ){
|
||||
rc = sqlite3BitvecSet(pBt->pBtAlloc, *pPgno);
|
||||
if( rc!=SQLITE_OK ){
|
||||
releasePage(*ppPage);
|
||||
*ppPage = 0;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
end_allocate_page:
|
||||
releasePage(pTrunk);
|
||||
releasePage(pPrevTrunk);
|
||||
@@ -6286,6 +6370,7 @@ static void dropCell(MemPage *pPage, int idx, int sz, int *pRC){
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Insert a new cell on pPage at cell index "i". pCell points to the
|
||||
** content of the cell.
|
||||
@@ -6351,7 +6436,7 @@ static void insertCell(
|
||||
assert( j==0 || pPage->aiOvfl[j-1]<(u16)i ); /* Overflows in sorted order */
|
||||
assert( j==0 || i==pPage->aiOvfl[j-1]+1 ); /* Overflows are sequential */
|
||||
}else{
|
||||
int rc = sqlite3PagerWrite(pPage->pDbPage);
|
||||
int rc = pagerWrite(pPage->pBt, pPage->pDbPage);
|
||||
if( rc!=SQLITE_OK ){
|
||||
*pRC = rc;
|
||||
return;
|
||||
@@ -7385,7 +7470,7 @@ static int balance_nonroot(
|
||||
if( i<nOld ){
|
||||
pNew = apNew[i] = apOld[i];
|
||||
apOld[i] = 0;
|
||||
rc = sqlite3PagerWrite(pNew->pDbPage);
|
||||
rc = pagerWrite(pBt, pNew->pDbPage);
|
||||
nNew++;
|
||||
if( rc ) goto balance_cleanup;
|
||||
}else{
|
||||
@@ -7755,7 +7840,7 @@ static int balance_deeper(MemPage *pRoot, MemPage **ppChild){
|
||||
** page that will become the new right-child of pPage. Copy the contents
|
||||
** of the node stored on pRoot into the new child page.
|
||||
*/
|
||||
rc = sqlite3PagerWrite(pRoot->pDbPage);
|
||||
rc = pagerWrite(pBt, pRoot->pDbPage);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = allocateBtreePage(pBt,&pChild,&pgnoChild,pRoot->pgno,0);
|
||||
copyNodeContent(pRoot, pChild, &rc);
|
||||
@@ -7837,7 +7922,7 @@ static int balance(BtCursor *pCur){
|
||||
MemPage * const pParent = pCur->apPage[iPage-1];
|
||||
int const iIdx = pCur->aiIdx[iPage-1];
|
||||
|
||||
rc = sqlite3PagerWrite(pParent->pDbPage);
|
||||
rc = pagerWrite(pParent->pBt, pParent->pDbPage);
|
||||
if( rc==SQLITE_OK ){
|
||||
#ifndef SQLITE_OMIT_QUICKBALANCE
|
||||
if( pPage->intKeyLeaf
|
||||
@@ -8058,7 +8143,7 @@ int sqlite3BtreeInsert(
|
||||
if( loc==0 ){
|
||||
CellInfo info;
|
||||
assert( idx<pPage->nCell );
|
||||
rc = sqlite3PagerWrite(pPage->pDbPage);
|
||||
rc = pagerWrite(pBt, pPage->pDbPage);
|
||||
if( rc ){
|
||||
goto end_insert;
|
||||
}
|
||||
@@ -8238,7 +8323,7 @@ int sqlite3BtreeDelete(BtCursor *pCur, u8 flags){
|
||||
/* Make the page containing the entry to be deleted writable. Then free any
|
||||
** overflow pages associated with the entry and finally remove the cell
|
||||
** itself from within the page. */
|
||||
rc = sqlite3PagerWrite(pPage->pDbPage);
|
||||
rc = pagerWrite(pBt, pPage->pDbPage);
|
||||
if( rc ) return rc;
|
||||
rc = clearCell(pPage, pCell, &info);
|
||||
dropCell(pPage, iCellIdx, info.nSize, &rc);
|
||||
@@ -8261,7 +8346,7 @@ int sqlite3BtreeDelete(BtCursor *pCur, u8 flags){
|
||||
assert( MX_CELL_SIZE(pBt) >= nCell );
|
||||
pTmp = pBt->pTmpSpace;
|
||||
assert( pTmp!=0 );
|
||||
rc = sqlite3PagerWrite(pLeaf->pDbPage);
|
||||
rc = pagerWrite(pBt, pLeaf->pDbPage);
|
||||
if( rc==SQLITE_OK ){
|
||||
insertCell(pPage, iCellIdx, pCell-4, nCell+4, pTmp, n, &rc);
|
||||
}
|
||||
@@ -8524,7 +8609,7 @@ static int clearDatabasePage(
|
||||
}
|
||||
if( freePageFlag ){
|
||||
freePage(pPage, &rc);
|
||||
}else if( (rc = sqlite3PagerWrite(pPage->pDbPage))==0 ){
|
||||
}else if( (rc = pagerWrite(pBt, pPage->pDbPage))==0 ){
|
||||
zeroPage(pPage, pPage->aData[hdr] | PTF_LEAF);
|
||||
}
|
||||
|
||||
@@ -9726,6 +9811,35 @@ int sqlite3BtreeIsReadonly(Btree *p){
|
||||
*/
|
||||
int sqlite3HeaderSizeBtree(void){ return ROUND8(sizeof(MemPage)); }
|
||||
|
||||
#ifdef SQLITE_ENABLE_TRANSACTION_PAGES
|
||||
int sqlite3BtreeTransactionPages(
|
||||
Btree *pBtree, /* Btree handle */
|
||||
int *pnRead, u32 **paiRead, /* OUT: Pages read */
|
||||
int *pnWrite, u32 **paiWrite /* OUT: Pages written */
|
||||
){
|
||||
int rc = SQLITE_OK;
|
||||
BtShared *pBt = pBtree->pBt;
|
||||
sqlite3BtreeEnter(pBtree);
|
||||
sqlite3_free(pBt->aiRead);
|
||||
sqlite3_free(pBt->aiWrite);
|
||||
pBt->nRead = pBt->nWrite = 0;
|
||||
pBt->aiRead = pBt->aiWrite = 0;
|
||||
if( pBtree->inTrans==TRANS_WRITE ){
|
||||
assert( pBt->inTransaction==TRANS_WRITE );
|
||||
rc = sqlite3BitvecToArray(pBt->pBtRead, &pBt->nRead, &pBt->aiRead);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3BitvecToArray(pBt->pBtWrite, &pBt->nWrite, &pBt->aiWrite);
|
||||
}
|
||||
}
|
||||
*pnRead = pBt->nRead;
|
||||
*paiRead = pBt->aiRead;
|
||||
*pnWrite = pBt->nWrite;
|
||||
*paiWrite = pBt->aiWrite;
|
||||
sqlite3BtreeLeave(pBtree);
|
||||
return rc;
|
||||
}
|
||||
#endif /* SQLITE_ENABLE_TRANSACTION_PAGES */
|
||||
|
||||
#if !defined(SQLITE_OMIT_SHARED_CACHE)
|
||||
/*
|
||||
** Return true if the Btree passed as the only argument is sharable.
|
||||
|
||||
@@ -366,5 +366,9 @@ void sqlite3BtreeCursorList(Btree*);
|
||||
# define sqlite3SchemaMutexHeld(X,Y,Z) 1
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_ENABLE_TRANSACTION_PAGES
|
||||
int sqlite3BtreeTransactionPages(Btree*, int*, u32**, int*, u32**);
|
||||
#endif
|
||||
|
||||
|
||||
#endif /* SQLITE_BTREE_H */
|
||||
|
||||
@@ -440,6 +440,16 @@ struct BtShared {
|
||||
Btree *pWriter; /* Btree with currently open write transaction */
|
||||
#endif
|
||||
u8 *pTmpSpace; /* Temp space sufficient to hold a single cell */
|
||||
|
||||
#ifdef SQLITE_ENABLE_TRANSACTION_PAGES
|
||||
Bitvec *pBtRead; /* Btree pages read during current write transaction */
|
||||
Bitvec *pBtWrite; /* Btree pages written during current transaction */
|
||||
Bitvec *pBtAlloc; /* Btree pages allocated during current transaction */
|
||||
int nRead; /* Number of entries in aiRead[] array */
|
||||
u32 *aiRead; /* Array returned to sqlite3_transaction_pages() */
|
||||
int nWrite; /* Number of entries in aiWrite[] array */
|
||||
u32 *aiWrite; /* Array returned to sqlite3_transaction_pages() */
|
||||
#endif
|
||||
};
|
||||
|
||||
/*
|
||||
|
||||
+29
@@ -4077,4 +4077,33 @@ int sqlite3_snapshot_recover(sqlite3 *db, const char *zDb){
|
||||
void sqlite3_snapshot_free(sqlite3_snapshot *pSnapshot){
|
||||
sqlite3_free(pSnapshot);
|
||||
}
|
||||
|
||||
#ifdef SQLITE_ENABLE_TRANSACTION_PAGES
|
||||
/*
|
||||
** Return the pages read and written by the current write transaction.
|
||||
*/
|
||||
int sqlite3_transaction_pages(
|
||||
sqlite3 *db, const char *zDbName,
|
||||
int *pnRead, unsigned int **paRead,
|
||||
int *pnWrite, unsigned int **paWrite
|
||||
){
|
||||
Btree *pBt; /* Btree to query */
|
||||
int rc; /* Return code */
|
||||
#ifdef SQLITE_ENABLE_API_ARMOR
|
||||
if( !sqlite3SafetyCheckOk(db) ){
|
||||
(void)SQLITE_MISUSE_BKPT;
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
sqlite3_mutex_enter(db->mutex);
|
||||
pBt = sqlite3DbNameToBtree(db, zDbName);
|
||||
if( pBt==0 ) return SQLITE_ERROR;
|
||||
rc = sqlite3BtreeTransactionPages(pBt, pnRead, paRead, pnWrite, paWrite);
|
||||
sqlite3_mutex_leave(db->mutex);
|
||||
|
||||
return rc;
|
||||
}
|
||||
#endif /* SQLITE_ENABLE_TRANSACTION_PAGES */
|
||||
|
||||
#endif /* SQLITE_ENABLE_SNAPSHOT */
|
||||
|
||||
+1
-1
@@ -4113,7 +4113,7 @@ int sqlite3PagerClose(Pager *pPager, sqlite3 *db){
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
#if !defined(NDEBUG) || defined(SQLITE_TEST)
|
||||
#if !defined(NDEBUG) || defined(SQLITE_TEST) || defined(SQLITE_ENABLE_TRANSACTION_PAGES)
|
||||
/*
|
||||
** Return the page number for page pPg.
|
||||
*/
|
||||
|
||||
+3
-1
@@ -220,8 +220,10 @@ void *sqlite3PagerCodec(DbPage *);
|
||||
#endif
|
||||
|
||||
/* Functions to support testing and debugging. */
|
||||
#if !defined(NDEBUG) || defined(SQLITE_TEST)
|
||||
#if !defined(NDEBUG) || defined(SQLITE_TEST) || defined(SQLITE_ENABLE_TRANSACTION_PAGES)
|
||||
Pgno sqlite3PagerPagenumber(DbPage*);
|
||||
#endif
|
||||
#if !defined(NDEBUG) || defined(SQLITE_TEST)
|
||||
int sqlite3PagerIswriteable(DbPage*);
|
||||
#endif
|
||||
#ifdef SQLITE_TEST
|
||||
|
||||
@@ -8448,6 +8448,53 @@ SQLITE_EXPERIMENTAL int sqlite3_snapshot_cmp(
|
||||
*/
|
||||
SQLITE_EXPERIMENTAL int sqlite3_snapshot_recover(sqlite3 *db, const char *zDb);
|
||||
|
||||
|
||||
/*
|
||||
** THIS API IS A HACK ONLY.
|
||||
**
|
||||
** Return the page numbers of all b-tree pages read from or written to
|
||||
** database zDb ("main", "temp" etc.) belonging to handle db since the
|
||||
** current write transaction was started. A page is only reported on if:
|
||||
**
|
||||
** + It is a b-tree page, not an overflow, free or pointer-map page, and
|
||||
**
|
||||
** + it was a b-tree page when the transaction was started (i.e. is not a
|
||||
** b-tree page created by reusing free page or extending the database
|
||||
** file).
|
||||
**
|
||||
** If successful, this function returns SQLITE_OK and sets the four output
|
||||
** variables as follows:
|
||||
**
|
||||
** + (*paRead) is set to point to an array containing the page numbers
|
||||
** of all pages read since the current write transaction was opened.
|
||||
** (*pnRead) is set to the number of elements in this array.
|
||||
**
|
||||
** + (*paWrite) is set to point to an array containing the page numbers
|
||||
** of all pages written since the current write transaction was opened.
|
||||
** (*paRead) is set to the number of elements in this array.
|
||||
**
|
||||
** The array references are valid until the next call to this API function
|
||||
** on the same database or until the database is DETACHed from the database
|
||||
** handle.
|
||||
**
|
||||
** If this function is called when there is no write transaction opened
|
||||
** on the specified database, all four output parameters are set to 0.
|
||||
**
|
||||
** If an error occurs, an SQLite error code is returned (e.g. SQLITE_NOMEM)
|
||||
** and the final values of the four output parameters are undefined.
|
||||
**
|
||||
** This function is only enabled if SQLITE_ENABLE_TRANSACTION_PAGES is
|
||||
** defined during compilation.
|
||||
*/
|
||||
SQLITE_EXPERIMENTAL int sqlite3_transaction_pages(
|
||||
sqlite3 *db, const char *zDb,
|
||||
int *pnRead, unsigned int **paRead,
|
||||
int *pnWrite, unsigned int **paWrite
|
||||
);
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
** Undo the hack that converts floating point types to integer for
|
||||
** builds on processors without floating point support.
|
||||
|
||||
@@ -3631,6 +3631,10 @@ u32 sqlite3BitvecSize(Bitvec*);
|
||||
int sqlite3BitvecBuiltinTest(int,int*);
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_ENABLE_TRANSACTION_PAGES
|
||||
int sqlite3BitvecToArray(Bitvec *p, int *pnElem, u32 **paElem);
|
||||
#endif
|
||||
|
||||
RowSet *sqlite3RowSetInit(sqlite3*, void*, unsigned int);
|
||||
void sqlite3RowSetClear(RowSet*);
|
||||
void sqlite3RowSetInsert(RowSet*, i64);
|
||||
|
||||
+52
@@ -2550,6 +2550,55 @@ static int SQLITE_TCLAPI test_delete_database(
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Usage: sqlite3_transaction_pages DB FILENAME
|
||||
*/
|
||||
#ifdef SQLITE_ENABLE_TRANSACTION_PAGES
|
||||
static int SQLITE_TCLAPI test_transaction_pages(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
const char *zDb;
|
||||
sqlite3 *db;
|
||||
int rc;
|
||||
|
||||
int nRead;
|
||||
int nWrite;
|
||||
unsigned int *aiRead;
|
||||
unsigned int *aiWrite;
|
||||
|
||||
if( objc!=3 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "DB FILE");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
|
||||
zDb = (const char*)Tcl_GetString(objv[2]);
|
||||
|
||||
rc = sqlite3_transaction_pages(db, zDb, &nRead, &aiRead, &nWrite, &aiWrite);
|
||||
if( rc==SQLITE_OK ){
|
||||
Tcl_Obj *pList = Tcl_NewObj();
|
||||
Tcl_Obj *p = Tcl_NewObj();
|
||||
int i;
|
||||
for(i=0; i<nRead; i++){
|
||||
Tcl_ListObjAppendElement(interp, p, Tcl_NewIntObj((int)aiRead[i]));
|
||||
}
|
||||
Tcl_ListObjAppendElement(interp, pList, p);
|
||||
p = Tcl_NewObj();
|
||||
for(i=0; i<nWrite; i++){
|
||||
Tcl_ListObjAppendElement(interp, p, Tcl_NewIntObj((int)aiWrite[i]));
|
||||
}
|
||||
Tcl_ListObjAppendElement(interp, pList, p);
|
||||
Tcl_SetObjResult(interp, pList);
|
||||
}else{
|
||||
Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite3ErrName(rc), -1));
|
||||
return TCL_ERROR;
|
||||
}
|
||||
return TCL_OK;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Usage: sqlite3_next_stmt DB STMT
|
||||
**
|
||||
@@ -7686,6 +7735,9 @@ int Sqlitetest1_Init(Tcl_Interp *interp){
|
||||
{ "sqlite3_snapshot_cmp_blob", test_snapshot_cmp_blob, 0 },
|
||||
#endif
|
||||
{ "sqlite3_delete_database", test_delete_database, 0 },
|
||||
#ifdef SQLITE_ENABLE_TRANSACTION_PAGES
|
||||
{ "sqlite3_transaction_pages", test_transaction_pages, 0 },
|
||||
#endif
|
||||
};
|
||||
static int bitmask_size = sizeof(Bitmask)*8;
|
||||
static int longdouble_size = sizeof(LONGDOUBLE_TYPE);
|
||||
|
||||
@@ -0,0 +1,144 @@
|
||||
# 2017-01-19
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
set ::testprefix tpages
|
||||
|
||||
sqlite3_test_control_pending_byte 0x1000000
|
||||
|
||||
proc integers {iFirst n} {
|
||||
set ret [list]
|
||||
for {set i $iFirst} {$i<$iFirst+$n} {incr i} {
|
||||
lappend ret $i
|
||||
}
|
||||
set ret
|
||||
}
|
||||
|
||||
do_execsql_test 1.0 {
|
||||
CREATE TABLE t1(a, b, c);
|
||||
WITH s(i) AS (
|
||||
SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<99
|
||||
)
|
||||
INSERT INTO t1 SELECT randomblob(10), randomblob(400), randomblob(400) FROM s;
|
||||
}
|
||||
|
||||
foreach {n1 n2} {
|
||||
0 10
|
||||
10 20
|
||||
5000 20
|
||||
5000 5000
|
||||
5001 5001
|
||||
60000 5001
|
||||
60000 5
|
||||
5000 1000
|
||||
6000 1000
|
||||
7000 1000
|
||||
} {
|
||||
set n1 [expr $n1]
|
||||
set n2 [expr $n2]
|
||||
|
||||
reset_db
|
||||
do_execsql_test 1.$n1.1 {
|
||||
CREATE TABLE t1(a, b, c);
|
||||
WITH s(i) AS (
|
||||
SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<$n1
|
||||
)
|
||||
INSERT INTO t1
|
||||
SELECT randomblob(10), randomblob(400), randomblob(400) FROM s;
|
||||
}
|
||||
|
||||
set iFirst [expr [db one {PRAGMA page_count}] + 1]
|
||||
do_execsql_test 1.$n1.2 {
|
||||
CREATE TABLE t2(a, b, c);
|
||||
WITH s(i) AS (
|
||||
SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<$n2
|
||||
)
|
||||
INSERT INTO t2
|
||||
SELECT randomblob(10), randomblob(400), randomblob(400) FROM s;
|
||||
}
|
||||
set nInt [expr [db one {PRAGMA page_count}] - $iFirst + 1]
|
||||
|
||||
do_test 1.$n1.3 {
|
||||
execsql {
|
||||
BEGIN IMMEDIATE;
|
||||
SELECT * FROM t2;
|
||||
}
|
||||
sqlite3_transaction_pages db main
|
||||
} [list [integers $iFirst $nInt] {}]
|
||||
execsql cOMMIT
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
proc do_transaction_pages_test {tn sql pages} {
|
||||
uplevel [list execsql $sql]
|
||||
uplevel [list \
|
||||
do_test $tn {sqlite3_transaction_pages db main} [list {*}$pages]
|
||||
]
|
||||
}
|
||||
reset_db
|
||||
do_transaction_pages_test 2.0 {
|
||||
PRAGMA auto_vacuum = 0;
|
||||
PRAGMA page_size = 1024;
|
||||
CREATE TABLE t1(x);
|
||||
BEGIN;
|
||||
INSERT INTO t1 VALUES(randomblob(1500));
|
||||
} {2 2}
|
||||
|
||||
do_transaction_pages_test 2.1 {
|
||||
COMMIT;
|
||||
BEGIN;
|
||||
DELETE FROM t1;
|
||||
} {2 2}
|
||||
|
||||
reset_db
|
||||
do_execsql_test 2.2 {
|
||||
PRAGMA auto_vacuum = 0;
|
||||
PRAGMA page_size = 1024;
|
||||
CREATE TABLE t1(x);
|
||||
INSERT INTO t1 VALUES(randomblob(900));
|
||||
INSERT INTO t1 VALUES(randomblob(900));
|
||||
CREATE TABLE t2(y);
|
||||
}
|
||||
|
||||
do_transaction_pages_test 2.3 {
|
||||
BEGIN;
|
||||
UPDATE t1 SET x=randomblob(899);
|
||||
} {{2 3 4} {3 4}}
|
||||
|
||||
do_transaction_pages_test 2.4 {
|
||||
DELETE FROM t1;
|
||||
COMMIT;
|
||||
} {{} {}}
|
||||
|
||||
do_transaction_pages_test 2.5 {
|
||||
BEGIN;
|
||||
INSERT INTO t2 VALUES(randomblob(900));
|
||||
INSERT INTO t2 VALUES(randomblob(900));
|
||||
} {5 5}
|
||||
|
||||
do_transaction_pages_test 2.6 {
|
||||
COMMIT;
|
||||
BEGIN;
|
||||
INSERT INTO t1 VALUES(randomblob(900));
|
||||
} {2 2}
|
||||
|
||||
do_transaction_pages_test 2.7 {
|
||||
INSERT INTO t1 VALUES(randomblob(900));
|
||||
INSERT INTO t1 VALUES(randomblob(900));
|
||||
INSERT INTO t1 VALUES(randomblob(900));
|
||||
INSERT INTO t1 VALUES(randomblob(900));
|
||||
} {2 2}
|
||||
|
||||
finish_test
|
||||
|
||||
Reference in New Issue
Block a user