]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Add support for table allocation (not deallocation) in auto-vacuum databases. (CVS...
authordanielk1977 <danielk1977@noemail.net>
Thu, 4 Nov 2004 02:57:33 +0000 (02:57 +0000)
committerdanielk1977 <danielk1977@noemail.net>
Thu, 4 Nov 2004 02:57:33 +0000 (02:57 +0000)
FossilOrigin-Name: 571de52376f52999268ba5e0cd05c6c6eff1ebbf

manifest
manifest.uuid
src/btree.c
test/autovacuum.test

index beb337c43efcf6aab8407bca9e5f92f8510d9313..43c7861a5cea4ac11e4b5a404e1df481d88b0049 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Update\stests\sto\swork\seven\sif\ssome\sfeatures\sof\sthe\slibrary\sare\sdisabled.\s(CVS\s2050)
-D 2004-11-03T16:27:01
+C Add\ssupport\sfor\stable\sallocation\s(not\sdeallocation)\sin\sauto-vacuum\sdatabases.\s(CVS\s2051)
+D 2004-11-04T02:57:34
 F Makefile.in c4d2416860f472a1e3393714d0372074197565df
 F Makefile.linux-gcc a9e5a0d309fa7c38e7c14d3ecf7690879d3a5457
 F README a01693e454a00cc117967e3f9fdab2d4d52e9bc1
@@ -29,7 +29,7 @@ F sqlite3.def dbaeb20c153e1d366e8f421b55a573f5dfc00863
 F sqlite3.pc.in 985b9bf34192a549d7d370e0f0b6b34a4f61369a
 F src/attach.c e49d09dad9f5f9fb10b4b0c1be5a70ae4c45e689
 F src/auth.c 3b81f2a42f48a62c2c9c9b0eda31a157c681edea
-F src/btree.c d7cc9fdb0fa91b1c0817a3352c3a45d1a1434685
+F src/btree.c 93163198e6fb666b92c893fba5edb3ef6f335c0f
 F src/btree.h 94dfec0a1722d33359b23e7e310f2b64ffedf029
 F src/build.c bb896c5f85ab749d17ae5d730235134c12c08033
 F src/date.c 34bdb0082db7ec2a83ef00063f7b44e61ee19dad
@@ -87,7 +87,7 @@ F test/attach.test 6ad560eb3e77751a4faecd77da09deac9e38cc41
 F test/attach2.test f7795123d3051ace1672b6d23973da6435de3745
 F test/attach3.test 6d060986ff004ebb89e1876a331d96c6bb62269e
 F test/auth.test 1cc252d9e7b3bdc1314199cbf3a0d3c5ed026c21
-F test/autovacuum.test 832bcbb0086b7a1a5af24f32399e02304f0056d4
+F test/autovacuum.test 9211914801ad35ad8f0fc15711b12461850ef2ac
 F test/bigfile.test d3744a8821ce9abb8697f2826a3e3d22b719e89f
 F test/bigrow.test f0aeb7573dcb8caaafea76454be3ade29b7fc747
 F test/bind.test fa74f98417cd313f28272acff832a8a7d04a0916
@@ -252,7 +252,7 @@ F www/tclsqlite.tcl 560ecd6a916b320e59f2917317398f3d59b7cc25
 F www/vdbe.tcl 59288db1ac5c0616296b26dce071c36cb611dfe9
 F www/version3.tcl 092a01f5ef430d2c4acc0ae558d74c4bb89638a0
 F www/whentouse.tcl fdacb0ba2d39831e8a6240d05a490026ad4c4e4c
-P a82980fd70285820c64b42393ef85a9e21addc5d
-R 8b665ecae63deee85dcecde69c7e3c11
-U drh
-Z 748c6cf073ccedb84a7443f14a915c73
+P b11fc9b3f3a2711f98e7e45724aa1d30081197f3
+R 242e2153cebd7e4c43dc16ab1542d7f2
+U danielk1977
+Z 7eb34cc29f203e9a41d2637315e55a85
index 66bd16c5bbd97d7930157f326116fd6e067c187c..ad44c2c9692ffd5de3786a69d486966377d6870a 100644 (file)
@@ -1 +1 @@
-b11fc9b3f3a2711f98e7e45724aa1d30081197f3
\ No newline at end of file
+571de52376f52999268ba5e0cd05c6c6eff1ebbf
\ No newline at end of file
index fbb45d201861ca476ef75164d182a0a7c42db9aa..218ad74d43839b7488c3b9b3c805487754ec3540 100644 (file)
@@ -9,7 +9,7 @@
 **    May you share freely, never taking more than you give.
 **
 *************************************************************************
-** $Id: btree.c,v 1.202 2004/11/03 11:37:08 danielk1977 Exp $
+** $Id: btree.c,v 1.203 2004/11/04 02:57:34 danielk1977 Exp $
 **
 ** This file implements a external (disk-based) database using BTrees.
 ** For a detailed discussion of BTrees, refer to
@@ -1426,6 +1426,11 @@ static int newDatabase(Btree *pBt){
   memset(&data[24], 0, 100-24);
   zeroPage(pP1, PTF_INTKEY|PTF_LEAF|PTF_LEAFDATA );
   pBt->pageSizeFixed = 1;
+#ifndef SQLITE_OMIT_AUTOVACUUM
+  if( pBt->autoVacuum ){
+    put4byte(&data[36 + 4*4], 1);
+  }
+#endif
   return SQLITE_OK;
 }
 
@@ -1606,6 +1611,74 @@ static void modifyPagePointer(MemPage *pPage, Pgno iFrom, Pgno iTo, u8 eType){
   }
 }
 
