/ Check-in [f9d4ae0e]
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 extra coverage test cases for wal.c. No changes to production code.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: f9d4ae0e8cc5d32c52eb78799f7959dd236ea9de
User & Date: dan 2010-06-05 11:53:34
Context
2010-06-05
14:42
Mark a condition in wal.c as ALWAYS(). check-in: 3fe0cc78 user: dan tags: trunk
11:53
Add extra coverage test cases for wal.c. No changes to production code. check-in: f9d4ae0e user: dan tags: trunk
2010-06-04
18:38
Clarify an assert in sqlite3WalExclusiveMode(). check-in: 25585069 user: dan tags: trunk
Changes
Hide Diffs Unified Diffs Show Whitespace Changes Patch

Changes to src/test_vfs.c.

26
27
28
29
30
31
32

33



34
35
36
37
38
39
40
..
73
74
75
76
77
78
79
80


81
82
83
84
85
86
87
88
89
90
91
92
93
...
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
...
603
604
605
606
607
608
609

610
611
612
613
614
615
616
617
...
709
710
711
712
713
714
715
























716
717
718
719
720
721
722
...
731
732
733
734
735
736
737

738
739
740
741
742
743
744
745
746
747
748
749
750
751



752
753
754
755
756
757
758
759
*/
struct TestvfsFile {
  sqlite3_file base;              /* Base class.  Must be first */
  sqlite3_vfs *pVfs;              /* The VFS */
  const char *zFilename;          /* Filename as passed to xOpen() */
  sqlite3_file *pReal;            /* The real, underlying file descriptor */
  Tcl_Obj *pShmId;                /* Shared memory id for Tcl callbacks */

  TestvfsBuffer *pShm;            /* Shared memory buffer */



};


/*
** An instance of this structure is allocated for each VFS created. The
** sqlite3_vfs.pAppData field of the VFS structure registered with SQLite
** is set to point to it.
................................................................................
#define TESTVFS_SHMCLOSE_MASK   0x00000040

#define TESTVFS_OPEN_MASK       0x00000080
#define TESTVFS_SYNC_MASK       0x00000100
#define TESTVFS_ALL_MASK        0x000001FF

/*
** A shared-memory buffer.


*/
struct TestvfsBuffer {
  char *zFile;                    /* Associated file name */
  int n;                          /* Size of allocated buffer in bytes */
  u8 *a;                          /* Buffer allocated using ckalloc() */
  int nRef;                       /* Number of references to this object */
  TestvfsBuffer *pNext;           /* Next in linked list of all buffers */
};


#define PARENTVFS(x) (((Testvfs *)((x)->pAppData))->pParent)

#define TESTVFS_MAX_ARGS 12
................................................................................
  Testvfs *p;
  int rc = SQLITE_OK;             /* Return code */
  TestvfsBuffer *pBuffer;         /* Buffer to open connection to */
  TestvfsFile *pFd;               /* The testvfs file structure */

  pFd = (TestvfsFile*)pFileDes;
  p = (Testvfs *)pFd->pVfs->pAppData;
  assert( pFd->pShmId && pFd->pShm==0 );

  /* Evaluate the Tcl script: 
  **
  **   SCRIPT xShmOpen FILENAME
  */
  Tcl_ResetResult(p->interp);
  if( p->pScript && p->mask&TESTVFS_SHMOPEN_MASK ){
................................................................................
    pBuffer->zFile = (char *)&pBuffer[1];
    strcpy(pBuffer->zFile, pFd->zFilename);
    pBuffer->pNext = p->pBuffer;
    p->pBuffer = pBuffer;
  }

  /* Connect the TestvfsBuffer to the new TestvfsShm handle and return. */

  pBuffer->nRef++;
  pFd->pShm = pBuffer;
  return SQLITE_OK;
}

static int tvfsShmSize(
  sqlite3_file *pFile,
  int reqSize,
................................................................................
    );
    tvfsResultCode(p, &rc);
  }

  if( rc==SQLITE_OK && p->mask&TESTVFS_SHMLOCK_MASK && tvfsInjectIoerr(p) ){
    rc = SQLITE_IOERR;
  }
























  return rc;
}

