/ Check-in [b6eb4bf8]
Login

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

Overview
Comment:Fix a pager bug that might have made multi-database commits non-atomic if a power failure occurred at just the wrong moment. (CVS 1900)
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1:b6eb4bf8c7763ef73723fc3d3697af435c19bae4
User & Date: drh 2004-08-21 19:20:42
Context
2004-08-24
15:23
Fix a bug in the parsing of wildcards that begin with '$'. (CVS 1901) check-in: 054dd890 user: drh tags: trunk
2004-08-21
19:20
Fix a pager bug that might have made multi-database commits non-atomic if a power failure occurred at just the wrong moment. (CVS 1900) check-in: b6eb4bf8 user: drh tags: trunk
17:54
Optimizations to the code generator. (CVS 1899) check-in: bd6649c5 user: drh tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to src/os_test.c.

92
93
94
95
96
97
98
99
100
101
102

103


104
105
106
107
108
109
110
...
153
154
155
156
157
158
159

160
161
162
163
164
165
166
...
279
280
281
282
283
284
285

286
287
288
289
290
291
292
...
382
383
384
385
386
387
388


389
390
391
392
393
394
395
396
  sqlite3OsEnterMutex();
  n = strlen(zCrashFile);
  if( zCrashFile[n-1]=='*' ){
    n--;
  }else if( strlen(zPath)>n ){
    n = strlen(zPath);
  }
  r = ( 
    iCrashDelay>0 &&
    !strncmp(zPath, zCrashFile, n) &&
    --iCrashDelay==0

  )?1:0;


  sqlite3OsLeaveMutex();
  return r;
}


static OsTestFile *pAllFiles = 0;

................................................................................

