]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Add temporary code to record and report on the set of b-tree pages read and
authordan <dan@noemail.net>
Wed, 18 Jan 2017 20:14:50 +0000 (20:14 +0000)
committerdan <dan@noemail.net>
Wed, 18 Jan 2017 20:14:50 +0000 (20:14 +0000)
written by the current transaction. This is likely still buggy.

FossilOrigin-Name: 2a8f6c890cfa5f61b491ddf8f560b6ba8c6cdc32

13 files changed:
manifest
manifest.uuid
src/bitvec.c
src/btree.c
src/btree.h
src/btreeInt.h
src/main.c
src/pager.c
src/pager.h
src/sqlite.h.in
src/sqliteInt.h
src/test1.c
test/tpages.test [new file with mode: 0644]

index e888f901e2096c677e4a602607fc96ed619edc72..0920577db278871c1a926ddb257d1256cf6ab15e 100644 (file)
--- 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
index 4ce408071418f151928823276ee23fc17c67953c..41195ecdea192f9c2375c83391a6783e9824e30c 100644 (file)
@@ -1 +1 @@
-681d96eb822e606da53700867191d4738bda20c8
\ No newline at end of file
+2a8f6c890cfa5f61b491ddf8f560b6ba8c6cdc32
\ No newline at end of file
index bd4a09429b2847e86fb543fb987b299ee0c2b90d..192f647615244815506d8d2d53a48a3f053ed2dc 100644 (file)
@@ -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
index 7b568676468f6f1a9a5bc2e11ddf3330b24a71be..8142d41433a06aa1a3d978cb271ed30aa281588c 100644 (file)
@@ -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 && *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 +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( 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 +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( idx<pPage->nCell );
-    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.
index ae57468e3fd5fec513515d97f842029ec9f9aa7c..0b71b17d2d921d2824e22f8e7a1b829cf9c43a66 100644 (file)
@@ -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 */
index fc2182b634591b00e1d105082e7b558399ec53d8..563ef2923a087a2a6236f18c079aa0c1cc41a568 100644 (file)
@@ -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
 };
 
 /*
index 9aad8fdd4cf8f05c1158aaae876de9e04bf3305e..ad581df774883211ba756b02c5012fa15e35b92d 100644 (file)
@@ -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 */
index 5813b4db476e7d6a85a9373a613c5e95fe9d1b57..824591c897ff5982a62c73e496351dbc266c67ea 100644 (file)
@@ -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.
 */
index dd57f598bd75ef28d7ed4a05035276caf1bf48e1..e1b200c08a11d46a372cca400163b76e54153d9a 100644 (file)
@@ -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
index 7d7e70f90194535cc9d31464420ca52875e46d87..c4800e0365012721f1727bf96ee568f5582ae2b9 100644 (file)
@@ -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.
index d5ac957d22f47ae139ac9bdf6bf8fed4b16ba85e..09fb1a39bfb700aeb6f74d165e30ded0041b2c26 100644 (file)
@@ -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);
index d17fc1022ad4208641894466a6d5fb1a20c76a91..0cc7b958235a1598605c40ce831ee25c23582070 100644 (file)
@@ -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);
diff --git a/test/tpages.test b/test/tpages.test
new file mode 100644 (file)
index 0000000..e6fd8c8
--- /dev/null
@@ -0,0 +1,85 @@
+# 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
+}
+
+
+
+
+finish_test