SQLite

Check-in [b78e58ae15]
Login

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

Overview
Comment:Do not create, open, or initialize the rollback journal until something actually needs to be written into the journal. That way, expensive filesystem operations are avoided if the transaction ends up being a no-op.
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: b78e58ae1570ab4d66a69db445a752c6456038a0
User & Date: drh 2010-03-19 15:48:14.000
Original Comment: Do create, open, or initialize the rollback journal until something actually needs to be written into the journal. That way, expensive filesystem operations are avoided if the transaction ends up being a no-op.
References
2010-06-17
13:22 Ticket [fc62af4523] Executing "PRAGMA journal_mode" may delete journal file while it is in use. status still Open with 1 other change (artifact: 068d606ebb user: drh)
Context
2010-03-19
16:52
Make sure the in-journal boolean vector is cleared and released when ending a transaction. This fixes an obscure problem with the previous check-in. (check-in: 69d749d93d user: drh tags: trunk)
15:48
Do not create, open, or initialize the rollback journal until something actually needs to be written into the journal. That way, expensive filesystem operations are avoided if the transaction ends up being a no-op. (check-in: b78e58ae15 user: drh tags: trunk)
13:59
Rewrite a couple of queries used internally by FTS3 to take advantage of the "SELECT max(x) FROM ..." optimization. (check-in: b7e42ae774 user: dan tags: trunk)
Changes
Unified Diff Ignore Whitespace Patch
Changes to src/pager.c.
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322

1323
1324
1325
1326
1327
1328
1329
      }
    }

#ifdef SQLITE_CHECK_PAGES
    sqlite3PcacheIterateDirty(pPager->pPCache, pager_set_pagehash);
#endif

    sqlite3PcacheCleanAll(pPager->pPCache);
    sqlite3BitvecDestroy(pPager->pInJournal);
    pPager->pInJournal = 0;
    pPager->nRec = 0;
  }


  if( !pPager->exclusiveMode ){
    rc2 = osUnlock(pPager->fd, SHARED_LOCK);
    pPager->state = PAGER_SHARED;
    pPager->changeCountDone = 0;
  }else if( pPager->state==PAGER_SYNCED ){
    pPager->state = PAGER_EXCLUSIVE;







<




>







1311
1312
1313
1314
1315
1316
1317

1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
      }
    }

#ifdef SQLITE_CHECK_PAGES
    sqlite3PcacheIterateDirty(pPager->pPCache, pager_set_pagehash);
