From: dan Date: Wed, 18 Jan 2017 20:14:50 +0000 (+0000) Subject: Add temporary code to record and report on the set of b-tree pages read and X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=21ff59fa4662ecc210bf2f67d10c9a6b83f3c114;p=thirdparty%2Fsqlite.git Add temporary code to record and report on the set of b-tree pages read and written by the current transaction. This is likely still buggy. FossilOrigin-Name: 2a8f6c890cfa5f61b491ddf8f560b6ba8c6cdc32 --- diff --git a/manifest b/manifest index e888f901e2..0920577db2 100644 --- a/manifest +++ b/manifest @@ -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\stemporary\scode\sto\srecord\sand\sreport\son\sthe\sset\sof\sb-tree\spages\sread\sand\nwritten\sby\sthe\scurrent\stransaction.\sThis\sis\slikely\sstill\sbuggy. +D 2017-01-18T20:14:50.383 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 b0b714e311138e5f543d167b48415bf4ab6f2529 +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 abcb738d7a61a62ee60b00ac37eeb987f4919062 F test/tpch01.test 04adbf8d8300fa60a222f28d901abd76e7be6dd4 F test/trace.test 6f676313e3ebd2a50585036d2f212a3319dd5836 F test/trace2.test f5cb67ad3bc09e0c58e8cca78dfd0b5639259983 @@ -1547,7 +1548,10 @@ 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 681d96eb822e606da53700867191d4738bda20c8 +R d3876579ed9b8addfbb830510866c4e1 +T *branch * transaction-pages +T *sym-transaction-pages * +T -sym-trunk * U dan -Z 623656530433c4c90deab7fe9f3d0d79 +Z 49b44b310f5c3b7319ea5dbf5a0bd059 diff --git a/manifest.uuid b/manifest.uuid index 4ce4080714..41195ecdea 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -681d96eb822e606da53700867191d4738bda20c8 \ No newline at end of file +2a8f6c890cfa5f61b491ddf8f560b6ba8c6cdc32 \ No newline at end of file diff --git a/src/bitvec.c b/src/bitvec.c index bd4a09429b..192f647615 100644 --- a/src/bitvec.c +++ b/src/bitvec.c @@ -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 && iu.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 && iu.aBitmap[i/BITVEC_SZELEM] & (1<<(i&(BITVEC_SZELEM-1))) ){ + rc = bitvecAppendArrayElem(pnAlloc, pnElem, paElem, i+iOff); + } + } + }else{ + for(i=0; rc==SQLITE_OK && iu.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 diff --git a/src/btree.c b/src/btree.c index 7b56867646..8142d41433 100644 --- a/src/btree.c +++ b/src/btree.c @@ -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: @@ -3266,6 +3279,21 @@ int sqlite3BtreeBeginTrans(Btree *p, int wrflag){ } } +#ifdef SQLITE_ENABLE_TRANSACTION_PAGES + if( rc==SQLITE_OK && wrflag ){ + assert( pBt->pBtRead==0 && pBt->pBtWrite==0 ); + 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; + sqlite3BitvecDestroy(pBt->pBtRead); + sqlite3BitvecDestroy(pBt->pBtWrite); + sqlite3BitvecDestroy(pBt->pBtAlloc); + pBt->pBtAlloc = pBt->pBtRead = pBt->pBtWrite = 0; + } + } +#endif trans_begun: if( rc==SQLITE_OK && wrflag ){ @@ -3790,6 +3818,19 @@ static void btreeEndTransaction(Btree *p){ unlockBtreeIfUnused(pBt); } +#ifdef SQLITE_ENABLE_TRANSACTION_PAGES + 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; + } +#endif + btreeIntegrity(p); } @@ -4417,6 +4458,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 +4552,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 +5896,16 @@ static int allocateBtreePage( assert( *pPgno!=PENDING_BYTE_PAGE(pBt) ); +#ifdef SQLITE_ENABLE_TRANSACTION_PAGES + if( rc==SQLITE_OK && *pPgnopBtAlloc) ){ + rc = sqlite3BitvecSet(pBt->pBtAlloc, *pPgno); + if( rc!=SQLITE_OK ){ + releasePage(*ppPage); + *ppPage = 0; + } + } +#endif + end_allocate_page: releasePage(pTrunk); releasePage(pPrevTrunk); @@ -6286,6 +6362,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 +6428,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 +7462,7 @@ static int balance_nonroot( if( ipDbPage); + rc = pagerWrite(pBt, pNew->pDbPage); nNew++; if( rc ) goto balance_cleanup; }else{ @@ -7755,7 +7832,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 +7914,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 +8135,7 @@ int sqlite3BtreeInsert( if( loc==0 ){ CellInfo info; assert( idxnCell ); - rc = sqlite3PagerWrite(pPage->pDbPage); + rc = pagerWrite(pBt, pPage->pDbPage); if( rc ){ goto end_insert; } @@ -8238,7 +8315,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 +8338,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 +8601,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 +9803,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. diff --git a/src/btree.h b/src/btree.h index ae57468e3f..0b71b17d2d 100644 --- a/src/btree.h +++ b/src/btree.h @@ -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 */ diff --git a/src/btreeInt.h b/src/btreeInt.h index fc2182b634..563ef2923a 100644 --- a/src/btreeInt.h +++ b/src/btreeInt.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 }; /* diff --git a/src/main.c b/src/main.c index 9aad8fdd4c..ad581df774 100644 --- a/src/main.c +++ b/src/main.c @@ -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 */ diff --git a/src/pager.c b/src/pager.c index 5813b4db47..824591c897 100644 --- a/src/pager.c +++ b/src/pager.c @@ -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. */ diff --git a/src/pager.h b/src/pager.h index dd57f598bd..e1b200c08a 100644 --- a/src/pager.h +++ b/src/pager.h @@ -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 diff --git a/src/sqlite.h.in b/src/sqlite.h.in index 7d7e70f901..c4800e0365 100644 --- a/src/sqlite.h.in +++ b/src/sqlite.h.in @@ -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. diff --git a/src/sqliteInt.h b/src/sqliteInt.h index d5ac957d22..09fb1a39bf 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -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); diff --git a/src/test1.c b/src/test1.c index d17fc1022a..0cc7b95823 100644 --- a/src/test1.c +++ b/src/test1.c @@ -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