-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
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
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
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
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
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
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
-681d96eb822e606da53700867191d4738bda20c8
\ No newline at end of file
+2a8f6c890cfa5f61b491ddf8f560b6ba8c6cdc32
\ No newline at end of file
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
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:
}
}
+#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 ){
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);
}
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
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;
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);
}
}
+
/*
** Insert a new cell on pPage at cell index "i". pCell points to the
** content of the cell.
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;
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{
** 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);
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
if( loc==0 ){
CellInfo info;
assert( idx<pPage->nCell );
- rc = sqlite3PagerWrite(pPage->pDbPage);
+ rc = pagerWrite(pBt, pPage->pDbPage);
if( rc ){
goto end_insert;
}
/* 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);
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);
}
}
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);
}
*/
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.
# define sqlite3SchemaMutexHeld(X,Y,Z) 1
#endif
+#ifdef SQLITE_ENABLE_TRANSACTION_PAGES
+ int sqlite3BtreeTransactionPages(Btree*, int*, u32**, int*, u32**);
+#endif
+
#endif /* SQLITE_BTREE_H */
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
};
/*
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 */
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.
*/
#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
*/
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.
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);
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
**
{ "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);
--- /dev/null
+# 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