/ Check-in [e98a7f87]
Login

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

Overview
Comment:Fix some problems with multi-file transactions in persistent journal mode. (CVS 5102)
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: e98a7f87f91c62676f94ad5a0c4980ab929ca79d
User & Date: danielk1977 2008-05-07 19:11:03
Context
2008-05-08
01:11
Fix the new ioerr4.test so that it plays well with others. (CVS 5103) check-in: 75df2d3d user: drh tags: trunk
2008-05-07
19:11
Fix some problems with multi-file transactions in persistent journal mode. (CVS 5102) check-in: e98a7f87 user: danielk1977 tags: trunk
18:59
Added test cases for corrupt SerialTypeLen header values, and additional check to improve detection of corrupt values. (CVS 5101) check-in: 530c6360 user: shane tags: trunk
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Changes to src/btree.c.

     5      5   ** a legal notice, here is a blessing:
     6      6   **
     7      7   **    May you do good and not evil.
     8      8   **    May you find forgiveness for yourself and forgive others.
     9      9   **    May you share freely, never taking more than you give.
    10     10   **
    11     11   *************************************************************************
    12         -** $Id: btree.c,v 1.456 2008/05/07 07:13:16 danielk1977 Exp $
           12  +** $Id: btree.c,v 1.457 2008/05/07 19:11:03 danielk1977 Exp $
    13     13   **
    14     14   ** This file implements a external (disk-based) database using BTrees.
    15     15   ** See the header comment on "btreeInt.h" for additional information.
    16     16   ** Including a description of file format and an overview of operation.
    17     17   */
    18     18   #include "btreeInt.h"
    19     19   
