/ 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 Side-by-Side Diffs Ignore Whitespace Patch

Changes to src/test_vfs.c.

    26     26   */
    27     27   struct TestvfsFile {
    28     28     sqlite3_file base;              /* Base class.  Must be first */
    29     29     sqlite3_vfs *pVfs;              /* The VFS */
    30     30     const char *zFilename;          /* Filename as passed to xOpen() */
    31     31     sqlite3_file *pReal;            /* The real, underlying file descriptor */
    32     32     Tcl_Obj *pShmId;                /* Shared memory id for Tcl callbacks */
           33  +
    33     34     TestvfsBuffer *pShm;            /* Shared memory buffer */
           35  +  u32 excllock;                   /* Mask of exclusive locks */
           36  +  u32 sharedlock;                 /* Mask of shared locks */
           37  +  TestvfsFile *pNext;             /* Next handle opened on the same file */
    34     38   };
    35     39   
    36     40   
    37     41   /*
    38     42   ** An instance of this structure is allocated for each VFS created. The
    39     43   ** sqlite3_vfs.pAppData field of the VFS structure registered with SQLite
    40     44   ** is set to point to it.
................................................................................
    73     77   #define TESTVFS_SHMCLOSE_MASK   0x00000040
    74     78   
    75     79   #define TESTVFS_OPEN_MASK       0x00000080
    76     80   #define TESTVFS_SYNC_MASK       0x00000100
    77     81   #define TESTVFS_ALL_MASK        0x000001FF
    78     82   
    79     83   /*
    80         -** A shared-memory buffer.
           84  +** A shared-memory buffer. There is one of these objects for each shared
           85  +** memory region opened by clients. If two clients open the same file,
           86  +** there are two TestvfsFile structures but only one TestvfsBuffer structure.
    81     87   */
    82     88   struct TestvfsBuffer {
    83     89     char *zFile;                    /* Associated file name */
    84     90     int n;                          /* Size of allocated buffer in bytes */
    85     91     u8 *a;                          /* Buffer allocated using ckalloc() */
    86         -  int nRef;                       /* Number of references to this object */
           92  +  TestvfsFile *pFile;             /* List of open handles */
    87     93     TestvfsBuffer *pNext;           /* Next in linked list of all buffers */
    88     94   };
    89     95   
    90     96   
    91     97   #define PARENTVFS(x) (((Testvfs *)((x)->pAppData))->pParent)
    92     98   
    93     99   #define TESTVFS_MAX_ARGS 12