/*
** Load block 'blk' into the cache of pFile.
*/
static int cacheBlock(OsTestFile *pFile, int blk){
  if( blk>=pFile->nBlk ){
    int n = ((pFile->nBlk * 2) + 100 + blk);

    pFile->apBlk = (u8 **)sqliteRealloc(pFile->apBlk, n * sizeof(u8*));
    if( !pFile->apBlk ) return SQLITE_NOMEM;
    memset(&pFile->apBlk[pFile->nBlk], 0, (n - pFile->nBlk)*sizeof(u8*));
    pFile->nBlk = n;
  }

  if( !pFile->apBlk[blk] ){
................................................................................

/*
** Close the file.
*/
int sqlite3OsClose(OsFile *id){
  if( !(*id) ) return SQLITE_OK;
  if( (*id)->fd.isOpen ){

    writeCache(*id);
    sqlite3RealClose(&(*id)->fd);
  }
  closeFile(id);
  return SQLITE_OK;
}

................................................................................
}

/*
** Sync the file. First flush the write-cache to disk, then call the
** real sync() function.
*/
int sqlite3OsSync(OsFile *id){


  int rc = writeCache(*id);
  if( rc!=SQLITE_OK ) return rc;
  rc = sqlite3RealSync(&(*id)->fd);
  return rc;
}

/*
** Truncate the file. Set the internal OsFile.nMaxWrite variable to the new







|
<
|
|
>
|
>
>







 







>







 







>







 







>
>
|







92
93
94
95
96
97
98
99

100
101
102
103
104
105
106
107
108
109
110
111
112
...
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
...
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
...
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
  sqlite3OsEnterMutex();
  n = strlen(zCrashFile);
  if( zCrashFile[n-1]=='*' ){
    n--;
  }else if( strlen(zPath)>n ){
    n = strlen(zPath);
  }
  r = 0;

  if( iCrashDelay>0 && strncmp(zPath, zCrashFile, n)==0 ){
    iCrashDelay--;
    if( iCrashDelay<=0 ){
      r = 1;
    }
  }
  sqlite3OsLeaveMutex();
  return r;
}


static OsTestFile *pAllFiles = 0;

................................................................................

/*
** Load block 'blk' into the cache of pFile.
*/
static int cacheBlock(OsTestFile *pFile, int blk){
  if( blk>=pFile->nBlk ){
    int n = ((pFile->nBlk * 2) + 100 + blk);
    /* if( pFile->nBlk==0 ){ printf("DIRTY %s\n", pFile->zName); } */
    pFile->apBlk = (u8 **)sqliteRealloc(pFile->apBlk, n * sizeof(u8*));
    if( !pFile->apBlk ) return SQLITE_NOMEM;
    memset(&pFile->apBlk[pFile->nBlk], 0, (n - pFile->nBlk)*sizeof(u8*));
    pFile->nBlk = n;
  }

  if( !pFile->apBlk[blk] ){
................................................................................

/*
** Close the file.
*/
int sqlite3OsClose(OsFile *id){
  if( !(*id) ) return SQLITE_OK;
  if( (*id)->fd.isOpen ){
    /* printf("CLOSE %s (%d blocks)\n", (*id)->zName, (*id)->nBlk); */
    writeCache(*id);
    sqlite3RealClose(&(*id)->fd);
  }
  closeFile(id);
  return SQLITE_OK;
}

................................................................................
}

/*
** Sync the file. First flush the write-cache to disk, then call the
** real sync() function.
*/
int sqlite3OsSync(OsFile *id){
  int rc;
  /* printf("SYNC %s (%d blocks)\n", (*id)->zName, (*id)->nBlk); */
  rc = writeCache(*id);
  if( rc!=SQLITE_OK ) return rc;
  rc = sqlite3RealSync(&(*id)->fd);
  return rc;
}

/*
** Truncate the file. Set the internal OsFile.nMaxWrite variable to the new

Changes to src/pager.c.

14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
...
218
219
220
221
222
223
224

225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
...
656
657
658
659
660
661
662

663
664
665
666
667
668
669
** 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.159 2004/08/19 13:29:15 drh Exp $
*/
#include "os.h"         /* Must be first to enable large file support */
#include "sqliteInt.h"
#include "pager.h"
#include <assert.h>
#include <string.h>

................................................................................
  u8 readOnly;                /* True for a read-only database */
  u8 needSync;                /* True if an fsync() is needed on the journal */
  u8 dirtyCache;              /* True if cached pages have changed */
  u8 alwaysRollback;          /* Disable dont_rollback() for all pages */
  u8 memDb;                   /* True to inhibit all file I/O */
  u8 *aInJournal;             /* One bit for each page in the database file */
  u8 *aInStmt;                /* One bit for each page in the database */

  BusyHandler *pBusyHandler;  /* Pointer to sqlite.busyHandler */
  PgHdr *pFirst, *pLast;      /* List of free pages */
  PgHdr *pFirstSynced;        /* First free page with PgHdr.needSync==0 */
  PgHdr *pAll;                /* List of all pages */
  PgHdr *pStmt;               /* List of pages in the statement subjournal */
  PgHdr *aHash[N_PG_HASH];    /* Hash table to map page number to PgHdr */
  off_t journalOff;           /* Current byte offset in the journal file */
  off_t journalHdr;           /* Byte offset to previous journal header */
  off_t stmtHdrOff;           /* First journal header written this statement */
  off_t stmtCksum;            /* cksumInit when statement was started */
  int sectorSize;             /* Assumed sector size during rollback */
  u8 setMaster;               /* True if a m-j name has been written to jrnl */
};

/*
** These are bits that can be set in Pager.errMask.
*/
#define PAGER_ERR_FULL     0x01  /* a write() failed */
#define PAGER_ERR_MEM      0x02  /* malloc() failed */
................................................................................
  rc = write32bits(&pPager->jfd, len);
  if( rc!=SQLITE_OK ) return rc;

  rc = write32bits(&pPager->jfd, cksum);
  if( rc!=SQLITE_OK ) return rc;

  rc = sqlite3OsWrite(&pPager->jfd, aJournalMagic, sizeof(aJournalMagic));

  return rc;
}