+
+static int relocatePage(
+  Btree *pBt, 
+  MemPage *pDbPage,
+  u8 eType,
+  Pgno iPtrPage,
+  Pgno iFreePage
+){
+  MemPage *pPtrPage;   /* The page that contains a pointer to pDbPage */
+  Pgno iDbPage = pDbPage->pgno;
+  Pager *pPager = pBt->pPager;
+  int rc;
+
+  assert( eType==PTRMAP_OVERFLOW2 
+      || eType==PTRMAP_OVERFLOW1 || eType==PTRMAP_BTREE );
+
+  /* Move page iDbPage from it's current location to page number iFreePage */
+  TRACE(("AUTOVACUUM: Moving %d to free page %d (ptr page %d type %d)\n", 
+      iDbPage, iFreePage, iPtrPage, eType));
+  rc = sqlite3pager_movepage(pPager, pDbPage->aData, iFreePage);
+  if( rc!=SQLITE_OK ){
+    return rc;
+  }
+  pDbPage->pgno = iFreePage;
+
+  /* If pDbPage was a btree-page, then it may have child pages and/or cells
+  ** that point to overflow pages. The pointer map entries for all these
+  ** pages need to be changed.
+  **
+  ** If pDbPage is an overflow page, then the first 4 bytes may store a
+  ** pointer to a subsequent overflow page. If this is the case, then
+  ** the pointer map needs to be updated for the subsequent overflow page.
+  */
+  if( eType==PTRMAP_BTREE ){
+    rc = setChildPtrmaps(pDbPage);
+    if( rc!=SQLITE_OK ){
+      return rc;
+    }
+  }else{
+    Pgno nextOvfl = get4byte(pDbPage->aData);
+    if( nextOvfl!=0 ){
+      assert( nextOvfl<=sqlite3pager_pagecount(pPager) );
+      rc = ptrmapPut(pBt, nextOvfl, PTRMAP_OVERFLOW2, iFreePage);
+      if( rc!=SQLITE_OK ){
+        return rc;
+      }
+    }
+  }
+
+  /* Fix the database pointer on page iPtrPage that pointed at iDbPage so
+  ** that it points at iFreePage. Also fix the pointer map entry for
+  ** iPtrPage.
+  */
+  rc = getPage(pBt, iPtrPage, &pPtrPage);
+  if( rc!=SQLITE_OK ){
+    return rc;
+  }
+  rc = sqlite3pager_write(pPtrPage->aData);
+  if( rc!=SQLITE_OK ){
+    releasePage(pPtrPage);
+    return rc;
+  }
+  modifyPagePointer(pPtrPage, iDbPage, iFreePage, eType);
+  rc = ptrmapPut(pBt, iFreePage, eType, iPtrPage);
+  releasePage(pPtrPage);
+  return rc;
+}
+
 /* Forward declaration required by autoVacuumCommit(). */
 static int allocatePage(Btree *, MemPage **, Pgno *, Pgno);
 