static void tvfsShmBarrier(sqlite3_file *pFile){
  TestvfsFile *pFd = (TestvfsFile *)pFile;
  Testvfs *p = (Testvfs *)(pFd->pVfs->pAppData);

................................................................................
  sqlite3_file *pFile,
  int deleteFlag
){
  int rc = SQLITE_OK;
  TestvfsFile *pFd = (TestvfsFile *)pFile;
  Testvfs *p = (Testvfs *)(pFd->pVfs->pAppData);
  TestvfsBuffer *pBuffer = pFd->pShm;


  assert( pFd->pShmId && pFd->pShm );
#if 0
  assert( (deleteFlag!=0)==(pBuffer->nRef==1) );
#endif

  if( p->pScript && p->mask&TESTVFS_SHMCLOSE_MASK ){
    tvfsExecTcl(p, "xShmClose", 
        Tcl_NewStringObj(pFd->pShm->zFile, -1), pFd->pShmId, 0
    );
    tvfsResultCode(p, &rc);
  }

  pBuffer->nRef--;



  if( pBuffer->nRef==0 ){
    TestvfsBuffer **pp;
    for(pp=&p->pBuffer; *pp!=pBuffer; pp=&((*pp)->pNext));
    *pp = (*pp)->pNext;
    ckfree((char *)pBuffer->a);
    ckfree((char *)pBuffer);
  }
  pFd->pShm = 0;







>

>
>
>







 







|
>
>





|







 







|







 







>
|







 







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







 







>


<
<
<








|
>
>
>
|







26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
..
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
...
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
...
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
...
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
...
762
763
764
765
766
767
768
769
770
771



772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
*/
struct TestvfsFile {
  sqlite3_file base;              /* Base class.  Must be first */
  sqlite3_vfs *pVfs;              /* The VFS */
  const char *zFilename;          /* Filename as passed to xOpen() */
  sqlite3_file *pReal;            /* The real, underlying file descriptor */
  Tcl_Obj *pShmId;                /* Shared memory id for Tcl callbacks */

  TestvfsBuffer *pShm;            /* Shared memory buffer */
  u32 excllock;                   /* Mask of exclusive locks */
  u32 sharedlock;                 /* Mask of shared locks */
  TestvfsFile *pNext;             /* Next handle opened on the same file */
};


/*
** An instance of this structure is allocated for each VFS created. The
** sqlite3_vfs.pAppData field of the VFS structure registered with SQLite
** is set to point to it.
................................................................................
#define TESTVFS_SHMCLOSE_MASK   0x00000040

#define TESTVFS_OPEN_MASK       0x00000080
#define TESTVFS_SYNC_MASK       0x00000100
#define TESTVFS_ALL_MASK        0x000001FF

/*
** A shared-memory buffer. There is one of these objects for each shared
** memory region opened by clients. If two clients open the same file,
** there are two TestvfsFile structures but only one TestvfsBuffer structure.
*/
struct TestvfsBuffer {
  char *zFile;                    /* Associated file name */
  int n;                          /* Size of allocated buffer in bytes */
  u8 *a;                          /* Buffer allocated using ckalloc() */
  TestvfsFile *pFile;             /* List of open handles */
  TestvfsBuffer *pNext;           /* Next in linked list of all buffers */
};


#define PARENTVFS(x) (((Testvfs *)((x)->pAppData))->pParent)

#define TESTVFS_MAX_ARGS 12
................................................................................
  Testvfs *p;
  int rc = SQLITE_OK;             /* Return code */
  TestvfsBuffer *pBuffer;         /* Buffer to open connection to */
  TestvfsFile *pFd;               /* The testvfs file structure */

  pFd = (TestvfsFile*)pFileDes;
  p = (Testvfs *)pFd->pVfs->pAppData;
  assert( pFd->pShmId && pFd->pShm==0 && pFd->pNext==0 );

  /* Evaluate the Tcl script: 
  **
  **   SCRIPT xShmOpen FILENAME
  */
  Tcl_ResetResult(p->interp);
  if( p->pScript && p->mask&TESTVFS_SHMOPEN_MASK ){
................................................................................
    pBuffer->zFile = (char *)&pBuffer[1];
    strcpy(pBuffer->zFile, pFd->zFilename);
    pBuffer->pNext = p->pBuffer;
    p->pBuffer = pBuffer;
  }

  /* Connect the TestvfsBuffer to the new TestvfsShm handle and return. */
  pFd->pNext = pBuffer->pFile;
  pBuffer->pFile = pFd;
  pFd->pShm = pBuffer;
  return SQLITE_OK;
}