/*
** Add or remove a page from the list of all pages that are in the
** statement journal.
**







|







 







>











<







 







>







14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
...
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236

237
238
239
240
241
242
243
...
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
** 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.160 2004/08/21 19:20:42 drh Exp $
*/
#include "os.h"         /* Must be first to enable large file support */
#include "sqliteInt.h"
#include "pager.h"
#include <assert.h>
#include <string.h>

................................................................................
  u8 readOnly;                /* True for a read-only database */
  u8 needSync;                /* True if an fsync() is needed on the journal */
  u8 dirtyCache;              /* True if cached pages have changed */
  u8 alwaysRollback;          /* Disable dont_rollback() for all pages */
  u8 memDb;                   /* True to inhibit all file I/O */
  u8 *aInJournal;             /* One bit for each page in the database file */
  u8 *aInStmt;                /* One bit for each page in the database */
  u8 setMaster;               /* True if a m-j name has been written to jrnl */
  BusyHandler *pBusyHandler;  /* Pointer to sqlite.busyHandler */
  PgHdr *pFirst, *pLast;      /* List of free pages */
  PgHdr *pFirstSynced;        /* First free page with PgHdr.needSync==0 */
  PgHdr *pAll;                /* List of all pages */
  PgHdr *pStmt;               /* List of pages in the statement subjournal */
  PgHdr *aHash[N_PG_HASH];    /* Hash table to map page number to PgHdr */
  off_t journalOff;           /* Current byte offset in the journal file */
  off_t journalHdr;           /* Byte offset to previous journal header */
  off_t stmtHdrOff;           /* First journal header written this statement */
  off_t stmtCksum;            /* cksumInit when statement was started */
  int sectorSize;             /* Assumed sector size during rollback */

};

/*
** These are bits that can be set in Pager.errMask.
*/
#define PAGER_ERR_FULL     0x01  /* a write() failed */
#define PAGER_ERR_MEM      0x02  /* malloc() failed */
................................................................................
  rc = write32bits(&pPager->jfd, len);
  if( rc!=SQLITE_OK ) return rc;

  rc = write32bits(&pPager->jfd, cksum);
  if( rc!=SQLITE_OK ) return rc;

  rc = sqlite3OsWrite(&pPager->jfd, aJournalMagic, sizeof(aJournalMagic));
  pPager->needSync = 1;
  return rc;
}

/*
** Add or remove a page from the list of all pages that are in the
** statement journal.
**

Changes to test/crash.test.

16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
...
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
# module "crashtest" compiled with the special "os_test.c" backend is used.
# The os_test.c simulates the kind of file corruption that can occur
# when writes are happening at the moment of power loss.
# 
# The special crash-test module with its os_test.c backend only works
# on Unix.
#
# $Id: crash.test,v 1.8 2004/08/21 17:54:46 drh Exp $

set testdir [file dirname $argv0]
source $testdir/tester.tcl

# set repeats 100
set repeats 10

................................................................................
    signature
  } $sig
  do_test crash-4.1.$i.3 {
    signature2
  } $sig2
} 
set i 0
set i 55
while {[incr i]} {
  set sig [signature]
  set sig2 [signature2]
  set ::fin 0
  do_test crash-4.2.$i.1 {
     set c [crashsql $i test2.db-journal "
       ATTACH 'test2.db' AS aux;







|







 







<







16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
...
269
270
271
272
273
274
275

276
277
278
279
280
281
282
# module "crashtest" compiled with the special "os_test.c" backend is used.
# The os_test.c simulates the kind of file corruption that can occur
# when writes are happening at the moment of power loss.
# 
# The special crash-test module with its os_test.c backend only works
# on Unix.
#
# $Id: crash.test,v 1.9 2004/08/21 19:20:42 drh Exp $

set testdir [file dirname $argv0]
source $testdir/tester.tcl

# set repeats 100
set repeats 10

................................................................................
    signature
  } $sig
  do_test crash-4.1.$i.3 {
    signature2
  } $sig2
} 
set i 0

while {[incr i]} {
  set sig [signature]
  set sig2 [signature2]
  set ::fin 0
  do_test crash-4.2.$i.1 {
     set c [crashsql $i test2.db-journal "
       ATTACH 'test2.db' AS aux;