................................................................................
  6835   6835       ** present in pTo before the copy operation, journal page i from pTo.
  6836   6836       */
  6837   6837       if( i!=iSkip && i<=nToPage ){
  6838   6838         DbPage *pDbPage = 0;
  6839   6839         rc = sqlite3PagerGet(pBtTo->pPager, i, &pDbPage);
  6840   6840         if( rc==SQLITE_OK ){
  6841   6841           rc = sqlite3PagerWrite(pDbPage);
         6842  +        if( rc==SQLITE_OK && i>nFromPage ){
         6843  +          /* Yeah.  It seems wierd to call DontWrite() right after Write(). But
         6844  +          ** that is because the names of those procedures do not exactly 
         6845  +          ** represent what they do.  Write() really means "put this page in the
         6846  +          ** rollback journal and mark it as dirty so that it will be written
         6847  +          ** to the database file later."  DontWrite() undoes the second part of
         6848  +          ** that and prevents the page from being written to the database. The
         6849  +          ** page is still on the rollback journal, though.  And that is the 
         6850  +          ** whole point of this block: to put pages on the rollback journal. 
         6851  +          */
         6852  +          sqlite3PagerDontWrite(pDbPage);
         6853  +        }
         6854  +        sqlite3PagerUnref(pDbPage);
  6842   6855         }
  6843         -      if( rc==SQLITE_OK && i>nFromPage ){
  6844         -        /* Yeah.  It seems wierd to call DontWrite() right after Write(). But
  6845         -        ** that is because the names of those procedures do not exactly 
  6846         -        ** represent what they do.  Write() really means "put this page in the
  6847         -        ** rollback journal and mark it as dirty so that it will be written
  6848         -        ** to the database file later."  DontWrite() undoes the second part of
  6849         -        ** that and prevents the page from being written to the database. The
  6850         -        ** page is still on the rollback journal, though.  And that is the 
  6851         -        ** whole point of this block: to put pages on the rollback journal. 
  6852         -        */
  6853         -        sqlite3PagerDontWrite(pDbPage);
  6854         -      }
  6855         -      sqlite3PagerUnref(pDbPage);
  6856   6856       }
  6857   6857   
  6858   6858       /* Overwrite the data in page i of the target database */
  6859   6859       if( rc==SQLITE_OK && i!=iSkip && i<=nNewPage ){
  6860   6860   
  6861   6861         DbPage *pToPage = 0;
  6862   6862         sqlite3_int64 iOff;

Changes to src/pager.c.

    14     14   ** The pager is used to access a database disk file.  It implements
    15     15   ** atomic commit and rollback through the use of a journal file that
    16     16   ** is separate from the database file.  The pager also implements file
    17     17   ** locking to prevent two processes from writing the same database
    18     18   ** file simultaneously, or one process from reading the database while
    19     19   ** another is writing.
    20     20   **
    21         -** @(#) $Id: pager.c,v 1.442 2008/05/07 12:45:41 drh Exp $
           21  +** @(#) $Id: pager.c,v 1.443 2008/05/07 19:11:03 danielk1977 Exp $
    22     22   */
    23     23   #ifndef SQLITE_OMIT_DISKIO
    24     24   #include "sqliteInt.h"
    25     25   #include <assert.h>
    26     26   #include <string.h>
    27     27   
    28     28   /*
................................................................................
   964    964   }
   965    965   
   966    966   /*
   967    967   ** Write zeros over the header of the journal file.  This has the
   968    968   ** effect of invalidating the journal file and committing the
   969    969   ** transaction.
   970    970   */
   971         -static int zeroJournalHdr(Pager *pPager){
   972         -  int rc;
          971  +static int zeroJournalHdr(Pager *pPager, int doTruncate){
          972  +  int rc = SQLITE_OK;
   973    973     static const char zeroHdr[28];
   974    974   
   975         -  IOTRACE(("JZEROHDR %p\n", pPager))
   976         -  rc = sqlite3OsWrite(pPager->jfd, zeroHdr, sizeof(zeroHdr), 0);
   977         -  if( rc==SQLITE_OK ){
   978         -    rc = sqlite3OsSync(pPager->jfd, SQLITE_SYNC_DATAONLY | pPager->sync_flags);
          975  +  if( pPager->journalOff ){
          976  +    IOTRACE(("JZEROHDR %p\n", pPager))
          977  +    if( doTruncate ){
          978  +      rc = sqlite3OsTruncate(pPager->jfd, 0);
          979  +    }else{
          980  +      rc = sqlite3OsWrite(pPager->jfd, zeroHdr, sizeof(zeroHdr), 0);
          981  +    }
          982  +    if( rc==SQLITE_OK ){
          983  +      rc = sqlite3OsSync(pPager->jfd, SQLITE_SYNC_DATAONLY|pPager->sync_flags);
          984  +    }
   979    985     }
   980    986     return rc;
   981    987   }
   982    988   
   983    989   /*
   984    990   ** The journal file must be open when this routine is called. A journal
   985    991   ** header (JOURNAL_HDR_SZ bytes) is written into the journal file at the
................................................................................
  1159   1165   ** this call is a no-op.
  1160   1166   */
  1161   1167   static int writeMasterJournal(Pager *pPager, const char *zMaster){
  1162   1168     int rc;
  1163   1169     int len; 
  1164   1170     int i; 
  1165   1171     i64 jrnlOff;
         1172  +  i64 jrnlSize;
  1166   1173     u32 cksum = 0;
  1167   1174     char zBuf[sizeof(aJournalMagic)+2*4];
  1168   1175   
  1169   1176     if( !zMaster || pPager->setMaster) return SQLITE_OK;
  1170   1177     pPager->setMaster = 1;
  1171   1178   
  1172   1179     len = strlen(zMaster);
................................................................................
  1192   1199     if( rc!=SQLITE_OK ) return rc;
  1193   1200     jrnlOff += len;
  1194   1201   
  1195   1202     put32bits(zBuf, len);
  1196   1203     put32bits(&zBuf[4], cksum);
  1197   1204     memcpy(&zBuf[8], aJournalMagic, sizeof(aJournalMagic));
  1198   1205     rc = sqlite3OsWrite(pPager->jfd, zBuf, 8+sizeof(aJournalMagic), jrnlOff);
         1206  +  jrnlOff += 8+sizeof(aJournalMagic);
  1199   1207     pPager->needSync = !pPager->noSync;
         1208  +
         1209  +  /* If the pager is in peristent-journal mode, then the physical 
         1210  +  ** journal-file may extend past the end of the master-journal name
         1211  +  ** and 8 bytes of magic data just written to the file. This is 
         1212  +  ** dangerous because the code to rollback a hot-journal file
         1213  +  ** will not be able to find the master-journal name to determine 
         1214  +  ** whether or not the journal is hot. 
         1215  +  **
         1216  +  ** Easiest thing to do in this scenario is to truncate the journal 
         1217  +  ** file to the required size.
         1218  +  */ 
         1219  +  if( (rc==SQLITE_OK)
         1220  +   && (rc = sqlite3OsFileSize(pPager->jfd, &jrnlSize))==SQLITE_OK
         1221  +   && jrnlSize>jrnlOff
         1222  +  ){
         1223  +    rc = sqlite3OsTruncate(pPager->jfd, jrnlOff);
         1224  +  }
  1200   1225     return rc;
  1201   1226   }
  1202   1227   
  1203   1228   /*
  1204   1229   ** Add or remove a page from the list of all pages that are in the
  1205   1230   ** statement journal.
  1206   1231   **
................................................................................
  1354   1379   **
  1355   1380   ** The journal file is either deleted or truncated.
  1356   1381   **
  1357   1382   ** TODO: Consider keeping the journal file open for temporary databases.
  1358   1383   ** This might give a performance improvement on windows where opening
  1359   1384   ** a file is an expensive operation.
  1360   1385   */
  1361         -static int pager_end_transaction(Pager *pPager){
         1386  +static int pager_end_transaction(Pager *pPager, int hasMaster){
  1362   1387     PgHdr *pPg;
  1363   1388     int rc = SQLITE_OK;
  1364   1389     int rc2 = SQLITE_OK;
  1365   1390     assert( !MEMDB );
  1366   1391     if( pPager->state<PAGER_RESERVED ){
  1367   1392       return SQLITE_OK;
  1368   1393     }
................................................................................
  1370   1395     if( pPager->stmtOpen && !pPager->exclusiveMode ){
  1371   1396       sqlite3OsClose(pPager->stfd);
  1372   1397       pPager->stmtOpen = 0;
  1373   1398     }
  1374   1399     if( pPager->journalOpen ){
  1375   1400       if( (pPager->exclusiveMode ||
  1376   1401            pPager->journalMode==PAGER_JOURNALMODE_PERSIST) 
  1377         -       && (rc = zeroJournalHdr(pPager))==SQLITE_OK ){
         1402  +       && (rc = zeroJournalHdr(pPager, hasMaster))==SQLITE_OK ){
  1378   1403         pPager->journalOff = 0;
  1379   1404         pPager->journalStarted = 0;
  1380   1405       }else{
  1381   1406         sqlite3OsClose(pPager->jfd);
  1382   1407         pPager->journalOpen = 0;
  1383   1408         if( rc==SQLITE_OK ){
  1384   1409           rc = sqlite3OsDelete(pPager->pVfs, pPager->zJournal, 0);
................................................................................
  1904   1929   
  1905   1930   end_playback:
  1906   1931     if( rc==SQLITE_OK ){
  1907   1932       zMaster = pPager->pTmpSpace;
  1908   1933       rc = readMasterJournal(pPager->jfd, zMaster, pPager->pVfs->mxPathname+1);
  1909   1934     }
  1910   1935     if( rc==SQLITE_OK ){
  1911         -    rc = pager_end_transaction(pPager);
         1936  +    rc = pager_end_transaction(pPager, zMaster[0]!='\0');
  1912   1937     }
  1913   1938     if( rc==SQLITE_OK && zMaster[0] ){
  1914   1939       /* If there was a master journal and this routine will return success,
  1915   1940       ** see if it is possible to delete the master journal.
  1916   1941       */
  1917   1942       rc = pager_delmaster(pPager, zMaster);
  1918   1943     }
................................................................................
  3956   3981   
  3957   3982     rc = writeJournalHdr(pPager);
  3958   3983   
  3959   3984     if( pPager->stmtAutoopen && rc==SQLITE_OK ){
  3960   3985       rc = sqlite3PagerStmtBegin(pPager);
  3961   3986     }
  3962   3987     if( rc!=SQLITE_OK && rc!=SQLITE_NOMEM && rc!=SQLITE_IOERR_NOMEM ){
  3963         -    rc = pager_end_transaction(pPager);
         3988  +    rc = pager_end_transaction(pPager, 0);
  3964   3989       if( rc==SQLITE_OK ){
  3965   3990         rc = SQLITE_FULL;
  3966   3991       }
  3967   3992     }
  3968   3993     return rc;
  3969   3994   
  3970   3995   failed_to_open_journal:
................................................................................
  4755   4780   #endif
  4756   4781       pPager->pStmt = 0;
  4757   4782       pPager->state = PAGER_SHARED;
  4758   4783       pagerLeave(pPager);
  4759   4784       return SQLITE_OK;
  4760   4785     }
  4761   4786     assert( pPager->state==PAGER_SYNCED || !pPager->dirtyCache );
  4762         -  rc = pager_end_transaction(pPager);
         4787  +  rc = pager_end_transaction(pPager, pPager->setMaster);
  4763   4788     rc = pager_error(pPager, rc);
  4764   4789     pagerLeave(pPager);
  4765   4790     return rc;
  4766   4791   }
  4767   4792   
  4768   4793   /*
  4769   4794   ** Rollback all changes.  The database falls back to PAGER_SHARED mode.
................................................................................
  4814   4839       pPager->stmtInUse = 0;
  4815   4840       pPager->state = PAGER_SHARED;
  4816   4841       return SQLITE_OK;
  4817   4842     }
  4818   4843   
  4819   4844     pagerEnter(pPager);
  4820   4845     if( !pPager->dirtyCache || !pPager->journalOpen ){
  4821         -    rc = pager_end_transaction(pPager);
         4846  +    rc = pager_end_transaction(pPager, pPager->setMaster);
  4822   4847       pagerLeave(pPager);
  4823   4848       return rc;
  4824   4849     }
  4825   4850   
  4826   4851     if( pPager->errCode && pPager->errCode!=SQLITE_FULL ){
  4827   4852       if( pPager->state>=PAGER_EXCLUSIVE ){
  4828   4853         pager_playback(pPager, 0);
................................................................................
  4829   4854       }
  4830   4855       pagerLeave(pPager);
  4831   4856       return pPager->errCode;
  4832   4857     }
  4833   4858     if( pPager->state==PAGER_RESERVED ){
  4834   4859       int rc2;
  4835   4860       rc = pager_playback(pPager, 0);
  4836         -    rc2 = pager_end_transaction(pPager);
         4861  +    rc2 = pager_end_transaction(pPager, pPager->setMaster);
  4837   4862       if( rc==SQLITE_OK ){
  4838   4863         rc = rc2;
  4839   4864       }
  4840   4865     }else{
  4841   4866       rc = pager_playback(pPager, 0);
  4842   4867     }
  4843   4868     /* pager_reset(pPager); */

Changes to test/jrnlmode.test.

     7      7   #    May you find forgiveness for yourself and forgive others.
     8      8   #    May you share freely, never taking more than you give.
     9      9   #
    10     10   #***********************************************************************
    11     11   # This file implements regression tests for SQLite library. The focus
    12     12   # of these tests is the journal mode pragma.
    13     13   #
    14         -# $Id: jrnlmode.test,v 1.1 2008/04/19 20:34:19 drh Exp $
           14  +# $Id: jrnlmode.test,v 1.2 2008/05/07 19:11:03 danielk1977 Exp $
    15     15   
    16     16   set testdir [file dirname $argv0]
    17     17   source $testdir/tester.tcl
    18     18   
    19     19   ifcapable {!pager_pragmas} {
    20     20     finish_test
    21     21     return
................................................................................
   147    147       execsql {
   148    148         DETACH aux1;
   149    149         DETACH aux2;
   150    150         DETACH aux3;
   151    151       }
   152    152     } {}
   153    153   }
          154  +
          155  +ifcapable attach {
          156  +  file delete -force test2.db
          157  +  do_test jrnlmode-2.1 {
          158  +    execsql {
          159  +      ATTACH 'test2.db' AS aux;
          160  +      PRAGMA main.journal_mode = persist;
          161  +      PRAGMA aux.journal_mode = persist;
          162  +      CREATE TABLE abc(a, b, c);
          163  +      CREATE TABLE aux.def(d, e, f);
          164  +    }
          165  +    execsql {
          166  +      BEGIN;
          167  +      INSERT INTO abc VALUES(1, 2, 3);
          168  +      INSERT INTO def VALUES(4, 5, 6);
          169  +      COMMIT;
          170  +    }
          171  +    list [file exists test.db-journal] [file exists test2.db-journal]
          172  +  } {1 1}
          173  +
          174  +  do_test jrnlmode-2.2 {
          175  +    file size test.db-journal
          176  +  } {0}
          177  +
          178  +  do_test jrnlmode-2.3 {
          179  +    execsql {
          180  +      SELECT * FROM abc;
          181  +    }
          182  +  } {1 2 3}
          183  +
          184  +  do_test jrnlmode-2.4 {
          185  +    file size test.db-journal
          186  +  } {0}
          187  +
          188  +  do_test jrnlmode-2.5 {
          189  +    execsql {
          190  +      SELECT * FROM def;
          191  +    }
          192  +  } {4 5 6}
          193  +
          194  +}
          195  +
          196  +
   154    197   
   155    198   
   156    199   finish_test