]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Work around the case where the pending-byte page is also a a pointer-map page. Ticket...
authordanielk1977 <danielk1977@noemail.net>
Fri, 10 Feb 2006 08:24:21 +0000 (08:24 +0000)
committerdanielk1977 <danielk1977@noemail.net>
Fri, 10 Feb 2006 08:24:21 +0000 (08:24 +0000)
FossilOrigin-Name: 5ea87fbbe09279e70d3a22b8cd9994befffeac53

manifest
manifest.uuid
src/btree.c
test/tkt1667.test [new file with mode: 0644]

index 61c6228494e27a215ef32a5ed23d33b0bac39253..4756873a5c696cf3fd863ef4209c7eb60d6bcd40 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-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
@@ -34,7 +34,7 @@ F src/alter.c 451b34fc4eb2475ca76a2e86b21e1030a9428091
 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
@@ -257,6 +257,7 @@ F test/tkt1536.test 83ff7a7b6e248016f8d682d4f7a4ae114070d466
 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
@@ -351,7 +352,7 @@ F www/tclsqlite.tcl bb0d1357328a42b1993d78573e587c6dcbc964b9
 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
index c4d6312f4ce191dc6344a237b91cc620a5df7692..531ea696e7712db30e8d2002b3458c1b86acbd85 100644 (file)
@@ -1 +1 @@
-5e8611e13de08d704cea6c9c4466c3af842a7a1a
\ No newline at end of file
+5ea87fbbe09279e70d3a22b8cd9994befffeac53
\ No newline at end of file
index ea6339f0a682bb7ce013fee584609517991008e7..6633ae938370cab9d735f385d4127946461f8f81 100644 (file)
@@ -9,7 +9,7 @@
 **    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
@@ -756,9 +756,19 @@ static void unlockAllTables(Btree *p){
 ** 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
@@ -810,16 +820,19 @@ static int ptrmapPut(BtShared *pBt, Pgno key, u8 eType, Pgno parent){
   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));
@@ -847,13 +860,13 @@ static int ptrmapGet(BtShared *pBt, Pgno key, u8 *pEType, Pgno *pPgno){
   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]);
 
@@ -2344,7 +2357,7 @@ static int autoVacuumCommit(BtShared *pBt, Pgno *nTrunc){
 #endif
 
   assert( pBt->autoVacuum );
-  if( PTRMAP_ISPAGE(pgsz, sqlite3pager_pagecount(pPager)) ){
+  if( PTRMAP_ISPAGE(pBt, sqlite3pager_pagecount(pPager)) ){
     return SQLITE_CORRUPT_BKPT;
   }
 
@@ -2357,14 +2370,26 @@ static int autoVacuumCommit(BtShared *pBt, Pgno *nTrunc){
     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));
 
@@ -2376,7 +2401,7 @@ static int autoVacuumCommit(BtShared *pBt, Pgno *nTrunc){
   */
   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;
     }
 
@@ -2434,6 +2459,7 @@ static int autoVacuumCommit(BtShared *pBt, Pgno *nTrunc){
   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) );
@@ -3806,7 +3832,7 @@ static int allocatePage(
     *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.
@@ -5440,7 +5466,7 @@ int sqlite3BtreeCreateTable(Btree *p, int *piTable, int flags){
     /* 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++;
     }
@@ -5713,7 +5739,7 @@ int sqlite3BtreeDropTable(Btree *p, int iTable, int *piMoved){
       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) );
@@ -6390,11 +6416,11 @@ char *sqlite3BtreeIntegrityCheck(Btree *p, int *aRoot, int nRoot){
     ** 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
@@ -6522,19 +6548,21 @@ int sqlite3BtreeIsInStmt(Btree *p){
 ** 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;
 }
 
 /*
diff --git a/test/tkt1667.test b/test/tkt1667.test
new file mode 100644 (file)
index 0000000..e1fd2fb
--- /dev/null
@@ -0,0 +1,85 @@
+# 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
+
+