static int tvfsShmSize(
  sqlite3_file *pFile,
  int reqSize,
................................................................................
    );
    tvfsResultCode(p, &rc);
  }

  if( rc==SQLITE_OK && p->mask&TESTVFS_SHMLOCK_MASK && tvfsInjectIoerr(p) ){
    rc = SQLITE_IOERR;
  }

  if( rc==SQLITE_OK ){
    int isLock = (flags & SQLITE_SHM_LOCK);
    int isExcl = (flags & SQLITE_SHM_EXCLUSIVE);
    u32 mask = (((1<<n)-1) << ofst);
    if( isLock ){
      TestvfsFile *p2;
      for(p2=pFd->pShm->pFile; p2; p2=p2->pNext){
        if( p2==pFd ) continue;
        if( (p2->excllock&mask) || (isExcl && p2->sharedlock&mask) ){
          rc = SQLITE_BUSY;
          break;
        }
      }
      if( rc==SQLITE_OK ){
        if( isExcl )  pFd->excllock |= mask;
        if( !isExcl ) pFd->sharedlock |= mask;
      }
    }else{
      if( isExcl )  pFd->excllock &= (~mask);
      if( !isExcl ) pFd->sharedlock &= (~mask);
    }
  }

  return rc;
}

static void tvfsShmBarrier(sqlite3_file *pFile){
  TestvfsFile *pFd = (TestvfsFile *)pFile;
  Testvfs *p = (Testvfs *)(pFd->pVfs->pAppData);

................................................................................
  sqlite3_file *pFile,
  int deleteFlag
){
  int rc = SQLITE_OK;
  TestvfsFile *pFd = (TestvfsFile *)pFile;
  Testvfs *p = (Testvfs *)(pFd->pVfs->pAppData);
  TestvfsBuffer *pBuffer = pFd->pShm;
  TestvfsFile **ppFd;

  assert( pFd->pShmId && pFd->pShm );




  if( p->pScript && p->mask&TESTVFS_SHMCLOSE_MASK ){
    tvfsExecTcl(p, "xShmClose", 
        Tcl_NewStringObj(pFd->pShm->zFile, -1), pFd->pShmId, 0
    );
    tvfsResultCode(p, &rc);
  }

  for(ppFd=&pBuffer->pFile; *ppFd!=pFd; ppFd=&((*ppFd)->pNext));
  assert( (*ppFd)==pFd );
  *ppFd = pFd->pNext;

  if( pBuffer->pFile==0 ){
    TestvfsBuffer **pp;
    for(pp=&p->pBuffer; *pp!=pBuffer; pp=&((*pp)->pNext));
    *pp = (*pp)->pNext;
    ckfree((char *)pBuffer->a);
    ckfree((char *)pBuffer);
  }
  pFd->pShm = 0;

Changes to test/wal3.test.

312
313
314
315
316
317
318


319














































































320

















321
322
323


324
325


}
do_test wal3-4.4 {
  db close
  set ::locks [list]
  sqlite3 db test.db -vfs T
  catchsql { SELECT * FROM x }
} {1 {locking protocol}}

















































































db close

















T delete

finish_test













>
>

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

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


<
>
>
|

>
>
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419

420
421
422
423
424
425
}
do_test wal3-4.4 {
  db close
  set ::locks [list]
  sqlite3 db test.db -vfs T
  catchsql { SELECT * FROM x }
} {1 {locking protocol}}
db close
T delete


#-------------------------------------------------------------------------
# Only one client may run recovery at a time. Test this mechanism.
#
# When client-2 tries to open a read transaction while client-1 is 
# running recovery, it fails to obtain a lock on an aReadMark[] slot
# (because they are all locked by recovery). It then tries to obtain
# a shared lock on the RECOVER lock to see if there really is a
# recovery running or not.
#
# This block of tests checks the effect of an SQLITE_BUSY or SQLITE_IOERR
# being returned when client-2 attempts a shared lock on the RECOVER byte.
#
# An SQLITE_BUSY should be converted to an SQLITE_BUSY_RECOVERY. An
# SQLITE_IOERR should be returned to the caller.
#
do_test wal3-5.1 {
  faultsim_delete_and_reopen
  execsql {
    PRAGMA journal_mode = WAL;
    CREATE TABLE t1(a, b);
    INSERT INTO t1 VALUES(1, 2);
    INSERT INTO t1 VALUES(3, 4);
  }
  faultsim_save_and_close
} {}

testvfs T -default 1
T script method_callback