#endif


    sqlite3BitvecDestroy(pPager->pInJournal);
    pPager->pInJournal = 0;
    pPager->nRec = 0;
  }
  sqlite3PcacheCleanAll(pPager->pPCache);

  if( !pPager->exclusiveMode ){
    rc2 = osUnlock(pPager->fd, SHARED_LOCK);
    pPager->state = PAGER_SHARED;
    pPager->changeCountDone = 0;
  }else if( pPager->state==PAGER_SYNCED ){
    pPager->state = PAGER_EXCLUSIVE;
4102
4103
4104
4105
4106
4107
4108
4109
4110


4111
4112
4113
4114
4115
4116
4117
4118
4119
4120
4121
4122
4123
4124
4125
4126
4127
4128
4129
4130
4131
4132
4133
4134
4135
4136
    if( rc==SQLITE_OK ){
      pPager->state = PAGER_RESERVED;
      if( exFlag ){
        rc = pager_wait_on_lock(pPager, EXCLUSIVE_LOCK);
      }
    }

    /* If the required locks were successfully obtained, open the journal
    ** file and write the first journal-header to it.


    */
    if( rc==SQLITE_OK && pPager->journalMode!=PAGER_JOURNALMODE_OFF ){
      rc = pager_open_journal(pPager);
    }
  }else if( isOpen(pPager->jfd) && pPager->journalOff==0 ){
    /* This happens when the pager was in exclusive-access mode the last
    ** time a (read or write) transaction was successfully concluded
    ** by this connection. Instead of deleting the journal file it was 
    ** kept open and either was truncated to 0 bytes or its header was
    ** overwritten with zeros.
    */
    assert( pPager->nRec==0 );
    assert( pPager->dbOrigSize==0 );
    assert( pPager->pInJournal==0 );
    rc = pager_open_journal(pPager);
  }

  PAGERTRACE(("TRANSACTION %d\n", PAGERID(pPager)));
  assert( !isOpen(pPager->jfd) || pPager->journalOff>0 || rc!=SQLITE_OK );
  if( rc!=SQLITE_OK ){
    assert( !pPager->dbModified );
    /* Ignore any IO error that occurs within pager_end_transaction(). The
    ** purpose of this call is to reset the internal state of the pager
    ** sub-system. It doesn't matter if the journal-file is not properly
    ** finalized at this point (since it is not a valid journal file anyway).
    */







|
|
>
>

<
<
<














<







4102
4103
4104
4105
4106
4107
4108
4109
4110
4111
4112
4113



4114
4115
4116
4117
4118
4119
4120
4121
4122
4123
4124
4125
4126
4127

4128
4129
4130
4131
4132
4133
4134
    if( rc==SQLITE_OK ){
      pPager->state = PAGER_RESERVED;
      if( exFlag ){
        rc = pager_wait_on_lock(pPager, EXCLUSIVE_LOCK);
      }
    }

    /* No need to open the journal file at this time.  It will be
    ** opened before it is written to.  If we defer opening the journal,
    ** we might save the work of creating a file if the transaction
    ** ends up being a no-op.
    */



  }else if( isOpen(pPager->jfd) && pPager->journalOff==0 ){
    /* This happens when the pager was in exclusive-access mode the last
    ** time a (read or write) transaction was successfully concluded
    ** by this connection. Instead of deleting the journal file it was 
    ** kept open and either was truncated to 0 bytes or its header was
    ** overwritten with zeros.
    */
    assert( pPager->nRec==0 );
    assert( pPager->dbOrigSize==0 );
    assert( pPager->pInJournal==0 );
    rc = pager_open_journal(pPager);
  }

  PAGERTRACE(("TRANSACTION %d\n", PAGERID(pPager)));

  if( rc!=SQLITE_OK ){
    assert( !pPager->dbModified );
    /* Ignore any IO error that occurs within pager_end_transaction(). The
    ** purpose of this call is to reset the internal state of the pager
    ** sub-system. It doesn't matter if the journal-file is not properly
    ** finalized at this point (since it is not a valid journal file anyway).
    */
4178
4179
4180
4181
4182
4183
4184
4185
4186
4187
4188
4189
4190
4191
4192
4193
4194
4195
4196
  }else{

    /* If we get this far, it means that the page needs to be
    ** written to the transaction journal or the ckeckpoint journal
    ** or both.
    **
    ** Higher level routines should have already started a transaction,
    ** which means they have acquired the necessary locks and opened
    ** a rollback journal.  Double-check to makes sure this is the case.
    */
    rc = sqlite3PagerBegin(pPager, 0, pPager->subjInMemory);
    if( NEVER(rc!=SQLITE_OK) ){
      return rc;
    }
    if( !isOpen(pPager->jfd) && pPager->journalMode!=PAGER_JOURNALMODE_OFF ){
      assert( pPager->useJournal );
      rc = pager_open_journal(pPager);
      if( rc!=SQLITE_OK ) return rc;
    }







|
|


|







4176
4177
4178
4179
4180
4181
4182
4183
4184
4185
4186
4187
4188
4189
4190
4191
4192
4193
4194
  }else{

    /* If we get this far, it means that the page needs to be
    ** written to the transaction journal or the ckeckpoint journal
    ** or both.
    **
    ** Higher level routines should have already started a transaction,
    ** which means they have acquired the necessary locks but the rollback
    ** journal might not yet be open.
    */
    rc = sqlite3PagerBegin(pPager, 0, pPager->subjInMemory);
    if( rc!=SQLITE_OK ){
      return rc;
    }
    if( !isOpen(pPager->jfd) && pPager->journalMode!=PAGER_JOURNALMODE_OFF ){
      assert( pPager->useJournal );
      rc = pager_open_journal(pPager);
      if( rc!=SQLITE_OK ) return rc;
    }
4917
4918
4919
4920
4921
4922
4923
4924
4925
4926
4927
4928
4929
4930
4931
4932
4933
4934
4935
4936
4937
4938
4939
4940
4941
4942
4943
4944
4945
4946
4947
4948
4949
4950
4951
4952
4953
4954
  int rc = SQLITE_OK;                       /* Return code */
  int nCurrent = pPager->nSavepoint;        /* Current number of savepoints */

  if( nSavepoint>nCurrent && pPager->useJournal ){
    int ii;                                 /* Iterator variable */
    PagerSavepoint *aNew;                   /* New Pager.aSavepoint array */

    /* Either there is no active journal or the sub-journal is open or 
    ** the journal is always stored in memory */
    assert( pPager->nSavepoint==0 || isOpen(pPager->sjfd) ||
            pPager->journalMode==PAGER_JOURNALMODE_MEMORY );

    /* Grow the Pager.aSavepoint array using realloc(). Return SQLITE_NOMEM
    ** if the allocation fails. Otherwise, zero the new portion in case a 
    ** malloc failure occurs while populating it in the for(...) loop below.
    */
    aNew = (PagerSavepoint *)sqlite3Realloc(
        pPager->aSavepoint, sizeof(PagerSavepoint)*nSavepoint
    );
    if( !aNew ){
      return SQLITE_NOMEM;
    }
    memset(&aNew[nCurrent], 0, (nSavepoint-nCurrent) * sizeof(PagerSavepoint));
    pPager->aSavepoint = aNew;
    pPager->nSavepoint = nSavepoint;

    /* Populate the PagerSavepoint structures just allocated. */
    for(ii=nCurrent; ii<nSavepoint; ii++){
      assert( pPager->dbSizeValid );
      aNew[ii].nOrig = pPager->dbSize;
      if( isOpen(pPager->jfd) && ALWAYS(pPager->journalOff>0) ){
        aNew[ii].iOffset = pPager->journalOff;
      }else{
        aNew[ii].iOffset = JOURNAL_HDR_SZ(pPager);
      }
      aNew[ii].iSubRec = pPager->nSubRec;
      aNew[ii].pInSavepoint = sqlite3BitvecCreate(pPager->dbSize);
      if( !aNew[ii].pInSavepoint ){







<
<
<
<
<


















|







4915
4916
4917
4918
4919
4920
4921





4922
4923
4924
4925
4926
4927
4928
4929
4930
4931
4932
4933
4934
4935
4936
4937
4938
4939
4940
4941
4942
4943
4944
4945
4946
4947
  int rc = SQLITE_OK;                       /* Return code */
  int nCurrent = pPager->nSavepoint;        /* Current number of savepoints */

  if( nSavepoint>nCurrent && pPager->useJournal ){
    int ii;                                 /* Iterator variable */
    PagerSavepoint *aNew;                   /* New Pager.aSavepoint array */






    /* Grow the Pager.aSavepoint array using realloc(). Return SQLITE_NOMEM
    ** if the allocation fails. Otherwise, zero the new portion in case a 
    ** malloc failure occurs while populating it in the for(...) loop below.
    */
    aNew = (PagerSavepoint *)sqlite3Realloc(
        pPager->aSavepoint, sizeof(PagerSavepoint)*nSavepoint
    );
    if( !aNew ){
      return SQLITE_NOMEM;
    }
    memset(&aNew[nCurrent], 0, (nSavepoint-nCurrent) * sizeof(PagerSavepoint));
    pPager->aSavepoint = aNew;
    pPager->nSavepoint = nSavepoint;

    /* Populate the PagerSavepoint structures just allocated. */
    for(ii=nCurrent; ii<nSavepoint; ii++){
      assert( pPager->dbSizeValid );
      aNew[ii].nOrig = pPager->dbSize;
      if( isOpen(pPager->jfd) && pPager->journalOff>0 ){
        aNew[ii].iOffset = pPager->journalOff;
      }else{
        aNew[ii].iOffset = JOURNAL_HDR_SZ(pPager);
      }
      aNew[ii].iSubRec = pPager->nSubRec;
      aNew[ii].pInSavepoint = sqlite3BitvecCreate(pPager->dbSize);
      if( !aNew[ii].pInSavepoint ){
5333
5334
5335
5336
5337
5338
5339









5340
5341
5342
5343
5344
5345
5346
              || eMode==PAGER_JOURNALMODE_OFF)
   && !pPager->dbModified
   && (!isOpen(pPager->jfd) || 0==pPager->journalOff)
  ){
    if( isOpen(pPager->jfd) ){
      sqlite3OsClose(pPager->jfd);
    }









    pPager->journalMode = (u8)eMode;
  }
  return (int)pPager->journalMode;
}

/*
** Get/set the size-limit used for persistent journal files.







>
>
>
>
>
>
>
>
>







5326
5327
5328
5329
5330
5331
5332
5333
5334
5335
5336
5337
5338
5339
5340
5341
5342
5343
5344
5345
5346
5347
5348
              || eMode==PAGER_JOURNALMODE_OFF)
   && !pPager->dbModified
   && (!isOpen(pPager->jfd) || 0==pPager->journalOff)
  ){
    if( isOpen(pPager->jfd) ){
      sqlite3OsClose(pPager->jfd);
    }
    assert( (PAGER_JOURNALMODE_TRUNCATE & 1)==1 );
    assert( (PAGER_JOURNALMODE_PERSIST & 1)==1 );
    assert( (PAGER_JOURNALMODE_DELETE & 1)==0 );
    assert( (PAGER_JOURNALMODE_MEMORY & 1)==0 );
    assert( (PAGER_JOURNALMODE_OFF & 1)==0 );
    if( (pPager->journalMode & 1)==1 && (eMode & 1)==0
         && !pPager->exclusiveMode ){
      sqlite3OsDelete(pPager->pVfs, pPager->zJournal, 0);
    }
    pPager->journalMode = (u8)eMode;
  }
  return (int)pPager->journalMode;
}

/*
** Get/set the size-limit used for persistent journal files.
Changes to test/jrnlmode.test.
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
    do_test jrnlmode-6.5 {
      execsql {
        PRAGMA journal_mode = MEMORY;
        BEGIN;
          INSERT INTO t4 VALUES(3, 4);
      }
      file exists test.db-journal
    } {1}
    do_test jrnlmode-6.7 {
      execsql {
        COMMIT;
        SELECT * FROM t4;
      }
    } {1 2 3 4}
    do_test jrnlmode-6.8 {
      file exists test.db-journal
    } {1}
    do_test jrnlmode-6.9 {
      execsql {
        PRAGMA journal_mode = DELETE;
        BEGIN IMMEDIATE; COMMIT;
      }
      file exists test.db-journal
    } {0}
  }
}

finish_test







|








|



|







480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
    do_test jrnlmode-6.5 {
      execsql {
        PRAGMA journal_mode = MEMORY;
        BEGIN;
          INSERT INTO t4 VALUES(3, 4);
      }
      file exists test.db-journal
    } {0}
    do_test jrnlmode-6.7 {
      execsql {
        COMMIT;
        SELECT * FROM t4;
      }
    } {1 2 3 4}
    do_test jrnlmode-6.8 {
      file exists test.db-journal
    } {0}
    do_test jrnlmode-6.9 {
      execsql {
        PRAGMA journal_mode = DELETE;
        BEGIN IMMEDIATE; INSERT INTO t4 VALUES(1,2); COMMIT;
      }
      file exists test.db-journal
    } {0}
  }
}

finish_test
Changes to test/misc1.test.
477
478
479
480
481
482
483
484
485




486
487
488
489
490
491
492
#
do_test misc1-14.1 {
  file mkdir tempdir
  cd tempdir
  execsql {BEGIN}
  file exists ./test.db-journal
} {0}
do_test misc1-14.2 {
  execsql {UPDATE t1 SET a=0 WHERE 0}




  file exists ../test.db-journal
} {1}
do_test misc1-14.3 {
  cd ..
  file delete -force tempdir
  execsql {COMMIT}
  file exists ./test.db-journal







|
|
>
>
>
>







477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
#
do_test misc1-14.1 {
  file mkdir tempdir
  cd tempdir
  execsql {BEGIN}
  file exists ./test.db-journal
} {0}
do_test misc1-14.2a {
  execsql {UPDATE t1 SET a=a||'x' WHERE 0}
  file exists ../test.db-journal
} {0}
do_test misc1-14.2b {
  execsql {UPDATE t1 SET a=a||'y' WHERE 1}
  file exists ../test.db-journal
} {1}
do_test misc1-14.3 {
  cd ..
  file delete -force tempdir
  execsql {COMMIT}
  file exists ./test.db-journal