-C Fix\sthe\sorigin\sAPIs\sso\sthat\sthey\scorrectly\shandle\sviews\sand\ssubqueries\sthat\scannot\sbe\sflattened.\s(CVS\s3072)
-D 2006-02-10T07:07:14
+C Work\saround\sthe\scase\swhere\sthe\spending-byte\spage\sis\salso\sa\sa\spointer-map\spage.\sTicket\s#1667.\s(CVS\s3073)
+D 2006-02-10T08:24:21
F Makefile.in 5d8dff443383918b700e495de42ec65bc1c8865b
F Makefile.linux-gcc 74ba0eadf88748a9ce3fd03d2a3ede2e6715baec
F README 9c4e2d6706bdcc3efdd773ce752a8cdab4f90028
F src/analyze.c 7d2b7ab9a9c2fd6e55700f69064dfdd3e36d7a8a
F src/attach.c d73a3505de3fb9e373d0a158978116c4212031d0
F src/auth.c 9ae84d2d94eb96195e04515715e08e85963e96c2
-F src/btree.c 66112ae6a5caab384010344b514e98b346f550c0
+F src/btree.c 579594eff6b200ee6e653f4da192d33c50413d56
F src/btree.h 5663c4f43e8521546ccebc8fc95acb013b8f3184
F src/build.c 11798f165cdcf3b46f4b10b2ba3853b8c370eac2
F src/callback.c 1bf497306c32229114f826707054df7ebe10abf2
F test/tkt1537.test e3a14332de9770be8ff14bd15c19a49cbec10808
F test/tkt1567.test 18023cc3626a365f0118e17b66decedec93b1a6f
F test/tkt1644.test 8926ebffd19950a7cce22734a06d8282a69c3a20
+F test/tkt1667.test c7faac5050b45d81d0b9f6294e4570b588106e19
F test/trace.test 75ffc1b992c780d054748a656e3e7fd674f18567
F test/trans.test b25eae982d156867eac338409905fd4ca589b7f8
F test/trigger1.test 0c1d44882dba5c92e4efee4dd133cc979f0b1860
F www/vdbe.tcl 87a31ace769f20d3627a64fa1fade7fed47b90d0
F www/version3.tcl a99cf5f6d8bd4d5537584a2b342f0fb9fa601d8b
F www/whentouse.tcl 97e2b5cd296f7d8057e11f44427dea8a4c2db513
-P a6c30be214bb575f9ecfa299b7a597d21e3d3aca
-R 41b2424fdff209e4871bfdf191620183
+P 5e8611e13de08d704cea6c9c4466c3af842a7a1a
+R 0a57b51faa251c36e1169634bc62a68d
U danielk1977
-Z d531a3addcf6506678bf78af10fce292
+Z 733d67f3b503a31d4190158ec7a34bd0
** May you share freely, never taking more than you give.
**
*************************************************************************
-** $Id: btree.c,v 1.312 2006/02/10 02:27:42 danielk1977 Exp $
+** $Id: btree.c,v 1.313 2006/02/10 08:24:21 danielk1977 Exp $
**
** This file implements a external (disk-based) database using BTrees.
** For a detailed discussion of BTrees, refer to
** used to test if pgno is a pointer-map page. PTRMAP_ISPAGE implements
** this test.
*/
-#define PTRMAP_PAGENO(pgsz, pgno) (((pgno-2)/(pgsz/5+1))*(pgsz/5+1)+2)
-#define PTRMAP_PTROFFSET(pgsz, pgno) (((pgno-2)%(pgsz/5+1)-1)*5)
-#define PTRMAP_ISPAGE(pgsz, pgno) (PTRMAP_PAGENO(pgsz,pgno)==pgno)
+#define PTRMAP_PAGENO(pBt, pgno) ptrmapPageno(pBt, pgno)
+#define PTRMAP_PTROFFSET(pBt, pgno) (5*(pgno-ptrmapPageno(pBt, pgno)-1))
+#define PTRMAP_ISPAGE(pBt, pgno) (PTRMAP_PAGENO((pBt),(pgno))==(pgno))
+
+static Pgno ptrmapPageno(BtShared *pBt, Pgno pgno){
+ int nPagesPerMapPage = (pBt->usableSize/5)+1;
+ int iPtrMap = (pgno-2)/nPagesPerMapPage;
+ int ret = (iPtrMap*nPagesPerMapPage) + 2;
+ if( ret==PENDING_BYTE_PAGE(pBt) ){
+ ret++;
+ }
+ return ret;
+}
/*
** The pointer map is a lookup table that identifies the parent page for
int offset; /* Offset in pointer map page */
int rc;
+ /* The master-journal page number must never be used as a pointer map page */
+ assert( 0==PTRMAP_ISPAGE(pBt, PENDING_BYTE_PAGE(pBt)) );
+
assert( pBt->autoVacuum );
if( key==0 ){
return SQLITE_CORRUPT_BKPT;
}
- iPtrmap = PTRMAP_PAGENO(pBt->usableSize, key);
+ iPtrmap = PTRMAP_PAGENO(pBt, key);
rc = sqlite3pager_get(pBt->pPager, iPtrmap, (void **)&pPtrmap);
if( rc!=SQLITE_OK ){
return rc;
}
- offset = PTRMAP_PTROFFSET(pBt->usableSize, key);
+ offset = PTRMAP_PTROFFSET(pBt, key);
if( eType!=pPtrmap[offset] || get4byte(&pPtrmap[offset+1])!=parent ){
TRACE(("PTRMAP_UPDATE: %d->(%d,%d)\n", key, eType, parent));
int offset; /* Offset of entry in pointer map */
int rc;
- iPtrmap = PTRMAP_PAGENO(pBt->usableSize, key);
+ iPtrmap = PTRMAP_PAGENO(pBt, key);
rc = sqlite3pager_get(pBt->pPager, iPtrmap, (void **)&pPtrmap);
if( rc!=0 ){
return rc;
}
- offset = PTRMAP_PTROFFSET(pBt->usableSize, key);
+ offset = PTRMAP_PTROFFSET(pBt, key);
if( pEType ) *pEType = pPtrmap[offset];
if( pPgno ) *pPgno = get4byte(&pPtrmap[offset+1]);
#endif
assert( pBt->autoVacuum );
- if( PTRMAP_ISPAGE(pgsz, sqlite3pager_pagecount(pPager)) ){
+ if( PTRMAP_ISPAGE(pBt, sqlite3pager_pagecount(pPager)) ){
return SQLITE_CORRUPT_BKPT;
}
return SQLITE_OK;
}
+ /* This block figures out how many pages there are in the database
+ ** now (variable origSize), and how many there will be after the
+ ** truncation (variable finSize).
+ **
+ ** The final size is the original size, less the number of free pages
+ ** in the database, less any pointer-map pages that will no longer
+ ** be required, less 1 if the pending-byte page was part of the database
+ ** but is not after the truncation.
+ **/
origSize = sqlite3pager_pagecount(pPager);
- nPtrMap = (nFreeList-origSize+PTRMAP_PAGENO(pgsz, origSize)+pgsz/5)/(pgsz/5);
+ if( origSize==PENDING_BYTE_PAGE(pBt) ){
+ origSize--;
+ }
+ nPtrMap = (nFreeList-origSize+PTRMAP_PAGENO(pBt, origSize)+pgsz/5)/(pgsz/5);
finSize = origSize - nFreeList - nPtrMap;
- if( origSize>=PENDING_BYTE_PAGE(pBt) && finSize<=PENDING_BYTE_PAGE(pBt) ){
+ if( origSize>PENDING_BYTE_PAGE(pBt) && finSize<=PENDING_BYTE_PAGE(pBt) ){
+ finSize--;
+ }
+ while( PTRMAP_ISPAGE(pBt, finSize) || finSize==PENDING_BYTE_PAGE(pBt) ){
finSize--;
- if( PTRMAP_ISPAGE(pBt->usableSize, finSize) ){
- finSize--;
- }
}
TRACE(("AUTOVACUUM: Begin (db size %d->%d)\n", origSize, finSize));
*/
for( iDbPage=finSize+1; iDbPage<=origSize; iDbPage++ ){
/* If iDbPage is a pointer map page, or the pending-byte page, skip it. */
- if( PTRMAP_ISPAGE(pgsz, iDbPage) || iDbPage==PENDING_BYTE_PAGE(pBt) ){
+ if( PTRMAP_ISPAGE(pBt, iDbPage) || iDbPage==PENDING_BYTE_PAGE(pBt) ){
continue;
}
put4byte(&pBt->pPage1->aData[36], 0);
if( rc!=SQLITE_OK ) goto autovacuum_out;
*nTrunc = finSize;
+ assert( finSize!=PENDING_BYTE_PAGE(pBt) );
autovacuum_out:
assert( nRef==*sqlite3pager_stats(pPager) );
*pPgno = sqlite3pager_pagecount(pBt->pPager) + 1;
#ifndef SQLITE_OMIT_AUTOVACUUM
- if( pBt->autoVacuum && PTRMAP_ISPAGE(pBt->usableSize, *pPgno) ){
+ if( pBt->autoVacuum && PTRMAP_ISPAGE(pBt, *pPgno) ){
/* If *pPgno refers to a pointer-map page, allocate two new pages
** at the end of the file instead of one. The first allocated page
** becomes a new pointer-map page, the second is used by the caller.
/* The new root-page may not be allocated on a pointer-map page, or the
** PENDING_BYTE page.
*/
- if( pgnoRoot==PTRMAP_PAGENO(pBt->usableSize, pgnoRoot) ||
+ if( pgnoRoot==PTRMAP_PAGENO(pBt, pgnoRoot) ||
pgnoRoot==PENDING_BYTE_PAGE(pBt) ){
pgnoRoot++;
}
if( maxRootPgno==PENDING_BYTE_PAGE(pBt) ){
maxRootPgno--;
}
- if( maxRootPgno==PTRMAP_PAGENO(pBt->usableSize, maxRootPgno) ){
+ if( maxRootPgno==PTRMAP_PAGENO(pBt, maxRootPgno) ){
maxRootPgno--;
}
assert( maxRootPgno!=PENDING_BYTE_PAGE(pBt) );
** references to pointer-map pages.
*/
if( sCheck.anRef[i]==0 &&
- (PTRMAP_PAGENO(pBt->usableSize, i)!=i || !pBt->autoVacuum) ){
+ (PTRMAP_PAGENO(pBt, i)!=i || !pBt->autoVacuum) ){
checkAppendMsg(&sCheck, 0, "Page %d is never used", i);
}
if( sCheck.anRef[i]!=0 &&
- (PTRMAP_PAGENO(pBt->usableSize, i)==i && pBt->autoVacuum) ){
+ (PTRMAP_PAGENO(pBt, i)==i && pBt->autoVacuum) ){
checkAppendMsg(&sCheck, 0, "Pointer map page %d is referenced", i);
}
#endif
** the write-transaction for this database file is to delete the journal.
*/
int sqlite3BtreeSync(Btree *p, const char *zMaster){
+ int rc = SQLITE_OK;
if( p->inTrans==TRANS_WRITE ){
BtShared *pBt = p->pBt;
-#ifndef SQLITE_OMIT_AUTOVACUUM
Pgno nTrunc = 0;
+#ifndef SQLITE_OMIT_AUTOVACUUM
if( pBt->autoVacuum ){
- int rc = autoVacuumCommit(pBt, &nTrunc);
- if( rc!=SQLITE_OK ) return rc;
+ rc = autoVacuumCommit(pBt, &nTrunc);
+ if( rc!=SQLITE_OK ){
+ return rc;
+ }
}
- return sqlite3pager_sync(pBt->pPager, zMaster, nTrunc);
#endif
- return sqlite3pager_sync(pBt->pPager, zMaster, 0);
+ rc = sqlite3pager_sync(pBt->pPager, zMaster, nTrunc);
}
- return SQLITE_OK;
+ return rc;
}
/*
--- /dev/null
+# 2006 February 10
+#
+# 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.
+#
+#***********************************************************************
+# This file implements regression tests for SQLite library.
+#
+# This file implements tests to verify that ticket #1667 has been
+# fixed.
+#
+#
+# $Id: tkt1667.test,v 1.1 2006/02/10 08:24:21 danielk1977 Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+ifcapable !autovacuum {
+ finish_test
+ return
+}
+
+db close
+file delete -force test.db test.db-journal
+
+# Set the pending byte offset such that the page it is on is
+# the first autovacuum pointer map page in the file (assume a page
+# size of 1024).
+
+set first_ptrmap_page [expr 1024/5 + 3]
+set sqlite_pending_byte [expr 1024 * ($first_ptrmap_page-1)]
+
+sqlite db test.db
+
+do_test tkt1667-1 {
+ execsql {
+ PRAGMA auto_vacuum = 1;
+ BEGIN;
+ CREATE TABLE t1(a, b);
+ }
+ for {set i 0} {$i < 500} {incr i} {
+ execsql {
+ INSERT INTO t1 VALUES($i, randstr(1000, 2000))
+ }
+ }
+ execsql {
+ COMMIT;
+ }
+} {}
+for {set i 0} {$i < 500} {incr i} {
+ do_test tkt1667-2.$i.1 {
+ execsql {
+ DELETE FROM t1 WHERE a = $i;
+ }
+ } {}
+ integrity_check tkt1667-2.$i.2
+}
+
+do_test tkt1667-3 {
+ execsql {
+ BEGIN;
+ }
+ for {set i 0} {$i < 500} {incr i} {
+ execsql {
+ INSERT INTO t1 VALUES($i, randstr(1000, 2000))
+ }
+ }
+ execsql {
+ COMMIT;
+ }
+} {}
+do_test tkt1667-4.1 {
+ execsql {
+ DELETE FROM t1;
+ }
+} {}
+integrity_check tkt1667-4.2
+
+finish_test
+
+