proc method_callback {method args} {
  if {$method == "xShmBarrier"} {
    incr ::barrier_count
    if {$::barrier_count == 1} {
      # This code is executed within the xShmBarrier() callback invoked
      # by the client running recovery as part of writing the recovered
      # wal-index header. If a second client attempts to access the 
      # database now, it reads a corrupt (partially written) wal-index
      # header. But it cannot even get that far, as the first client
      # is still holding all the locks (recovery takes an exclusive lock
      # on *all* db locks, preventing access by any other client).
      #
      # If global variable ::wal3_do_lockfailure is non-zero, then set
      # things up so that an IO error occurs within an xShmLock() callback
      # made by the second client (aka [db2]).
      #
      sqlite3 db2 test.db
      if { $::wal3_do_lockfailure } { T filter xShmLock }
      set ::testrc [ catch { db2 eval "SELECT * FROM t1" } ::testmsg ]
      T filter {}
      db2 close
    }
  }

  if {$method == "xShmLock"} {
    foreach {file handle spec} $args break
    if { $spec == "2 1 lock shared" } {
      return SQLITE_IOERR
    }
  }

  return SQLITE_OK
}

# Test a normal SQLITE_BUSY return.
#
T filter xShmBarrier
set testrc ""
set testmsg ""
set barrier_count 0
set wal3_do_lockfailure 0
do_test wal3-5.2 {
  faultsim_restore_and_reopen
  execsql { SELECT * FROM t1 }
} {1 2 3 4}
do_test wal3-5.3 {
  list $::testrc $::testmsg
} {1 {database is locked}}
db close

# Test an SQLITE_IOERR return.
#
T filter xShmBarrier
set barrier_count 0
set wal3_do_lockfailure 1
set testrc ""
set testmsg ""
do_test wal3-5.4 {
  faultsim_restore_and_reopen
  execsql { SELECT * FROM t1 }
} {1 2 3 4}
do_test wal3-5.5 {
  list $::testrc $::testmsg
} {1 {disk I/O error}}

db close
T delete


#-------------------------------------------------------------------------
# When opening a read-transaction on a database 
#

finish_test

Changes to test/walfault.test.

317
318
319
320
321
322
323
324









































325
326
  faultsim_integrity_check

  catch { db eval { ROLLBACK TO spoint } }
  catch { db eval { COMMIT } }
  set n [db one {SELECT count(*) FROM abc}]
  if {$n != 1 && $n != 2} { error "Incorrect number of rows: $n" }
}










































finish_test









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


317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
  faultsim_integrity_check

  catch { db eval { ROLLBACK TO spoint } }
  catch { db eval { COMMIT } }
  set n [db one {SELECT count(*) FROM abc}]
  if {$n != 1 && $n != 2} { error "Incorrect number of rows: $n" }
}

do_test walfault-10-pre1 {
  faultsim_delete_and_reopen
  execsql {
    PRAGMA journal_mode = WAL;
    PRAGMA wal_checkpoint = 0;
    CREATE TABLE z(zz INTEGER PRIMARY KEY, zzz BLOB);
    CREATE INDEX zzzz ON z(zzz);
    INSERT INTO z VALUES(NULL, randomblob(800));
    INSERT INTO z VALUES(NULL, randomblob(800));
    INSERT INTO z SELECT NULL, randomblob(800) FROM z;
    INSERT INTO z SELECT NULL, randomblob(800) FROM z;
    INSERT INTO z SELECT NULL, randomblob(800) FROM z;
    INSERT INTO z SELECT NULL, randomblob(800) FROM z;
    INSERT INTO z SELECT NULL, randomblob(800) FROM z;
  }
  faultsim_save_and_close
} {}
do_faultsim_test walfault-10 -prep {
  faultsim_restore_and_reopen
  execsql {
    PRAGMA cache_size = 10;
    BEGIN;
      UPDATE z SET zzz = randomblob(799);
  }

  set ::stmt [sqlite3_prepare db "SELECT zzz FROM z WHERE zz IN (1, 2, 3)" -1]
  sqlite3_step $::stmt
} -body {
  execsql { INSERT INTO z VALUES(NULL, NULL) }
} -test {
  sqlite3_finalize $::stmt
  faultsim_integrity_check

  faultsim_test_result {0 {}}
  catch { db eval { ROLLBACK } }
  faultsim_integrity_check

  set n [db eval {SELECT count(*), sum(length(zzz)) FROM z}]
  if {$n != "64 51200"} { error "Incorrect data: $n" }
}

finish_test