................................................................................
   569    575     Testvfs *p;
   570    576     int rc = SQLITE_OK;             /* Return code */
   571    577     TestvfsBuffer *pBuffer;         /* Buffer to open connection to */
   572    578     TestvfsFile *pFd;               /* The testvfs file structure */
   573    579   
   574    580     pFd = (TestvfsFile*)pFileDes;
   575    581     p = (Testvfs *)pFd->pVfs->pAppData;
   576         -  assert( pFd->pShmId && pFd->pShm==0 );
          582  +  assert( pFd->pShmId && pFd->pShm==0 && pFd->pNext==0 );
   577    583   
   578    584     /* Evaluate the Tcl script: 
   579    585     **
   580    586     **   SCRIPT xShmOpen FILENAME
   581    587     */
   582    588     Tcl_ResetResult(p->interp);
   583    589     if( p->pScript && p->mask&TESTVFS_SHMOPEN_MASK ){
................................................................................
   603    609       pBuffer->zFile = (char *)&pBuffer[1];
   604    610       strcpy(pBuffer->zFile, pFd->zFilename);
   605    611       pBuffer->pNext = p->pBuffer;
   606    612       p->pBuffer = pBuffer;
   607    613     }
   608    614   
   609    615     /* Connect the TestvfsBuffer to the new TestvfsShm handle and return. */
   610         -  pBuffer->nRef++;
          616  +  pFd->pNext = pBuffer->pFile;
          617  +  pBuffer->pFile = pFd;
   611    618     pFd->pShm = pBuffer;
   612    619     return SQLITE_OK;
   613    620   }
   614    621   
   615    622   static int tvfsShmSize(
   616    623     sqlite3_file *pFile,
   617    624     int reqSize,
................................................................................
   709    716       );
   710    717       tvfsResultCode(p, &rc);
   711    718     }
   712    719   
   713    720     if( rc==SQLITE_OK && p->mask&TESTVFS_SHMLOCK_MASK && tvfsInjectIoerr(p) ){
   714    721       rc = SQLITE_IOERR;
   715    722     }
          723  +
          724  +  if( rc==SQLITE_OK ){
          725  +    int isLock = (flags & SQLITE_SHM_LOCK);
          726  +    int isExcl = (flags & SQLITE_SHM_EXCLUSIVE);
          727  +    u32 mask = (((1<<n)-1) << ofst);
          728  +    if( isLock ){
          729  +      TestvfsFile *p2;
          730  +      for(p2=pFd->pShm->pFile; p2; p2=p2->pNext){
          731  +        if( p2==pFd ) continue;
          732  +        if( (p2->excllock&mask) || (isExcl && p2->sharedlock&mask) ){
          733  +          rc = SQLITE_BUSY;
          734  +          break;
          735  +        }
          736  +      }
          737  +      if( rc==SQLITE_OK ){
          738  +        if( isExcl )  pFd->excllock |= mask;
          739  +        if( !isExcl ) pFd->sharedlock |= mask;
          740  +      }
          741  +    }else{
          742  +      if( isExcl )  pFd->excllock &= (~mask);
          743  +      if( !isExcl ) pFd->sharedlock &= (~mask);
          744  +    }
          745  +  }
          746  +
   716    747     return rc;
   717    748   }
   718    749   
   719    750   static void tvfsShmBarrier(sqlite3_file *pFile){
   720    751     TestvfsFile *pFd = (TestvfsFile *)pFile;
   721    752     Testvfs *p = (Testvfs *)(pFd->pVfs->pAppData);
   722    753   
................................................................................
   731    762     sqlite3_file *pFile,
   732    763     int deleteFlag
   733    764   ){
   734    765     int rc = SQLITE_OK;
   735    766     TestvfsFile *pFd = (TestvfsFile *)pFile;
   736    767     Testvfs *p = (Testvfs *)(pFd->pVfs->pAppData);
   737    768     TestvfsBuffer *pBuffer = pFd->pShm;
          769  +  TestvfsFile **ppFd;
   738    770   
   739    771     assert( pFd->pShmId && pFd->pShm );
   740         -#if 0
   741         -  assert( (deleteFlag!=0)==(pBuffer->nRef==1) );
   742         -#endif
   743    772   
   744    773     if( p->pScript && p->mask&TESTVFS_SHMCLOSE_MASK ){
   745    774       tvfsExecTcl(p, "xShmClose", 
   746    775           Tcl_NewStringObj(pFd->pShm->zFile, -1), pFd->pShmId, 0
   747    776       );
   748    777       tvfsResultCode(p, &rc);
   749    778     }
   750    779   
   751         -  pBuffer->nRef--;
   752         -  if( pBuffer->nRef==0 ){
          780  +  for(ppFd=&pBuffer->pFile; *ppFd!=pFd; ppFd=&((*ppFd)->pNext));
          781  +  assert( (*ppFd)==pFd );
          782  +  *ppFd = pFd->pNext;
          783  +
          784  +  if( pBuffer->pFile==0 ){
   753    785       TestvfsBuffer **pp;
   754    786       for(pp=&p->pBuffer; *pp!=pBuffer; pp=&((*pp)->pNext));
   755    787       *pp = (*pp)->pNext;
   756    788       ckfree((char *)pBuffer->a);
   757    789       ckfree((char *)pBuffer);
   758    790     }
   759    791     pFd->pShm = 0;

Changes to test/wal3.test.

   312    312   }
   313    313   do_test wal3-4.4 {
   314    314     db close
   315    315     set ::locks [list]
   316    316     sqlite3 db test.db -vfs T
   317    317     catchsql { SELECT * FROM x }
   318    318   } {1 {locking protocol}}
          319  +db close
          320  +T delete
          321  +
          322  +
          323  +#-------------------------------------------------------------------------
          324  +# Only one client may run recovery at a time. Test this mechanism.
          325  +#
          326  +# When client-2 tries to open a read transaction while client-1 is 
          327  +# running recovery, it fails to obtain a lock on an aReadMark[] slot
          328  +# (because they are all locked by recovery). It then tries to obtain
          329  +# a shared lock on the RECOVER lock to see if there really is a
          330  +# recovery running or not.
          331  +#
          332  +# This block of tests checks the effect of an SQLITE_BUSY or SQLITE_IOERR
          333  +# being returned when client-2 attempts a shared lock on the RECOVER byte.
          334  +#
          335  +# An SQLITE_BUSY should be converted to an SQLITE_BUSY_RECOVERY. An
          336  +# SQLITE_IOERR should be returned to the caller.
          337  +#
          338  +do_test wal3-5.1 {
          339  +  faultsim_delete_and_reopen
          340  +  execsql {
          341  +    PRAGMA journal_mode = WAL;
          342  +    CREATE TABLE t1(a, b);
          343  +    INSERT INTO t1 VALUES(1, 2);
          344  +    INSERT INTO t1 VALUES(3, 4);
          345  +  }
          346  +  faultsim_save_and_close
          347  +} {}
          348  +
          349  +testvfs T -default 1
          350  +T script method_callback
          351  +
          352  +proc method_callback {method args} {
          353  +  if {$method == "xShmBarrier"} {
          354  +    incr ::barrier_count
          355  +    if {$::barrier_count == 1} {
          356  +      # This code is executed within the xShmBarrier() callback invoked
          357  +      # by the client running recovery as part of writing the recovered
          358  +      # wal-index header. If a second client attempts to access the 
          359  +      # database now, it reads a corrupt (partially written) wal-index
          360  +      # header. But it cannot even get that far, as the first client
          361  +      # is still holding all the locks (recovery takes an exclusive lock
          362  +      # on *all* db locks, preventing access by any other client).
          363  +      #
          364  +      # If global variable ::wal3_do_lockfailure is non-zero, then set
          365  +      # things up so that an IO error occurs within an xShmLock() callback
          366  +      # made by the second client (aka [db2]).
          367  +      #
          368  +      sqlite3 db2 test.db
          369  +      if { $::wal3_do_lockfailure } { T filter xShmLock }
          370  +      set ::testrc [ catch { db2 eval "SELECT * FROM t1" } ::testmsg ]
          371  +      T filter {}
          372  +      db2 close
          373  +    }
          374  +  }
          375  +
          376  +  if {$method == "xShmLock"} {
          377  +    foreach {file handle spec} $args break
          378  +    if { $spec == "2 1 lock shared" } {
          379  +      return SQLITE_IOERR
          380  +    }
          381  +  }
          382  +
          383  +  return SQLITE_OK
          384  +}
          385  +
          386  +# Test a normal SQLITE_BUSY return.
          387  +#
          388  +T filter xShmBarrier
          389  +set testrc ""
          390  +set testmsg ""
          391  +set barrier_count 0
          392  +set wal3_do_lockfailure 0
          393  +do_test wal3-5.2 {
          394  +  faultsim_restore_and_reopen
          395  +  execsql { SELECT * FROM t1 }
          396  +} {1 2 3 4}
          397  +do_test wal3-5.3 {
          398  +  list $::testrc $::testmsg
          399  +} {1 {database is locked}}
          400  +db close
          401  +
          402  +# Test an SQLITE_IOERR return.
          403  +#
          404  +T filter xShmBarrier
          405  +set barrier_count 0
          406  +set wal3_do_lockfailure 1
          407  +set testrc ""
          408  +set testmsg ""
          409  +do_test wal3-5.4 {
          410  +  faultsim_restore_and_reopen
          411  +  execsql { SELECT * FROM t1 }
          412  +} {1 2 3 4}
          413  +do_test wal3-5.5 {
          414  +  list $::testrc $::testmsg
          415  +} {1 {disk I/O error}}
   319    416   
   320    417   db close
   321    418   T delete
   322    419   
   323         -finish_test
          420  +#-------------------------------------------------------------------------
          421  +# When opening a read-transaction on a database 
          422  +#
   324    423   
          424  +finish_test
   325    425   

Changes to test/walfault.test.

   317    317     faultsim_integrity_check
   318    318   
   319    319     catch { db eval { ROLLBACK TO spoint } }
   320    320     catch { db eval { COMMIT } }
   321    321     set n [db one {SELECT count(*) FROM abc}]
   322    322     if {$n != 1 && $n != 2} { error "Incorrect number of rows: $n" }
   323    323   }
          324  +
          325  +do_test walfault-10-pre1 {
          326  +  faultsim_delete_and_reopen
          327  +  execsql {
          328  +    PRAGMA journal_mode = WAL;
          329  +    PRAGMA wal_checkpoint = 0;
          330  +    CREATE TABLE z(zz INTEGER PRIMARY KEY, zzz BLOB);
          331  +    CREATE INDEX zzzz ON z(zzz);
          332  +    INSERT INTO z VALUES(NULL, randomblob(800));
          333  +    INSERT INTO z VALUES(NULL, randomblob(800));
          334  +    INSERT INTO z SELECT NULL, randomblob(800) FROM z;
          335  +    INSERT INTO z SELECT NULL, randomblob(800) FROM z;
          336  +    INSERT INTO z SELECT NULL, randomblob(800) FROM z;
          337  +    INSERT INTO z SELECT NULL, randomblob(800) FROM z;
          338  +    INSERT INTO z SELECT NULL, randomblob(800) FROM z;
          339  +  }
          340  +  faultsim_save_and_close
          341  +} {}
          342  +do_faultsim_test walfault-10 -prep {
          343  +  faultsim_restore_and_reopen
          344  +  execsql {
          345  +    PRAGMA cache_size = 10;
          346  +    BEGIN;
          347  +      UPDATE z SET zzz = randomblob(799);
          348  +  }
          349  +
          350  +  set ::stmt [sqlite3_prepare db "SELECT zzz FROM z WHERE zz IN (1, 2, 3)" -1]
          351  +  sqlite3_step $::stmt
          352  +} -body {
          353  +  execsql { INSERT INTO z VALUES(NULL, NULL) }
          354  +} -test {
          355  +  sqlite3_finalize $::stmt
          356  +  faultsim_integrity_check
          357  +
          358  +  faultsim_test_result {0 {}}
          359  +  catch { db eval { ROLLBACK } }
          360  +  faultsim_integrity_check
          361  +
          362  +  set n [db eval {SELECT count(*), sum(length(zzz)) FROM z}]
          363  +  if {$n != "64 51200"} { error "Incorrect data: $n" }
          364  +}
   324    365   
   325    366   finish_test
   326    367