/ Check-in [a64d96db]
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:Change things so that journal2.test works with ENABLE_ATOMIC_WRITE.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | experimental
Files: files | file ages | folders
SHA1: a64d96db09ef2b7651fa4e98d3c7bf3ae5d3fe96
User & Date: dan 2010-06-21 12:34:30
Context
2010-06-21
12:47
Merge the experimental UNDELETABLE_WHEN_OPEN optimization into the trunk. check-in: ee0acef1 user: drh tags: trunk
12:34
Change things so that journal2.test works with ENABLE_ATOMIC_WRITE. Closed-Leaf check-in: a64d96db user: dan tags: experimental
07:45
Add further pager tests. check-in: 4104b175 user: dan tags: experimental
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Changes to src/pager.c.

   332    332     u8 noReadlock;              /* Do not bother to obtain readlocks */
   333    333     u8 noSync;                  /* Do not sync the journal if true */
   334    334     u8 fullSync;                /* Do extra syncs of the journal for robustness */
   335    335     u8 sync_flags;              /* One of SYNC_NORMAL or SYNC_FULL */
   336    336     u8 tempFile;                /* zFilename is a temporary file */
   337    337     u8 readOnly;                /* True for a read-only database */
   338    338     u8 memDb;                   /* True to inhibit all file I/O */
   339         -  u8 safeJrnlHandle;          /* True if jrnl may be held open with no lock */
   340    339   
   341    340     /* The following block contains those class members that are dynamically
   342    341     ** modified during normal operations. The other variables in this structure
   343    342     ** are either constant throughout the lifetime of the pager, or else
   344    343     ** used to store configuration parameters that affect the way the pager 
   345    344     ** operates.
   346    345     **
................................................................................
  1216   1215   ** an open journal-file, then the next time a shared-lock is obtained
  1217   1216   ** on the pager file (by this or any other process), it will be
  1218   1217   ** treated as a hot-journal and rolled back.
  1219   1218   */
  1220   1219   static void pager_unlock(Pager *pPager){
  1221   1220     if( !pPager->exclusiveMode ){
  1222   1221       int rc = SQLITE_OK;          /* Return code */
         1222  +    int iDc = isOpen(pPager->fd)?sqlite3OsDeviceCharacteristics(pPager->fd):0;
  1223   1223   
  1224   1224       /* Always close the journal file when dropping the database lock.
  1225   1225       ** Otherwise, another connection with journal_mode=delete might
  1226   1226       ** delete the file out from under us.
  1227   1227       */
  1228         -    if( pPager->safeJrnlHandle==0 
  1229         -     || (pPager->journalMode!=PAGER_JOURNALMODE_TRUNCATE 
  1230         -      && pPager->journalMode!=PAGER_JOURNALMODE_PERSIST)
         1228  +    assert( (PAGER_JOURNALMODE_MEMORY   & 5)!=1 );
         1229  +    assert( (PAGER_JOURNALMODE_OFF      & 5)!=1 );
         1230  +    assert( (PAGER_JOURNALMODE_WAL      & 5)!=1 );
         1231  +    assert( (PAGER_JOURNALMODE_DELETE   & 5)!=1 );
         1232  +    assert( (PAGER_JOURNALMODE_TRUNCATE & 5)==1 );
         1233  +    assert( (PAGER_JOURNALMODE_PERSIST  & 5)==1 );
         1234  +    if( 0==(iDc & SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN)
         1235  +     || 1!=(pPager->journalMode & 5)
  1231   1236       ){
  1232   1237         sqlite3OsClose(pPager->jfd);
  1233   1238       }
  1234   1239   
  1235   1240       sqlite3BitvecDestroy(pPager->pInJournal);
  1236   1241       pPager->pInJournal = 0;
  1237   1242       releaseAllSavepoints(pPager);
................................................................................
  3092   3097   int sqlite3PagerClose(Pager *pPager){
  3093   3098     u8 *pTmp = (u8 *)pPager->pTmpSpace;
  3094   3099   
  3095   3100     disable_simulated_io_errors();
  3096   3101     sqlite3BeginBenignMalloc();
  3097   3102     pPager->errCode = 0;
  3098   3103     pPager->exclusiveMode = 0;
  3099         -  pPager->safeJrnlHandle = 0;
  3100   3104   #ifndef SQLITE_OMIT_WAL
  3101   3105     sqlite3WalClose(pPager->pWal,
  3102   3106       (pPager->noSync ? 0 : pPager->sync_flags), 
  3103   3107       pPager->pageSize, pTmp
  3104   3108     );
  3105   3109     pPager->pWal = 0;
  3106   3110   #endif
................................................................................
  3119   3123       }
  3120   3124       pagerUnlockAndRollback(pPager);
  3121   3125     }
  3122   3126     sqlite3EndBenignMalloc();
  3123   3127     enable_simulated_io_errors();
  3124   3128     PAGERTRACE(("CLOSE %d\n", PAGERID(pPager)));
  3125   3129     IOTRACE(("CLOSE %p\n", pPager))
         3130  +  sqlite3OsClose(pPager->jfd);
  3126   3131     sqlite3OsClose(pPager->fd);
  3127   3132     sqlite3PageFree(pTmp);
  3128   3133     sqlite3PcacheClose(pPager->pPCache);
  3129   3134   
  3130   3135   #ifdef SQLITE_HAS_CODEC
  3131   3136     if( pPager->xCodecFree ) pPager->xCodecFree(pPager->pCodec);
  3132   3137   #endif
................................................................................
  4503   4508           );
  4504   4509   #ifdef SQLITE_ENABLE_ATOMIC_WRITE
  4505   4510         rc = sqlite3JournalOpen(
  4506   4511             pVfs, pPager->zJournal, pPager->jfd, flags, jrnlBufferSize(pPager)
  4507   4512         );
  4508   4513   #else
  4509   4514         rc = sqlite3OsOpen(pVfs, pPager->zJournal, pPager->jfd, flags, 0);
  4510         -      if( rc==SQLITE_OK ){
  4511         -        int iDc = sqlite3OsDeviceCharacteristics(pPager->jfd);
  4512         -        pPager->safeJrnlHandle = (iDc&SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN)!=0;
  4513         -      }
  4514   4515   #endif
  4515   4516       }
  4516   4517       assert( rc!=SQLITE_OK || isOpen(pPager->jfd) );
  4517   4518     }
  4518   4519   
  4519   4520   
  4520   4521     /* Write the first journal header to the journal file and open 

Changes to test/malloc_common.tcl.

   132    132     }
   133    133   }
   134    134   proc faultsim_save_and_close {} {
   135    135     faultsim_save
   136    136     catch { db close }
   137    137     return ""
   138    138   }
   139         -proc faultsim_restore_and_reopen {} {
          139  +proc faultsim_restore_and_reopen {{dbfile test.db}} {
   140    140     catch { db close }
   141    141     foreach f [glob -nocomplain test.db*] { file delete -force $f }
   142    142     foreach f2 [glob -nocomplain sv_test.db*] {
   143    143       set f [string range $f2 3 end]
   144    144       file copy -force $f2 $f
   145    145     }
   146         -  sqlite3 db test.db
          146  +  sqlite3 db $dbfile
   147    147     sqlite3_extended_result_codes db 1
   148    148     sqlite3_db_config_lookaside db 0 0 0
   149    149   }
   150    150   
   151    151   proc faultsim_integrity_check {{db db}} {
   152    152     set ic [$db eval { PRAGMA integrity_check }]
   153    153     if {$ic != "ok"} { error "Integrity check: $ic" }
   154    154   }
   155    155   
   156    156   proc faultsim_delete_and_reopen {{file test.db}} {
   157    157     catch { db close }
   158    158     foreach f [glob -nocomplain test.db*] { file delete -force $f }
   159         -  sqlite3 db test.db
          159  +  sqlite3 db $file
   160    160   }
   161    161   
   162    162   
   163    163   # The following procs are used as [do_one_faultsim_test] callbacks when 
   164    164   # injecting OOM faults into test cases.
   165    165   #
   166    166   proc oom_injectstart {nRepeat iFail} {

Changes to test/pager1.test.

   223    223   #               compute) the associated journal is rolled back (and no
   224    224   #               xAccess() call to check for the presence of any master 
   225    225   #               journal file is made).
   226    226   #
   227    227   # pager1.4.3.*: Test that the contents of a hot-journal are ignored if the
   228    228   #               page-size or sector-size in the journal header appear to
   229    229   #               be invalid (too large, too small or not a power of 2).
          230  +#
          231  +# pager1.4.4.*: Test hot-journal rollback of journal file with a master
          232  +#               journal pointer generated in various "PRAGMA synchronous"
          233  +#               modes.
          234  +#
          235  +# pager1.4.5.*: Test that hot-journal rollback stops if it encounters a
          236  +#               journal-record for which the checksum fails.
   230    237   # 
   231    238   do_test pager1.4.1.1 {
   232    239     faultsim_delete_and_reopen
   233    240     execsql { 
   234    241       CREATE TABLE x(y, z);
   235    242       INSERT INTO x VALUES(1, 2);
   236    243     }
................................................................................
   349    356     do_test pager1.4.3.$tn {
   350    357       faultsim_restore_and_reopen
   351    358       hexio_write test.db-journal $ofst [format %.8x $value]
   352    359       execsql { SELECT * FROM t1 }
   353    360     } $result
   354    361   }
   355    362   db close
          363  +
          364  +# Set up a VFS that snapshots the file-system just before a master journal
          365  +# file is deleted to commit a multi-file transaction. Specifically, the
          366  +# file-system is saved just before the xDelete() call to remove the 
          367  +# master journal file from the file-system.
          368  +#
          369  +testvfs tv -default 1
          370  +tv script copy_on_mj_delete
          371  +set ::mj_filename_length 0
          372  +proc copy_on_mj_delete {method filename args} {
          373  +  if {[string match *mj* [file tail $filename]]} { 
          374  +    set ::mj_filename_length [string length $filename]
          375  +    faultsim_save 
          376  +  }
          377  +  return SQLITE_OK
          378  +}
          379  +
          380  +set pwd [pwd]
          381  +foreach {tn1 tcl} {
          382  +  1 { set prefix "test.db" }
          383  +  2 { 
          384  +    # This test depends on the underlying VFS being able to open paths
          385  +    # 512 bytes in length. The idea is to create a hot-journal file that
          386  +    # contains a master-journal pointer so large that it could contain
          387  +    # a valid page record (if the file page-size is 512 bytes). So as to
          388  +    # make sure SQLite doesn't get confused by this.
          389  +    #
          390  +    set nPadding [expr 511 - $::mj_filename_length]
          391  +
          392  +    # We cannot just create a really long database file name to open, as
          393  +    # Linux limits a single component of a path to 255 bytes by default
          394  +    # (and presumably other systems have limits too). So create a directory
          395  +    # hierarchy to work in.
          396  +    #
          397  +    set dirname "d123456789012345678901234567890/"
          398  +    set nDir [expr $nPadding / 32]
          399  +    if { $nDir } {
          400  +      set p [string repeat $dirname $nDir]
          401  +      file mkdir $p
          402  +      cd $p
          403  +    }
          404  +
          405  +    set padding [string repeat x [expr $nPadding %32]]
          406  +    set prefix "test.db${padding}"
          407  +  }
          408  +} {
          409  +  eval $tcl
          410  +  foreach {tn2 sql} {
          411  +    o { 
          412  +      PRAGMA main.synchronous=OFF;
          413  +      PRAGMA aux.synchronous=OFF;
          414  +    }
          415  +    o512 { 
          416  +      PRAGMA main.synchronous=OFF;
          417  +      PRAGMA aux.synchronous=OFF;
          418  +      PRAGMA main.page_size = 512;
          419  +      PRAGMA aux.page_size = 512;
          420  +    }
          421  +    n { 
          422  +      PRAGMA main.synchronous=NORMAL;
          423  +      PRAGMA aux.synchronous=NORMAL;
          424  +    }
          425  +    f { 
          426  +      PRAGMA main.synchronous=FULL;
          427  +      PRAGMA aux.synchronous=FULL;
          428  +    }
          429  +  } {
          430  +
          431  +    set tn "${tn1}.${tn2}"
          432  +  
          433  +    # Set up a connection to have two databases, test.db (main) and 
          434  +    # test.db2 (aux). Then run a multi-file transaction on them. The
          435  +    # VFS will snapshot the file-system just before the master-journal
          436  +    # file is deleted to commit the transaction.
          437  +    #
          438  +    tv filter xDelete
          439  +    do_test pager1-4.4.$tn.1 {
          440  +      faultsim_delete_and_reopen $prefix
          441  +      execsql "
          442  +        ATTACH '${prefix}2' AS aux;
          443  +        $sql
          444  +        CREATE TABLE a(x);
          445  +        CREATE TABLE aux.b(x);
          446  +        INSERT INTO a VALUES('double-you');
          447  +        INSERT INTO a VALUES('why');
          448  +        INSERT INTO a VALUES('zed');
          449  +        INSERT INTO b VALUES('won');
          450  +        INSERT INTO b VALUES('too');
          451  +        INSERT INTO b VALUES('free');
          452  +      "
          453  +      execsql {
          454  +        BEGIN;
          455  +          INSERT INTO a SELECT * FROM b WHERE rowid<=3;
          456  +          INSERT INTO b SELECT * FROM a WHERE rowid<=3;
          457  +        COMMIT;
          458  +      }
          459  +    } {}
          460  +    tv filter {}
          461  +    
          462  +    # Check that the transaction was committed successfully.
          463  +    #
          464  +    do_execsql_test pager1-4.4.$tn.2 {
          465  +      SELECT * FROM a
          466  +    } {double-you why zed won too free}
          467  +    do_execsql_test pager1-4.4.$tn.3 {
          468  +      SELECT * FROM b
          469  +    } {won too free double-you why zed}
          470  +    
          471  +    # Restore the file-system and reopen the databases. Check that it now
          472  +    # appears that the transaction was not committed (because the file-system
          473  +    # was restored to the state where it had not been).
          474  +    #
          475  +    do_test pager1-4.4.$tn.4 {
          476  +      faultsim_restore_and_reopen $prefix
          477  +      execsql "ATTACH '${prefix}2' AS aux"
          478  +    } {}
          479  +    do_execsql_test pager1-4.4.$tn.5 {SELECT * FROM a} {double-you why zed}
          480  +    do_execsql_test pager1-4.4.$tn.6 {SELECT * FROM b} {won too free}
          481  +    
          482  +    # Restore the file-system again. This time, before reopening the databases,
          483  +    # delete the master-journal file from the file-system. It now appears that
          484  +    # the transaction was committed (no master-journal file == no rollback).
          485  +    #
          486  +    do_test pager1-4.4.$tn.7 {
          487  +      faultsim_restore_and_reopen $prefix
          488  +      foreach f [glob ${prefix}-mj*] { file delete -force $f }
          489  +      execsql "ATTACH '${prefix}2' AS aux"
          490  +    } {}
          491  +    do_execsql_test pager1-4.4.$tn.8 {
          492  +      SELECT * FROM a
          493  +    } {double-you why zed won too free}
          494  +    do_execsql_test pager1-4.4.$tn.9 {
          495  +      SELECT * FROM b
          496  +    } {won too free double-you why zed}
          497  +  }
          498  +
          499  +  cd $pwd
          500  +}
          501  +db close
          502  +tv delete
   356    503   
   357    504   #-------------------------------------------------------------------------
   358    505   # The following tests deal with multi-file commits.
   359    506   #
   360    507   # pager1-5.1.*: The case where a multi-file cannot be committed because
   361    508   #               another connection is holding a SHARED lock on one of the
   362    509   #               files. After the SHARED lock is removed, the COMMIT succeeds.
................................................................................
   369    516   #               name is added to a journal file immediately after the last
   370    517   #               journal record. But with synchronous=full, extra unused space
   371    518   #               is allocated between the last journal record and the 
   372    519   #               master-journal file name so that the master-journal file
   373    520   #               name does not lie on the same sector as the last journal file
   374    521   #               record.
   375    522   #
   376         -# pager1-5.5.*: 
          523  +# pager1-5.5.*: Check that in journal_mode=PERSIST mode, a journal file is
          524  +#               truncated to zero bytes when a multi-file transaction is 
          525  +#               committed (instead of the first couple of bytes being zeroed).
          526  +#
   377    527   #
   378    528   do_test pager1-5.1.1 {
   379    529     faultsim_delete_and_reopen
   380    530     execsql {
   381    531       ATTACH 'test.db2' AS aux;
   382    532       CREATE TABLE t1(a, b);
   383    533       CREATE TABLE aux.t2(a, b);