/ Check-in [bdf5cb8d]
Login
SQLite training in Houston TX on 2019-11-05 (details)
Part of the 2019 Tcl Conference

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

Overview
Comment:Add tests to simulate power-failure on devices that support IOCAP_SEQUENTIAL or IOCAP_SAFE_APPEND. (CVS 4284)
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: bdf5cb8d25d93d48220ce46acad2ccf967a87843
User & Date: danielk1977 2007-08-24 08:15:54
Context
2007-08-24
11:43
Remove unnecessary sqlite3MallocDisallow() that was preventing win32 from running. (CVS 4285) check-in: eb6c98fc user: drh tags: trunk
08:15
Add tests to simulate power-failure on devices that support IOCAP_SEQUENTIAL or IOCAP_SAFE_APPEND. (CVS 4284) check-in: bdf5cb8d user: danielk1977 tags: trunk
04:15
Bug fix in the memory leak trace output. (CVS 4283) check-in: a1b495c2 user: drh tags: trunk
Changes
Hide Diffs Unified Diffs Show Whitespace Changes Patch

Changes to src/journal.c.

6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
...
215
216
217
218
219
220
221











222
223
224
225
226
227
228
229
230
231
**
**    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.
**
*************************************************************************
**
** @(#) $Id: journal.c,v 1.2 2007/08/23 08:06:45 danielk1977 Exp $
*/

#ifdef SQLITE_ENABLE_ATOMIC_WRITE

/*
** This file implements a special kind of sqlite3_file object used
** by SQLite to create journal files if the atomic-write optimization
................................................................................
  p->pMethod = &JournalFileMethods;
  p->nBuf = nBuf;
  p->flags = flags;
  p->zJournal = zName;
  p->pVfs = pVfs;
  return SQLITE_OK;
}












/* 
** Return the number of bytes required to store a JournalFile that uses vfs
** pVfs to create the underlying on-disk files.
*/
int sqlite3JournalSize(sqlite3_vfs *pVfs){
  return (pVfs->szOsFile+sizeof(JournalFile));
}
#endif








|







 







>
>
>
>
>
>
>
>
>
>
>










6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
...
215
216
217
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
**
**    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.
**
*************************************************************************
**
** @(#) $Id: journal.c,v 1.3 2007/08/24 08:15:54 danielk1977 Exp $
*/

#ifdef SQLITE_ENABLE_ATOMIC_WRITE

/*
** This file implements a special kind of sqlite3_file object used
** by SQLite to create journal files if the atomic-write optimization
................................................................................
  p->pMethod = &JournalFileMethods;
  p->nBuf = nBuf;
  p->flags = flags;
  p->zJournal = zName;
  p->pVfs = pVfs;
  return SQLITE_OK;
}

/*
** If the argument p points to a JournalFile structure, and the underlying
** file has not yet been created, create it now.
*/
int sqlite3JournalCreate(sqlite3_file *p){
  if( p->pMethods!=&JournalFileMethods ){
    return SQLITE_OK;
  }
  return createFile((JournalFile *)p);
}

/* 
** Return the number of bytes required to store a JournalFile that uses vfs
** pVfs to create the underlying on-disk files.
*/
int sqlite3JournalSize(sqlite3_vfs *pVfs){
  return (pVfs->szOsFile+sizeof(JournalFile));
}
#endif

Changes to src/pager.c.

14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
....
4188
4189
4190
4191
4192
4193
4194
4195
4196
4197
4198
4199
4200
4201
4202
4203
4204
4205
4206
4207
4208


4209

4210
4211

4212
4213
4214
4215
4216
4217
4218
4219
4220
4221
4222
4223
4224





4225
4226
4227
4228
4229
4230
4231
4232
4233

4234
4235
4236
4237
4238
4239
4240
** 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.372 2007/08/24 03:51:34 drh Exp $
*/
#ifndef SQLITE_OMIT_DISKIO
#include "sqliteInt.h"
#include <assert.h>
#include <string.h>

/*
................................................................................
  pagerEnter(pPager);

  /* If this is an in-memory db, or no pages have been written to, or this
  ** function has already been called, it is a no-op.
  */
  if( pPager->state!=PAGER_SYNCED && !MEMDB && pPager->dirtyCache ){
    PgHdr *pPg;
    assert( pPager->journalOpen );

#ifdef SQLITE_ENABLE_ATOMIC_WRITE
    /* The atomic-write optimization can be used if all of the
    ** following are true:
    **
    **    + The file-system supports the atomic-write property for
    **      blocks of size page-size, and
    **    + This commit is not part of a multi-file transaction, and
    **    + Exactly one page has been modified and store in the journal file.
    **
    ** If the optimization can be used, then the journal file will never
    ** be created for this transaction.
    */


    if( !zMaster && pPager->journalOff==jrnlBufferSize(pPager) && nTrunc==0

      && (0==pPager->pDirty || 0==pPager->pDirty->pDirty)
    ){

      /* Update the nRec field in the journal file. */
      int offset = pPager->journalHdr + sizeof(aJournalMagic);
      assert(pPager->nRec==1);
      rc = write32bits(pPager->jfd, offset, pPager->nRec);

      /* Update the db file change counter. The following call will modify
      ** the in-memory representation of page 1 to include the updated
      ** change counter and then write page 1 directly to the database
      ** file. Because of the atomic-write property of the host file-system, 
      ** this is safe.
      */
      rc = pager_incr_changecounter(pPager, 1);
    }else 





#endif

    /* If a master journal file name has already been written to the
    ** journal file, then no sync is required. This happens when it is
    ** written, then the process fails to upgrade from a RESERVED to an
    ** EXCLUSIVE lock. The next time the process tries to commit the
    ** transaction the m-j name will have already been written.
    */
    if( !pPager->setMaster ){

      rc = pager_incr_changecounter(pPager, 0);
      if( rc!=SQLITE_OK ) goto sync_exit;
#ifndef SQLITE_OMIT_AUTOVACUUM
      if( nTrunc!=0 ){
        /* If this transaction has made the database smaller, then all pages
        ** being discarded by the truncation must be written to the journal
        ** file.







|







 







<













>
>
|
>
|
|
>












|
>
>
>
>
>









>







14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
....
4188
4189
4190
4191
4192
4193
4194

4195
4196
4197
4198
4199
4200
4201
4202
4203
4204
4205
4206
4207
4208
4209
4210
4211
4212
4213
4214
4215
4216
4217
4218
4219
4220
4221
4222
4223
4224
4225
4226
4227
4228
4229
4230
4231
4232
4233
4234
4235
4236
4237
4238
4239
4240
4241
4242
4243
4244
4245
4246
4247
4248
4249
** 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.373 2007/08/24 08:15:54 danielk1977 Exp $
*/
#ifndef SQLITE_OMIT_DISKIO
#include "sqliteInt.h"
#include <assert.h>
#include <string.h>

/*
................................................................................
  pagerEnter(pPager);

  /* If this is an in-memory db, or no pages have been written to, or this
  ** function has already been called, it is a no-op.
  */
  if( pPager->state!=PAGER_SYNCED && !MEMDB && pPager->dirtyCache ){
    PgHdr *pPg;


#ifdef SQLITE_ENABLE_ATOMIC_WRITE
    /* The atomic-write optimization can be used if all of the
    ** following are true:
    **
    **    + The file-system supports the atomic-write property for
    **      blocks of size page-size, and
    **    + This commit is not part of a multi-file transaction, and
    **    + Exactly one page has been modified and store in the journal file.
    **
    ** If the optimization can be used, then the journal file will never
    ** be created for this transaction.
    */
    int useAtomicWrite = (
        !zMaster && 
        pPager->journalOff==jrnlBufferSize(pPager) && 
        nTrunc==0 && 
        (0==pPager->pDirty || 0==pPager->pDirty->pDirty)
    );
    if( useAtomicWrite ){
      /* Update the nRec field in the journal file. */
      int offset = pPager->journalHdr + sizeof(aJournalMagic);
      assert(pPager->nRec==1);
      rc = write32bits(pPager->jfd, offset, pPager->nRec);

      /* Update the db file change counter. The following call will modify
      ** the in-memory representation of page 1 to include the updated
      ** change counter and then write page 1 directly to the database
      ** file. Because of the atomic-write property of the host file-system, 
      ** this is safe.
      */
      rc = pager_incr_changecounter(pPager, 1);
    }else{
      rc = sqlite3JournalCreate(pPager->jfd);
      if( rc!=SQLITE_OK ) goto sync_exit;
    }

    if( !useAtomicWrite )
#endif

    /* If a master journal file name has already been written to the
    ** journal file, then no sync is required. This happens when it is
    ** written, then the process fails to upgrade from a RESERVED to an
    ** EXCLUSIVE lock. The next time the process tries to commit the
    ** transaction the m-j name will have already been written.
    */
    if( !pPager->setMaster ){
      assert( pPager->journalOpen );
      rc = pager_incr_changecounter(pPager, 0);
      if( rc!=SQLITE_OK ) goto sync_exit;
#ifndef SQLITE_OMIT_AUTOVACUUM
      if( nTrunc!=0 ){
        /* If this transaction has made the database smaller, then all pages
        ** being discarded by the truncation must be written to the journal
        ** file.

Changes to src/sqliteInt.h.

7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
....
1863
1864
1865
1866
1867
1868
1869

1870
1871
1872
1873
1874
1875
1876
**    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.
**
*************************************************************************
** Internal interface definitions for SQLite.
**
** @(#) $Id: sqliteInt.h,v 1.598 2007/08/23 02:47:53 drh Exp $
*/
#ifndef _SQLITEINT_H_
#define _SQLITEINT_H_
#include "sqliteLimit.h"


#if defined(SQLITE_TCL) || defined(TCLSH)
................................................................................
int sqlite3Reprepare(Vdbe*);
void sqlite3ExprListCheckLength(Parse*, ExprList*, int, const char*);
CollSeq *sqlite3BinaryCompareCollSeq(Parse *, Expr *, Expr *);

#ifdef SQLITE_ENABLE_ATOMIC_WRITE
  int sqlite3JournalOpen(sqlite3_vfs *, const char *, sqlite3_file *, int, int);
  int sqlite3JournalSize(sqlite3_vfs *);

#else
  #define sqlite3JournalSize(pVfs) ((pVfs)->szOsFile)
#endif

#if SQLITE_MAX_EXPR_DEPTH>0
  void sqlite3ExprSetHeight(Expr *);
  int sqlite3SelectExprHeight(Select *);







|







 







>







7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
....
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
**    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.
**
*************************************************************************
** Internal interface definitions for SQLite.
**
** @(#) $Id: sqliteInt.h,v 1.599 2007/08/24 08:15:54 danielk1977 Exp $
*/
#ifndef _SQLITEINT_H_
#define _SQLITEINT_H_
#include "sqliteLimit.h"


#if defined(SQLITE_TCL) || defined(TCLSH)
................................................................................
int sqlite3Reprepare(Vdbe*);
void sqlite3ExprListCheckLength(Parse*, ExprList*, int, const char*);
CollSeq *sqlite3BinaryCompareCollSeq(Parse *, Expr *, Expr *);

#ifdef SQLITE_ENABLE_ATOMIC_WRITE
  int sqlite3JournalOpen(sqlite3_vfs *, const char *, sqlite3_file *, int, int);
  int sqlite3JournalSize(sqlite3_vfs *);
  int sqlite3JournalCreate(sqlite3_file *);
#else
  #define sqlite3JournalSize(pVfs) ((pVfs)->szOsFile)
#endif

#if SQLITE_MAX_EXPR_DEPTH>0
  void sqlite3ExprSetHeight(Expr *);
  int sqlite3SelectExprHeight(Select *);

Changes to src/test6.c.

10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
...
158
159
160
161
162
163
164
165
166
167
168
169
170
171




172
173
174
175
176
177
178
179








180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
...
199
200
201
202
203
204
205



206
207
208




















209
210
211
212
213
214
215
...
222
223
224
225
226
227
228
229


230
231
232
233
234
235
236
237
238
239


240
241
242
243
244
245
246
247
248
249
250
251
252


253
254
255
256
257
258
259
...
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
...
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
**
******************************************************************************
**
** This file contains code that modified the OS layer in order to simulate
** the effect on the database file of an OS crash or power failure.  This
** is used to test the ability of SQLite to recover from those situations.
*/
#if SQLITE_TEST          /* This file is used for the testing only */
#include "sqliteInt.h"
#include "tcl.h"

#ifndef SQLITE_OMIT_DISKIO  /* This file is a no-op if disk I/O is disabled */

/* #define TRACE_CRASHTEST */

................................................................................
/*
** Flush the write-list as if xSync() had been called on file handle
** pFile. If isCrash is true, simulate a crash.
*/
static int writeListSync(CrashFile *pFile, int isCrash){
  int rc = SQLITE_OK;
  int iDc = g.iDeviceCharacteristics;
  i64 iSize;

  WriteBuffer *pWrite;
  WriteBuffer **ppPtr;

  /* Set pFinal to point to the last element of the write-list that
  ** is associated with file handle pFile.




  */
  WriteBuffer *pFinal = 0;
  if( !isCrash ){
    for(pWrite=g.pWriteList; pWrite; pWrite=pWrite->pNext){
      if( pWrite->pFile==pFile ){
        pFinal = pWrite;
      }
    }








  }

#ifdef TRACE_CRASHTEST
  printf("Sync %s (is %s crash)\n", pFile->zName, (isCrash?"a":"not a"));
#endif

  sqlite3OsFileSize((sqlite3_file *)pFile, &iSize);

  ppPtr = &g.pWriteList;
  for(pWrite=*ppPtr; rc==SQLITE_OK && pWrite; pWrite=*ppPtr){
    sqlite3_file *pRealFile = pWrite->pFile->pRealFile;

    /* (eAction==1)      -> write block out normally,
    ** (eAction==2)      -> do nothing,
    ** (eAction==3)      -> trash sectors.
................................................................................
      if( (pWrite->pFile==pFile || iDc&SQLITE_IOCAP_SEQUENTIAL) ){
        eAction = 1;
      }
    }else{
      char random;
      sqlite3Randomness(1, &random);




      if( iDc&SQLITE_IOCAP_ATOMIC || pWrite->zBuf==0 ){
        random &= 0x01;
      }





















      if( (random&0x06)==0x06 ){
        eAction = 3;
      }else{
        eAction = ((random&0x01)?2:1);
      }
    }
................................................................................
          );
        }else{
          rc = sqlite3OsTruncate(pRealFile, pWrite->iOffset);
        }
        *ppPtr = pWrite->pNext;
#ifdef TRACE_CRASHTEST
        if( isCrash ){
          printf("Writing %d bytes @ %d\n", pWrite->nBuf, (int)pWrite->iOffset);


        }
#endif
        sqlite3_free(pWrite);
        break;
      }
      case 2: {               /* Do nothing */
        ppPtr = &pWrite->pNext;
#ifdef TRACE_CRASHTEST
        if( isCrash ){
          printf("Omiting %d bytes @ %d\n", pWrite->nBuf, (int)pWrite->iOffset);


        }
#endif
        break;
      }
      case 3: {               /* Trash sectors */
        u8 *zGarbage;
        int iFirst = (pWrite->iOffset/g.iSectorSize);
        int iLast = (pWrite->iOffset+pWrite->nBuf-1)/g.iSectorSize;

        assert(pWrite->zBuf);

#ifdef TRACE_CRASHTEST
        printf("Trashing %d sectors @ sector %d\n", 1+iLast-iFirst, iFirst);


#endif

        zGarbage = sqlite3_malloc(g.iSectorSize);
        if( zGarbage ){
          sqlite3_int64 i;
          for(i=iFirst; rc==SQLITE_OK && i<=iLast; i++){
            sqlite3Randomness(g.iSectorSize, zGarbage); 
................................................................................
** The caller will have allocated pVfs->szOsFile bytes of space
** at pFile. This file uses this space for the CrashFile structure
** and allocates space for the "real" file structure using 
** sqlite3_malloc(). The assumption here is (pVfs->szOsFile) is
** equal or greater than sizeof(CrashFile).
*/
static int cfOpen(
  void *pAppData,
  const char *zName,
  sqlite3_file *pFile,
  int flags,
  int *pOutFlags
){
  sqlite3_vfs *pVfs = (sqlite3_vfs *)pAppData;
  int rc;
  CrashFile *pWrapper = (CrashFile *)pFile;
  sqlite3_file *pReal = (sqlite3_file*)&pWrapper[1];

  memset(pWrapper, 0, sizeof(CrashFile));
  rc = sqlite3OsOpen(pVfs, zName, pReal, flags, pOutFlags);

................................................................................
  }
  if( rc!=SQLITE_OK && pWrapper->pMethod ){
    sqlite3OsClose(pFile);
  }
  return rc;
}

static int cfDelete(void *pAppData, const char *zPath, int dirSync){
  sqlite3_vfs *pVfs = (sqlite3_vfs *)pAppData;
  return pVfs->xDelete(pVfs->pAppData, zPath, dirSync);
}
static int cfAccess(void *pAppData, const char *zPath, int flags){
  sqlite3_vfs *pVfs = (sqlite3_vfs *)pAppData;
  return pVfs->xAccess(pVfs->pAppData, zPath, flags);
}
static int cfGetTempName(void *pAppData, char *zBufOut){
  sqlite3_vfs *pVfs = (sqlite3_vfs *)pAppData;
  return pVfs->xGetTempName(pVfs->pAppData, zBufOut);
}
static int cfFullPathname(void *pAppData, const char *zPath, char *zPathOut){
  sqlite3_vfs *pVfs = (sqlite3_vfs *)pAppData;
  return pVfs->xFullPathname(pVfs->pAppData, zPath, zPathOut);
}
static void *cfDlOpen(void *pAppData, const char *zPath){
  sqlite3_vfs *pVfs = (sqlite3_vfs *)pAppData;
  return pVfs->xDlOpen(pVfs->pAppData, zPath);
}
static int cfRandomness(void *pAppData, int nByte, char *zBufOut){
  sqlite3_vfs *pVfs = (sqlite3_vfs *)pAppData;
  return pVfs->xRandomness(pVfs->pAppData, nByte, zBufOut);
}
static int cfSleep(void *pAppData, int nMicro){
  sqlite3_vfs *pVfs = (sqlite3_vfs *)pAppData;
  return pVfs->xSleep(pVfs->pAppData, nMicro);
}
static int cfCurrentTime(void *pAppData, double *pTimeOut){
  sqlite3_vfs *pVfs = (sqlite3_vfs *)pAppData;
  return pVfs->xCurrentTime(pVfs->pAppData, pTimeOut);
}

static int processDevSymArgs(
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[],
  int *piDeviceChar,







|







 







<




|
|
>
>
>
>








>
>
>
>
>
>
>
>






<
<







 







>
>
>
|


>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







 







|
>
>









|
>
>












|
>
>







 







|





|







 







|
|
|

|
|
|

|
|
|

|
|
|

|
|
|

|
|
|

|
|
|

|
|
|







10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
...
158
159
160
161
162
163
164

165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196


197
198
199
200
201
202
203
...
208
209
210
211
212
213
214
215
216
217
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
244
245
246
247
...
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
...
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
...
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
**
******************************************************************************
**
** This file contains code that modified the OS layer in order to simulate
** the effect on the database file of an OS crash or power failure.  This
** is used to test the ability of SQLite to recover from those situations.
*/
#if SQLITE_TEST          /* This file is used for testing only */
#include "sqliteInt.h"
#include "tcl.h"

#ifndef SQLITE_OMIT_DISKIO  /* This file is a no-op if disk I/O is disabled */

/* #define TRACE_CRASHTEST */

................................................................................
/*
** Flush the write-list as if xSync() had been called on file handle
** pFile. If isCrash is true, simulate a crash.
*/
static int writeListSync(CrashFile *pFile, int isCrash){
  int rc = SQLITE_OK;
  int iDc = g.iDeviceCharacteristics;


  WriteBuffer *pWrite;
  WriteBuffer **ppPtr;

  /* If this is not a crash simulation, set pFinal to point to the 
  ** last element of the write-list that is associated with file handle
  ** pFile.
  **
  ** If this is a crash simulation, set pFinal to an arbitrarily selected
  ** element of the write-list.
  */
  WriteBuffer *pFinal = 0;
  if( !isCrash ){
    for(pWrite=g.pWriteList; pWrite; pWrite=pWrite->pNext){
      if( pWrite->pFile==pFile ){
        pFinal = pWrite;
      }
    }
  }else if( iDc&(SQLITE_IOCAP_SEQUENTIAL|SQLITE_IOCAP_SAFE_APPEND) ){
    int nWrite = 0;
    int iFinal;
    for(pWrite=g.pWriteList; pWrite; pWrite=pWrite->pNext) nWrite++;
    sqlite3Randomness(sizeof(int), &iFinal);
    iFinal = ((iFinal<0)?-1*iFinal:iFinal)%nWrite;
    for(pWrite=g.pWriteList; iFinal>0; pWrite=pWrite->pNext) iFinal--;
    pFinal = pWrite;
  }

#ifdef TRACE_CRASHTEST
  printf("Sync %s (is %s crash)\n", pFile->zName, (isCrash?"a":"not a"));
#endif



  ppPtr = &g.pWriteList;
  for(pWrite=*ppPtr; rc==SQLITE_OK && pWrite; pWrite=*ppPtr){
    sqlite3_file *pRealFile = pWrite->pFile->pRealFile;

    /* (eAction==1)      -> write block out normally,
    ** (eAction==2)      -> do nothing,
    ** (eAction==3)      -> trash sectors.
................................................................................
      if( (pWrite->pFile==pFile || iDc&SQLITE_IOCAP_SEQUENTIAL) ){
        eAction = 1;
      }
    }else{
      char random;
      sqlite3Randomness(1, &random);

      /* Do not select option 3 (sector trashing) if the IOCAP_ATOMIC flag 
      ** is set or this is an OsTruncate(), not an Oswrite().
      */
      if( (iDc&SQLITE_IOCAP_ATOMIC) || (pWrite->zBuf==0) ){
        random &= 0x01;
      }

      /* If IOCAP_SEQUENTIAL is set and this is not the final entry
      ** in the truncated write-list, always select option 1 (write
      ** out correctly).
      */
      if( (iDc&SQLITE_IOCAP_SEQUENTIAL && pWrite!=pFinal) ){
        random = 0;
      }

      /* If IOCAP_SAFE_APPEND is set and this OsWrite() operation is
      ** an append (first byte of the written region is 1 byte past the
      ** current EOF), always select option 1 (write out correctly).
      */
      if( iDc&SQLITE_IOCAP_SAFE_APPEND && pWrite->zBuf ){
        i64 iSize;
        sqlite3OsFileSize(pRealFile, &iSize);
        if( iSize==pWrite->iOffset ){
          random = 0;
        }
      }

      if( (random&0x06)==0x06 ){
        eAction = 3;
      }else{
        eAction = ((random&0x01)?2:1);
      }
    }
................................................................................
          );
        }else{
          rc = sqlite3OsTruncate(pRealFile, pWrite->iOffset);
        }
        *ppPtr = pWrite->pNext;
#ifdef TRACE_CRASHTEST
        if( isCrash ){
          printf("Writing %d bytes @ %d (%s)\n", 
            pWrite->nBuf, (int)pWrite->iOffset, pWrite->pFile->zName
          );
        }
#endif
        sqlite3_free(pWrite);
        break;
      }
      case 2: {               /* Do nothing */
        ppPtr = &pWrite->pNext;
#ifdef TRACE_CRASHTEST
        if( isCrash ){
          printf("Omiting %d bytes @ %d (%s)\n", 
            pWrite->nBuf, (int)pWrite->iOffset, pWrite->pFile->zName
          );
        }
#endif
        break;
      }
      case 3: {               /* Trash sectors */
        u8 *zGarbage;
        int iFirst = (pWrite->iOffset/g.iSectorSize);
        int iLast = (pWrite->iOffset+pWrite->nBuf-1)/g.iSectorSize;

        assert(pWrite->zBuf);

#ifdef TRACE_CRASHTEST
        printf("Trashing %d sectors @ sector %d (%s)\n", 
            1+iLast-iFirst, iFirst, pWrite->pFile->zName
        );
#endif

        zGarbage = sqlite3_malloc(g.iSectorSize);
        if( zGarbage ){
          sqlite3_int64 i;
          for(i=iFirst; rc==SQLITE_OK && i<=iLast; i++){
            sqlite3Randomness(g.iSectorSize, zGarbage); 
................................................................................
** The caller will have allocated pVfs->szOsFile bytes of space
** at pFile. This file uses this space for the CrashFile structure
** and allocates space for the "real" file structure using 
** sqlite3_malloc(). The assumption here is (pVfs->szOsFile) is
** equal or greater than sizeof(CrashFile).
*/
static int cfOpen(
  sqlite3_vfs *pCfVfs,
  const char *zName,
  sqlite3_file *pFile,
  int flags,
  int *pOutFlags
){
  sqlite3_vfs *pVfs = (sqlite3_vfs *)pCfVfs->pAppData;
  int rc;
  CrashFile *pWrapper = (CrashFile *)pFile;
  sqlite3_file *pReal = (sqlite3_file*)&pWrapper[1];

  memset(pWrapper, 0, sizeof(CrashFile));
  rc = sqlite3OsOpen(pVfs, zName, pReal, flags, pOutFlags);

................................................................................
  }
  if( rc!=SQLITE_OK && pWrapper->pMethod ){
    sqlite3OsClose(pFile);
  }
  return rc;
}

static int cfDelete(sqlite3_vfs *pCfVfs, const char *zPath, int dirSync){
  sqlite3_vfs *pVfs = (sqlite3_vfs *)pCfVfs->pAppData;
  return pVfs->xDelete(pVfs, zPath, dirSync);
}
static int cfAccess(sqlite3_vfs *pCfVfs, const char *zPath, int flags){
  sqlite3_vfs *pVfs = (sqlite3_vfs *)pCfVfs->pAppData;
  return pVfs->xAccess(pVfs, zPath, flags);
}
static int cfGetTempName(sqlite3_vfs *pCfVfs, char *zBufOut){
  sqlite3_vfs *pVfs = (sqlite3_vfs *)pCfVfs->pAppData;
  return pVfs->xGetTempName(pVfs, zBufOut);
}
static int cfFullPathname(sqlite3_vfs *pCfVfs, const char *zPath, char *zPathOut){
  sqlite3_vfs *pVfs = (sqlite3_vfs *)pCfVfs->pAppData;
  return pVfs->xFullPathname(pVfs, zPath, zPathOut);
}
static void *cfDlOpen(sqlite3_vfs *pCfVfs, const char *zPath){
  sqlite3_vfs *pVfs = (sqlite3_vfs *)pCfVfs->pAppData;
  return pVfs->xDlOpen(pVfs, zPath);
}
static int cfRandomness(sqlite3_vfs *pCfVfs, int nByte, char *zBufOut){
  sqlite3_vfs *pVfs = (sqlite3_vfs *)pCfVfs->pAppData;
  return pVfs->xRandomness(pVfs, nByte, zBufOut);
}
static int cfSleep(sqlite3_vfs *pCfVfs, int nMicro){
  sqlite3_vfs *pVfs = (sqlite3_vfs *)pCfVfs->pAppData;
  return pVfs->xSleep(pVfs, nMicro);
}
static int cfCurrentTime(sqlite3_vfs *pCfVfs, double *pTimeOut){
  sqlite3_vfs *pVfs = (sqlite3_vfs *)pCfVfs->pAppData;
  return pVfs->xCurrentTime(pVfs, pTimeOut);
}

static int processDevSymArgs(
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[],
  int *piDeviceChar,

Changes to test/crash3.test.

5
6
7
8
9
10
11




12
13
14
15
16
17
18
19
..
30
31
32
33
34
35
36


37
38
39
40
41
42
43
..
91
92
93
94
95
96
97
98




















































































99
100
#
#    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.
#
#***********************************************************************
#




# $Id: crash3.test,v 1.1 2007/08/23 11:07:10 danielk1977 Exp $

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

ifcapable !crashtest {
  finish_test
  return
................................................................................
      }
      set res
    } {{$res1} or {$res2}}
  }]
  uplevel $script
}



# Each iteration of the following loop sets up the database to contain
# the following schema and data:
#
#    CREATE TABLE abc(a, b, c);
#    INSERT INTO abc VALUES(1, 2, 3);
#
# Then execute the SQL statement, scheduling a crash for part-way through
................................................................................
    do_test2 crash3-1.$tn.3 {
      execsql { SELECT * FROM abc }
    } {1 2 3} $res2

    incr tn
  }
}





















































































finish_test








>
>
>
>
|







 







>
>







 








>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>


5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
..
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
..
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
#
#    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 contains tests that verify that SQLite can correctly rollback
# databases after crashes when using the special IO modes triggered 
# by device IOCAP flags.
#
# $Id: crash3.test,v 1.2 2007/08/24 08:15:54 danielk1977 Exp $

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

ifcapable !crashtest {
  finish_test
  return
................................................................................
      }
      set res
    } {{$res1} or {$res2}}
  }]
  uplevel $script
}

# This block tests crash-recovery when the IOCAP_ATOMIC flags is set.
#
# Each iteration of the following loop sets up the database to contain
# the following schema and data:
#
#    CREATE TABLE abc(a, b, c);
#    INSERT INTO abc VALUES(1, 2, 3);
#
# Then execute the SQL statement, scheduling a crash for part-way through
................................................................................
    do_test2 crash3-1.$tn.3 {
      execsql { SELECT * FROM abc }
    } {1 2 3} $res2

    incr tn
  }
}

# This block tests both the IOCAP_SEQUENTIAL and IOCAP_SAFE_APPEND flags.
#
db close
file delete -force test.db test.db-journal
sqlite3 db test.db
do_test crash3-2.0 {
  execsql {
    BEGIN;
    CREATE TABLE abc(a PRIMARY KEY, b, c);
    CREATE TABLE def(d PRIMARY KEY, e, f);
    PRAGMA default_cache_size = 10;
    INSERT INTO abc VALUES(randstr(10,1000),randstr(10,1000),randstr(10,1000));
    INSERT INTO abc 
      SELECT randstr(10,1000),randstr(10,1000),randstr(10,1000) FROM abc;
    INSERT INTO abc 
      SELECT randstr(10,1000),randstr(10,1000),randstr(10,1000) FROM abc;
    INSERT INTO abc 
      SELECT randstr(10,1000),randstr(10,1000),randstr(10,1000) FROM abc;
    INSERT INTO abc 
      SELECT randstr(10,1000),randstr(10,1000),randstr(10,1000) FROM abc;
    INSERT INTO abc 
      SELECT randstr(10,1000),randstr(10,1000),randstr(10,1000) FROM abc;
    INSERT INTO abc 
      SELECT randstr(10,1000),randstr(10,1000),randstr(10,1000) FROM abc;
    COMMIT;
  }
} {}

set tn 1
foreach {::crashfile ::delay ::char} {
  test.db         1 sequential
  test.db         1 safe_append
  test.db-journal 1 sequential
  test.db-journal 1 safe_append
  test.db-journal 2 safe_append
  test.db-journal 2 sequential
  test.db-journal 3 sequential
  test.db-journal 3 safe_append
} {
  for {set ii 0} {$ii < 100} {incr ii} {
    set ::SQL [subst {
      SELECT randstr($ii,$ii+10);
      BEGIN;
      DELETE FROM abc WHERE random()%5;
      INSERT INTO abc 
        SELECT randstr(10,1000),randstr(10,1000),randstr(10,1000) 
        FROM abc
        WHERE (random()%5)==0;
      DELETE FROM def WHERE random()%5;
      INSERT INTO def 
        SELECT randstr(10,1000),randstr(10,1000),randstr(10,1000) 
        FROM def
        WHERE (random()%5)==0;
      COMMIT;
    }]

    do_test crash3-2.$tn.$ii {
      crashsql -file $::crashfile -delay $::delay -char $::char $::SQL
      db close
      sqlite3 db test.db
      execsql {PRAGMA integrity_check}
    } {ok}
  }
  incr tn
}

# The following block tests an interaction between IOCAP_ATOMIC and
# IOCAP_SEQUENTIAL. At one point, if both flags were set, small
# journal files that contained only a single page, but were required 
# for some other reason (i.e. nTrunk) were not being written to
# disk.
#
for {set ii 0} {$ii < 10} {incr ii} {
  db close
  file delete -force test.db test.db-journal
  crashsql -file test.db -char {sequential atomic} {
    CREATE TABLE abc(a, b, c);
  }
  sqlite3 db test.db
  do_test crash3-3.$ii {
    execsql {PRAGMA integrity_check}
  } {ok}
}

finish_test