SQLite

Check-in [c9dcf2b926]
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
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: c9dcf2b926c99ff9cee68589f364461ab2a1d11f
User & Date: drh 2007-06-16 04:42:12.000
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: 57bf8204cd 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: c9dcf2b926 user: drh tags: trunk)
03:06
Additional debugging instrumentation added to the pager. (CVS 4078) (check-in: dcdb20f81a user: drh tags: trunk)
Changes
Unified Diff Ignore Whitespace Patch
Changes to src/pager.c.
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
** The pager is used to access a database disk file.  It implements
** atomic commit and rollback through the use of a journal file that
** is separate from the database file.  The pager also implements file
** locking to prevent two processes from writing the same database
** file simultaneously, or one process from reading the database while
** another is writing.
**
** @(#) $Id: pager.c,v 1.344 2007/06/16 03:06:28 drh Exp $
*/
#ifndef SQLITE_OMIT_DISKIO
#include "sqliteInt.h"
#include "os.h"
#include "pager.h"
#include <assert.h>
#include <string.h>







|







14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
** The pager is used to access a database disk file.  It implements
** atomic commit and rollback through the use of a journal file that
** is separate from the database file.  The pager also implements file
** locking to prevent two processes from writing the same database
** file simultaneously, or one process from reading the database while
** another is writing.
**
** @(#) $Id: pager.c,v 1.345 2007/06/16 04:42:12 drh Exp $
*/
#ifndef SQLITE_OMIT_DISKIO
#include "sqliteInt.h"
#include "os.h"
#include "pager.h"
#include <assert.h>
#include <string.h>
1102
1103
1104
1105
1106
1107
1108
1109
1110

1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
  ** and the main file. The page is then marked not dirty.
  **
  ** Ticket #1171:  The statement journal might contain page content that is
  ** different from the page content at the start of the transaction.
  ** This occurs when a page is changed prior to the start of a statement
  ** then changed again within the statement.  When rolling back such a
  ** statement we must not write to the original database unless we know
  ** for certain that original page contents are in the main rollback
  ** journal.  Otherwise, if a full ROLLBACK occurs after the statement

  ** rollback the full ROLLBACK will not restore the page to its original
  ** content.  Two conditions must be met before writing to the database
  ** files. (1) the database must be locked.  (2) we know that the original
  ** page content is in the main journal either because the page is not in
  ** cache or else it is marked as needSync==0.
  */
  pPg = pager_lookup(pPager, pgno);
  PAGERTRACE4("PLAYBACK %d page %d hash(%08x)\n",
               PAGERID(pPager), pgno, pager_datahash(pPager->pageSize, aData));
  if( pPager->state>=PAGER_EXCLUSIVE && (pPg==0 || pPg->needSync==0) ){
    rc = sqlite3OsSeek(pPager->fd, (pgno-1)*(i64)pPager->pageSize);
    if( rc==SQLITE_OK ){







|
|
>
|
|
|
|
|







1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
  ** and the main file. The page is then marked not dirty.
  **
  ** Ticket #1171:  The statement journal might contain page content that is
  ** different from the page content at the start of the transaction.
  ** This occurs when a page is changed prior to the start of a statement
  ** then changed again within the statement.  When rolling back such a
  ** statement we must not write to the original database unless we know
  ** for certain that original page contents are synced into the main rollback
  ** journal.  Otherwise, a power loss might leave modified data in the
  ** database file without an entry in the rollback journal that can
  ** restore the database to its original form.  Two conditions must be
  ** met before writing to the database files. (1) the database must be
  ** locked.  (2) we know that the original page content is fully synced
  ** in the main journal either because the page is not in cache or else
  ** the page is marked as needSync==0.
  */
  pPg = pager_lookup(pPager, pgno);
  PAGERTRACE4("PLAYBACK %d page %d hash(%08x)\n",
               PAGERID(pPager), pgno, pager_datahash(pPager->pageSize, aData));
  if( pPager->state>=PAGER_EXCLUSIVE && (pPg==0 || pPg->needSync==0) ){
    rc = sqlite3OsSeek(pPager->fd, (pgno-1)*(i64)pPager->pageSize);
    if( rc==SQLITE_OK ){
4239
4240
4241
4242
4243
4244
4245
4246
4247
4248


4249
4250
4251
4252
4253
4254
4255
4256
4257
4258
4259
4260
4261
4262
4263
4264
4265
4266
4267
4268
4269
4270
  pPager->xCodec = xCodec;
  pPager->pCodecArg = pCodecArg;
}
#endif

#ifndef SQLITE_OMIT_AUTOVACUUM
/*
** Move the page identified by pData to location pgno in the file. 
**
** There must be no references to the current page pgno. If current page


** pgno is not already in the rollback journal, it is not written there by
** by this routine. The same applies to the page pData refers to on entry to
** this routine.
**
** References to the page refered to by pData remain valid. Updating any
** meta-data associated with page pData (i.e. data stored in the nExtra bytes
** allocated along with the page) is the responsibility of the caller.
**
** A transaction must be active when this routine is called. It used to be
** required that a statement transaction was not active, but this restriction
** has been removed (CREATE INDEX needs to move a page when a statement
** transaction is active).
*/
int sqlite3PagerMovepage(Pager *pPager, DbPage *pPg, Pgno pgno){
  PgHdr *pPgOld; 
  int h;
  Pgno needSyncPgno = 0;

  assert( pPg->nRef>0 );

  PAGERTRACE5("MOVE %d page %d (needSync=%d) moves to %d\n", 
      PAGERID(pPager), pPg->pgno, pPg->needSync, pgno);







|

|
>
>
|
<
<

|
|








|







4240
4241
4242
4243
4244
4245
4246
4247
4248
4249
4250
4251
4252


4253
4254
4255
4256
4257
4258
4259
4260
4261
4262
4263
4264
4265
4266
4267
4268
4269
4270
4271
  pPager->xCodec = xCodec;
  pPager->pCodecArg = pCodecArg;
}
#endif

#ifndef SQLITE_OMIT_AUTOVACUUM
/*
** Move the page pPg to location pgno in the file. 
**
** There must be no references to the page previously located at
** pgno (which we call pPgOld) though that page is allowed to be
** in cache.  If the page previous located at pgno is not already
** in the rollback journal, it is not put there by by this routine.


**
** References to the page pPg remain valid. Updating any
** meta-data associated with pPg (i.e. data stored in the nExtra bytes
** allocated along with the page) is the responsibility of the caller.
**
** A transaction must be active when this routine is called. It used to be
** required that a statement transaction was not active, but this restriction
** has been removed (CREATE INDEX needs to move a page when a statement
** transaction is active).
*/
int sqlite3PagerMovepage(Pager *pPager, DbPage *pPg, Pgno pgno){
  PgHdr *pPgOld;  /* The page being overwritten. */
  int h;
  Pgno needSyncPgno = 0;

  assert( pPg->nRef>0 );

  PAGERTRACE5("MOVE %d page %d (needSync=%d) moves to %d\n", 
      PAGERID(pPager), pPg->pgno, pPg->needSync, pgno);
4282
4283
4284
4285
4286
4287
4288

4289
4290
4291
4292
4293
4294
4295
4296
4297
4298
4299







4300
4301
4302
4303
4304
4305
4306
  unlinkHashChain(pPager, pPg);

  /* If the cache contains a page with page-number pgno, remove it
  ** from it's hash chain. Also, if the PgHdr.needSync was set for 
  ** page pgno before the 'move' operation, it needs to be retained 
  ** for the page moved there.
  */

  pPgOld = pager_lookup(pPager, pgno);
  if( pPgOld ){
    assert( pPgOld->nRef==0 );
    unlinkHashChain(pPager, pPgOld);
    makeClean(pPgOld);
    if( pPgOld->needSync ){
      assert( pPgOld->inJournal );
      pPg->inJournal = 1;
      pPg->needSync = 1;
      assert( pPager->needSync );
    }







  }

  /* Change the page number for pPg and insert it into the new hash-chain. */
  assert( pgno!=0 );
  pPg->pgno = pgno;
  h = pgno & (pPager->nHash-1);
  if( pPager->aHash[h] ){







>





|
|
<
|
<
|
>
>
>
>
>
>
>







4283
4284
4285
4286
4287
4288
4289
4290
4291
4292
4293
4294
4295
4296
4297

4298

4299
4300
4301
4302
4303
4304
4305
4306
4307
4308
4309
4310
4311
4312
4313
  unlinkHashChain(pPager, pPg);

  /* If the cache contains a page with page-number pgno, remove it
  ** from it's hash chain. Also, if the PgHdr.needSync was set for 
  ** page pgno before the 'move' operation, it needs to be retained 
  ** for the page moved there.
  */
  pPg->needSync = 0;
  pPgOld = pager_lookup(pPager, pgno);
  if( pPgOld ){
    assert( pPgOld->nRef==0 );
    unlinkHashChain(pPager, pPgOld);
    makeClean(pPgOld);
    pPg->needSync = pPgOld->needSync;
  }else{

    pPg->needSync = 0;

  }
  if( pPager->aInJournal && (int)pgno<=pPager->origDbSize ){
    pPg->inJournal =  (pPager->aInJournal[pgno/8] & (1<<(pgno&7)))!=0;
  }else if( (int)pgno>=pPager->origDbSize ){
    pPg->inJournal = 1;
  }else{
    pPg->inJournal = 0;
    assert( pPg->needSync==0 );
  }

  /* Change the page number for pPg and insert it into the new hash-chain. */
  assert( pgno!=0 );
  pPg->pgno = pgno;
  h = pgno & (pPager->nHash-1);
  if( pPager->aHash[h] ){