@@ -1624,10 +1697,8 @@ static int autoVacuumCommit(Btree *pBt){
   u8 eType;
   int pgsz = pBt->pageSize;  /* Page size for this database */
   Pgno iDbPage;              /* The database page to move */
-  u8 *pDbPage = 0;           /* "" */
   MemPage *pDbMemPage = 0;   /* "" */
   Pgno iPtrPage;             /* The page that contains a pointer to iDbPage */
-  MemPage *pPtrMemPage = 0;  /* "" */
   Pgno iFreePage;            /* The free-list page to move iDbPage to */
   MemPage *pFreeMemPage = 0; /* "" */
 
@@ -1685,7 +1756,6 @@ static int autoVacuumCommit(Btree *pBt){
     }
     rc = getPage(pBt, iDbPage, &pDbMemPage);
     if( rc!=SQLITE_OK ) goto autovacuum_out;
-    pDbPage = pDbMemPage->aData;
 
     /* Find the next page in the free-list that is not already at the end 
     ** of the file. A page can be pulled off the free list using the 
@@ -1700,50 +1770,12 @@ static int autoVacuumCommit(Btree *pBt){
       if( rc!=SQLITE_OK ) goto autovacuum_out;
       assert( iFreePage<=origSize );
     }while( iFreePage>finSize );
-
-    /* Move page iDbPage from it's current location to page number iFreePage */
-    TRACE(("AUTOVACUUM: Moving %d to free page %d (ptr page %d type %d)\n", 
-        iDbPage, iFreePage, iPtrPage, eType));
     releasePage(pFreeMemPage);
     pFreeMemPage = 0;
-    rc = sqlite3pager_movepage(pPager, pDbPage, iFreePage);
-    if( rc!=SQLITE_OK ) goto autovacuum_out;
-    pDbMemPage->pgno = iFreePage;
 
-    /* If pDbPage was a btree-page, then it may have child pages and/or cells
-    ** that point to overflow pages. The pointer map entries for all these
-    ** pages need to be changed.
-    **
-    ** If pDbPage is an overflow page, then the first 4 bytes may store a
-    ** pointer to a subsequent overflow page. If this is the case, then
-    ** the pointer map needs to be updated for the subsequent overflow page.
-    */
-    if( eType==PTRMAP_BTREE ){
-      rc = setChildPtrmaps(pDbMemPage);
-      if( rc!=SQLITE_OK ) goto autovacuum_out;
-    }else{
-      Pgno nextOvfl = get4byte(pDbPage);
-      if( nextOvfl!=0 ){
-        assert( nextOvfl<=origSize );
-        rc = ptrmapPut(pBt, nextOvfl, PTRMAP_OVERFLOW2, iFreePage);
-        if( rc!=SQLITE_OK ) goto autovacuum_out;
-      }
-    }
+    rc = relocatePage(pBt, pDbMemPage, eType, iPtrPage, iFreePage);
     releasePage(pDbMemPage);
-    pDbMemPage = 0;
-
-    /* Fix the database pointer on page iPtrPage that pointed at iDbPage so
-    ** that it points at iFreePage. Also fix the pointer map entry for
-    ** iPtrPage.
-    */
-    rc = getPage(pBt, iPtrPage, &pPtrMemPage);
-    if( rc!=SQLITE_OK ) goto autovacuum_out;
-    rc = sqlite3pager_write(pPtrMemPage->aData);
     if( rc!=SQLITE_OK ) goto autovacuum_out;
-    modifyPagePointer(pPtrMemPage, iDbPage, iFreePage, eType);
-    rc = ptrmapPut(pBt, iFreePage, eType, iPtrPage);
-    if( rc!=SQLITE_OK ) goto autovacuum_out;
-    releasePage(pPtrMemPage);
   }
 
   /* The entire free-list has been swapped to the end of the file. So
@@ -4198,6 +4230,7 @@ int sqlite3BtreeCreateTable(Btree *pBt, int *piTable, int flags){
   MemPage *pRoot;
   Pgno pgnoRoot;
   int rc;
+/* TODO: Disallow schema modifications if there are open cursors */
   if( pBt->inTrans!=TRANS_WRITE ){
     /* Must start a transaction first */
     return pBt->readOnly ? SQLITE_READONLY : SQLITE_ERROR;
@@ -4205,18 +4238,90 @@ int sqlite3BtreeCreateTable(Btree *pBt, int *piTable, int flags){
   if( pBt->readOnly ){
     return SQLITE_READONLY;
   }
+#ifdef SQLITE_OMIT_AUTOVACUUM
   rc = allocatePage(pBt, &pRoot, &pgnoRoot, 1);
   if( rc ) return rc;
-#ifndef SQLITE_OMIT_AUTOVACUUM
-  /* Note: This is temporary code for use during development of auto-vacuum. 
-  ** There should be no need for a pointer map entry for root-pages.
-  */
+#else
   if( pBt->autoVacuum ){
+    Pgno pgnoMove;      /* Move a page here to make room for the root-page */
+    MemPage *pPageMove; /* The page to move to. */
+
+    /* Run the auto-vacuum code to ensure the free-list is empty. This is
+    ** not really necessary, but it avoids complications in dealing with
+    ** a free-list in the code below.
+    ** TODO: This may need to be revisited.
+    */
+    rc = autoVacuumCommit(pBt);
+    if( rc!=SQLITE_OK ) return rc;
+
+    /* Read the value of meta[3] from the database to determine where the
+    ** root page of the new table should go. meta[3] is the largest root-page
+    ** created so far, so the new root-page is (meta[3]+1).
+    */
+    rc = sqlite3BtreeGetMeta(pBt, 4, &pgnoRoot);
+    if( rc!=SQLITE_OK ) return rc;
+    pgnoRoot++;
+
+    /* The new root-page may not be allocated on a pointer-map page. */
+    if( pgnoRoot==PTRMAP_PAGENO(pBt->pageSize, pgnoRoot) ){
+      pgnoRoot++;
+    }
+    assert( pgnoRoot>=3 );
+
+    /* Allocate a page. The page that currently resides at pgnoRoot will
+    ** be moved to the allocated page (unless the allocated page happens
+    ** to reside at pgnoRoot).
+    */
+    rc = allocatePage(pBt, &pPageMove, &pgnoMove, 0);
+    if( rc!=SQLITE_OK ){
+      return rc;
+    }
+
+    if( pgnoMove!=pgnoRoot ){
+      u8 eType;
+      Pgno iPtrPage;
+
+      releasePage(pPageMove);
+      rc = getPage(pBt, pgnoRoot, &pRoot);
+      if( rc!=SQLITE_OK ){
+        return rc;
+      }
+      rc = ptrmapGet(pBt, pgnoRoot, &eType, &iPtrPage);
+      if( rc!=SQLITE_OK ){
+        releasePage(pRoot);
+        return rc;
+      }
+      rc = relocatePage(pBt, pRoot, eType, iPtrPage, pgnoMove);
+      releasePage(pRoot);
+      if( rc!=SQLITE_OK ){
+        return rc;
+      }
+      rc = getPage(pBt, pgnoRoot, &pRoot);
+      if( rc!=SQLITE_OK ){
+        return rc;
+      }
+      rc = sqlite3pager_write(pRoot->aData);
+      if( rc!=SQLITE_OK ){
+        releasePage(pRoot);
+        return rc;
+      }
+    }else{
+      pRoot = pPageMove;
+    } 
+
     rc = ptrmapPut(pBt, pgnoRoot, PTRMAP_ROOTPAGE, 0);
     if( rc ){
-      sqlite3pager_unref(pRoot->aData);
+      releasePage(pRoot);
+      return rc;
+    }
+    rc = sqlite3BtreeUpdateMeta(pBt, 4, pgnoRoot);
+    if( rc ){
+      releasePage(pRoot);
       return rc;
     }
+  }else{
+    rc = allocatePage(pBt, &pRoot, &pgnoRoot, 1);
+    if( rc ) return rc;
   }
 #endif
   assert( sqlite3pager_iswriteable(pRoot->aData) );
@@ -4307,6 +4412,7 @@ int sqlite3BtreeDropTable(Btree *pBt, int iTable){
   int rc;
   MemPage *pPage;
   BtCursor *pCur;
+/* TODO: Disallow schema modifications if there are open cursors */
   if( pBt->inTrans!=TRANS_WRITE ){
     return pBt->readOnly ? SQLITE_READONLY : SQLITE_ERROR;
   }
@@ -4351,7 +4457,9 @@ int sqlite3BtreeGetMeta(Btree *pBt, int idx, u32 *pMeta){
 
   /* The current implementation is unable to handle writes to an autovacuumed
   ** database.  So make such a database readonly. */
+#ifdef SQLITE_OMIT_AUTOVACUUM
   if( idx==4 && *pMeta>0 ) pBt->readOnly = 1;
+#endif
 
   return SQLITE_OK;
 }
index 341eb92ef075cca241c1d13b7cde8f23e3a238d9..9af1ab6522ad1cda02a793a9680c69abf8e7c448 100644 (file)
@@ -11,7 +11,7 @@
 # This file implements regression tests for SQLite library.  The
 # focus of this file is testing the SELECT statement.
 #
-# $Id: autovacuum.test,v 1.4 2004/11/03 09:30:55 danielk1977 Exp $
+# $Id: autovacuum.test,v 1.5 2004/11/04 02:57:35 danielk1977 Exp $
 
 set testdir [file dirname $argv0]
 source $testdir/tester.tcl
@@ -112,5 +112,38 @@ foreach delete_order $delete_orders {
   } {4}
 }
 
+# Tests autovacuum-2.* test that root pages are allocated correctly at
+# the start of the file.
+do_test autovacuum-2.1 {
+  for {set i 0} {$i<5} {incr i} {
+    execsql "
+      INSERT INTO av1 VALUES('[make_str abc 1000]')
+    "
+  }
+  file_pages 
+} {14}
+
+for {set i 5} {$i < 15} {incr i} {
+  set tablename "av$i"
+  
+  do_test autovacuum-2.$i.2 {
+    execsql "
+      CREATE TABLE $tablename (a);
+      SELECT rootpage FROM sqlite_master WHERE name = '$tablename';
+    "
+  } $i
+
+  do_test autovacuum-2.$i.3 {
+    file_pages 
+  } [expr $i+10]
+
+  do_test autovacuum-2.$i.4 {
+    execsql {
+      pragma integrity_check
+    }
+  } {ok}
+}
+
+
 finish_test