/ Check-in [c9dcf2b9]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Fix a database corruption problem that can occur in auto-vacuum mode when a malloc() failure causes a statement rollback, additional statements are run in the same transaction, then the total transaction rolls back. (CVS 4079)
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: c9dcf2b926c99ff9cee68589f364461ab2a1d11f
User & Date: drh 2007-06-16 04:42:12
Context
2007-06-16
11:17
A minor logic correction in the previous check-in. Also added a lengthy comment describing the meanings of various flags in the {quote: PgHdr} structure. (CVS 4080) check-in: 57bf8204 user: drh tags: trunk
04:42
Fix a database corruption problem that can occur in auto-vacuum mode when a malloc() failure causes a statement rollback, additional statements are run in the same transaction, then the total transaction rolls back. (CVS 4079) check-in: c9dcf2b9 user: drh tags: trunk
03:06
Additional debugging instrumentation added to the pager. (CVS 4078) check-in: dcdb20f8 user: drh tags: trunk
Changes
Hide Diffs Side-by-Side Diffs Show Whitespace Changes Patch

Changes to src/pager.c.

    14     14   ** The pager is used to access a database disk file.  It implements
    15     15   ** atomic commit and rollback through the use of a journal file that
    16     16   ** is separate from the database file.  The pager also implements file
    17     17   ** locking to prevent two processes from writing the same database
    18     18   ** file simultaneously, or one process from reading the database while
    19     19   ** another is writing.
    20     20   **
    21         -** @(#) $Id: pager.c,v 1.344 2007/06/16 03:06:28 drh Exp $
           21  +** @(#) $Id: pager.c,v 1.345 2007/06/16 04:42:12 drh Exp $
    22     22   */
    23     23   #ifndef SQLITE_OMIT_DISKIO
    24     24   #include "sqliteInt.h"
    25     25   #include "os.h"
    26     26   #include "pager.h"
    27     27   #include <assert.h>
    28     28   #include <string.h>
................................................................................
  1102   1102     ** and the main file. The page is then marked not dirty.
  1103   1103     **
  1104   1104     ** Ticket #1171:  The statement journal might contain page content that is
  1105   1105     ** different from the page content at the start of the transaction.
  1106   1106     ** This occurs when a page is changed prior to the start of a statement
  1107   1107     ** then changed again within the statement.  When rolling back such a
  1108   1108     ** statement we must not write to the original database unless we know
  1109         -  ** for certain that original page contents are in the main rollback
  1110         -  ** journal.  Otherwise, if a full ROLLBACK occurs after the statement
  1111         -  ** rollback the full ROLLBACK will not restore the page to its original
  1112         -  ** content.  Two conditions must be met before writing to the database
  1113         -  ** files. (1) the database must be locked.  (2) we know that the original
  1114         -  ** page content is in the main journal either because the page is not in
  1115         -  ** cache or else it is marked as needSync==0.
         1109  +  ** for certain that original page contents are synced into the main rollback
         1110  +  ** journal.  Otherwise, a power loss might leave modified data in the
         1111  +  ** database file without an entry in the rollback journal that can
         1112  +  ** restore the database to its original form.  Two conditions must be
         1113  +  ** met before writing to the database files. (1) the database must be
         1114  +  ** locked.  (2) we know that the original page content is fully synced
         1115  +  ** in the main journal either because the page is not in cache or else
         1116  +  ** the page is marked as needSync==0.
  1116   1117     */
  1117   1118     pPg = pager_lookup(pPager, pgno);
  1118   1119     PAGERTRACE4("PLAYBACK %d page %d hash(%08x)\n",
  1119   1120                  PAGERID(pPager), pgno, pager_datahash(pPager->pageSize, aData));
  1120   1121     if( pPager->state>=PAGER_EXCLUSIVE && (pPg==0 || pPg->needSync==0) ){
  1121   1122       rc = sqlite3OsSeek(pPager->fd, (pgno-1)*(i64)pPager->pageSize);
  1122   1123       if( rc==SQLITE_OK ){
................................................................................
  4239   4240     pPager->xCodec = xCodec;
  4240   4241     pPager->pCodecArg = pCodecArg;
  4241   4242   }
  4242   4243   #endif
  4243   4244   
  4244   4245   #ifndef SQLITE_OMIT_AUTOVACUUM
  4245   4246   /*
  4246         -** Move the page identified by pData to location pgno in the file. 
         4247  +** Move the page pPg to location pgno in the file. 
  4247   4248   **
  4248         -** There must be no references to the current page pgno. If current page
  4249         -** pgno is not already in the rollback journal, it is not written there by
  4250         -** by this routine. The same applies to the page pData refers to on entry to
  4251         -** this routine.
         4249  +** There must be no references to the page previously located at
         4250  +** pgno (which we call pPgOld) though that page is allowed to be
         4251  +** in cache.  If the page previous located at pgno is not already
         4252  +** in the rollback journal, it is not put there by by this routine.
  4252   4253   **
  4253         -** References to the page refered to by pData remain valid. Updating any
  4254         -** meta-data associated with page pData (i.e. data stored in the nExtra bytes
         4254  +** References to the page pPg remain valid. Updating any
         4255  +** meta-data associated with pPg (i.e. data stored in the nExtra bytes
  4255   4256   ** allocated along with the page) is the responsibility of the caller.
  4256   4257   **
  4257   4258   ** A transaction must be active when this routine is called. It used to be
  4258   4259   ** required that a statement transaction was not active, but this restriction
  4259   4260   ** has been removed (CREATE INDEX needs to move a page when a statement
  4260   4261   ** transaction is active).
  4261   4262   */
  4262   4263   int sqlite3PagerMovepage(Pager *pPager, DbPage *pPg, Pgno pgno){
  4263         -  PgHdr *pPgOld; 
         4264  +  PgHdr *pPgOld;  /* The page being overwritten. */
  4264   4265     int h;
  4265   4266     Pgno needSyncPgno = 0;
  4266   4267   
  4267   4268     assert( pPg->nRef>0 );
  4268   4269   
  4269   4270     PAGERTRACE5("MOVE %d page %d (needSync=%d) moves to %d\n", 
  4270   4271         PAGERID(pPager), pPg->pgno, pPg->needSync, pgno);
................................................................................
  4282   4283     unlinkHashChain(pPager, pPg);
  4283   4284   
  4284   4285     /* If the cache contains a page with page-number pgno, remove it
  4285   4286     ** from it's hash chain. Also, if the PgHdr.needSync was set for 
  4286   4287     ** page pgno before the 'move' operation, it needs to be retained 
  4287   4288     ** for the page moved there.
  4288   4289     */
         4290  +  pPg->needSync = 0;
  4289   4291     pPgOld = pager_lookup(pPager, pgno);
  4290   4292     if( pPgOld ){
  4291   4293       assert( pPgOld->nRef==0 );
  4292   4294       unlinkHashChain(pPager, pPgOld);
  4293   4295       makeClean(pPgOld);
  4294         -    if( pPgOld->needSync ){
  4295         -      assert( pPgOld->inJournal );
  4296         -      pPg->inJournal = 1;
  4297         -      pPg->needSync = 1;
  4298         -      assert( pPager->needSync );
         4296  +    pPg->needSync = pPgOld->needSync;
         4297  +  }else{
         4298  +    pPg->needSync = 0;
  4299   4299       }
         4300  +  if( pPager->aInJournal && (int)pgno<=pPager->origDbSize ){
         4301  +    pPg->inJournal =  (pPager->aInJournal[pgno/8] & (1<<(pgno&7)))!=0;
         4302  +  }else if( (int)pgno>=pPager->origDbSize ){
         4303  +    pPg->inJournal = 1;
         4304  +  }else{
         4305  +    pPg->inJournal = 0;
         4306  +    assert( pPg->needSync==0 );
  4300   4307     }
  4301   4308   
  4302   4309     /* Change the page number for pPg and insert it into the new hash-chain. */
  4303   4310     assert( pgno!=0 );
  4304   4311     pPg->pgno = pgno;
  4305   4312     h = pgno & (pPager->nHash-1);
  4306   4313     if( pPager->aHash[h] ){