/ Check-in [f1ed3689]
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:Update the pager so that it does not try to commit a transaction if there have been no changes to the database. (CVS 5127)
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: f1ed3689239098e0630e8d61f52971bcdf2801b6
User & Date: drh 2008-05-13 00:58:18
Context
2008-05-13
13:27
Make the benign-fault setting recursive. Make all malloc failures during a rollback benign since there is nothing we can do about them. (CVS 5128) check-in: a9d1d931 user: drh tags: trunk
00:58
Update the pager so that it does not try to commit a transaction if there have been no changes to the database. (CVS 5127) check-in: f1ed3689 user: drh tags: trunk
00:57
Update the autoconf makefile so that it includes -lpthread. (CVS 5126) check-in: bd654ebd user: drh tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to src/pager.c.

14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
...
347
348
349
350
351
352
353

354
355
356
357
358
359
360
....
1436
1437
1438
1439
1440
1441
1442

1443
1444
1445
1446
1447
1448
1449
....
4054
4055
4056
4057
4058
4059
4060
4061
4062
4063
4064

4065
4066
4067
4068
4069
4070
4071
....
4171
4172
4173
4174
4175
4176
4177

4178
4179
4180
4181
4182
4183
4184
....
4192
4193
4194
4195
4196
4197
4198

4199
4200
4201
4202
4203
4204
4205
....
4601
4602
4603
4604
4605
4606
4607









4608
4609
4610
4611
4612
4613
4614
....
4752
4753
4754
4755
4756
4757
4758






4759
4760
4761
4762
4763
4764
4765
....
5170
5171
5172
5173
5174
5175
5176

5177
5178
5179
5180
5181
5182
5183
** The pager is used to access a database disk file.  It implements
** atomic commit and rollback through the use of a journal file that
** is separate from the database file.  The pager also implements file
** locking to prevent two processes from writing the same database
** file simultaneously, or one process from reading the database while
** another is writing.
**
** @(#) $Id: pager.c,v 1.444 2008/05/09 16:57:51 danielk1977 Exp $
*/
#ifndef SQLITE_OMIT_DISKIO
#include "sqliteInt.h"
#include <assert.h>
#include <string.h>

/*
................................................................................
  u8 dirtyCache;              /* True if cached pages have changed */
  u8 alwaysRollback;          /* Disable DontRollback() for all pages */
  u8 memDb;                   /* True to inhibit all file I/O */
  u8 setMaster;               /* True if a m-j name has been written to jrnl */
  u8 doNotSync;               /* Boolean. While true, do not spill the cache */
  u8 exclusiveMode;           /* Boolean. True if locking_mode==EXCLUSIVE */
  u8 journalMode;             /* On of the PAGER_JOURNALMODE_* values */

  u8 changeCountDone;         /* Set after incrementing the change-counter */
  u32 vfsFlags;               /* Flags for sqlite3_vfs.xOpen() */
  int errCode;                /* One of several kinds of errors */
  int dbSize;                 /* Number of pages in the file */
  int origDbSize;             /* dbSize before the current change */
  int stmtSize;               /* Size of database (in pages) at stmt_begin() */
  int nRec;                   /* Number of pages written to the journal */
................................................................................
    pPager->state = PAGER_EXCLUSIVE;
  }
  pPager->origDbSize = 0;
  pPager->setMaster = 0;
  pPager->needSync = 0;
  lruListSetFirstSynced(pPager);
  pPager->dbSize = -1;


  return (rc==SQLITE_OK?rc2:rc);
}

/*
** Compute and return a checksum for the page of data.
**
................................................................................
      PAGERTRACE2("TRANSACTION %d\n", PAGERID(pPager));
      if( pPager->useJournal && !pPager->tempFile
             && pPager->journalMode!=PAGER_JOURNALMODE_OFF ){
        rc = pager_open_journal(pPager);
      }
    }
  }else if( pPager->journalOpen && pPager->journalOff==0 ){
    /* This happens when the pager was in exclusive-access mode last
    ** time a (read or write) transaction was successfully concluded
    ** by this connection. Instead of deleting the journal file it was 
    ** kept open and truncated to 0 bytes.

    */
    assert( pPager->nRec==0 );
    assert( pPager->origDbSize==0 );
    assert( pPager->pInJournal==0 );
    sqlite3PagerPagecount(pPager);
    pagerLeave(pPager);
    pPager->pInJournal = sqlite3BitvecCreate( pPager->dbSize );
................................................................................

  /* Mark the page as dirty.  If the page has already been written
  ** to the journal then we can return right away.
  */
  makeDirty(pPg);
  if( pPg->inJournal && (pageInStatement(pPg) || pPager->stmtInUse==0) ){
    pPager->dirtyCache = 1;

  }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.
    **
    ** First check to see that the transaction journal exists and
................................................................................
    assert( pPager->state>=PAGER_RESERVED );
    if( !pPager->journalOpen && pPager->useJournal
          && pPager->journalMode!=PAGER_JOURNALMODE_OFF ){
      rc = pager_open_journal(pPager);
      if( rc!=SQLITE_OK ) return rc;
    }
    pPager->dirtyCache = 1;

  
    /* The transaction journal now exists and we have a RESERVED or an
    ** EXCLUSIVE lock on the main database file.  Write the current page to
    ** the transaction journal if it is not there already.
    */
    if( !pPg->inJournal && (pPager->journalOpen || MEMDB) ){
      if( (int)pPg->pgno <= pPager->origDbSize ){
................................................................................
int sqlite3PagerCommitPhaseOne(
  Pager *pPager, 
  const char *zMaster, 
  Pgno nTrunc,
  int noSync
){
  int rc = SQLITE_OK;










  PAGERTRACE4("DATABASE SYNC: File=%s zMaster=%s nTrunc=%d\n", 
      pPager->zFilename, zMaster, nTrunc);
  pagerEnter(pPager);

  /* If this is an in-memory db, or no pages have been written to, or this
  ** function has already been called, it is a no-op.
................................................................................
  PgHdr *pPg;

  if( pPager->errCode ){
    return pPager->errCode;
  }
  if( pPager->state<PAGER_RESERVED ){
    return SQLITE_ERROR;






  }
  pagerEnter(pPager);
  PAGERTRACE2("COMMIT %d\n", PAGERID(pPager));
  if( MEMDB ){
    pPg = pager_get_all_dirty_pages(pPager);
    while( pPg ){
      PgHistory *pHist = PGHDR_TO_HIST(pPg, pPager);
................................................................................
  }
  pPg->pNextHash = pPager->aHash[h];
  pPager->aHash[h] = pPg;
  pPg->pPrevHash = 0;

  makeDirty(pPg);
  pPager->dirtyCache = 1;


  if( needSyncPgno ){
    /* If needSyncPgno is non-zero, then the journal file needs to be 
    ** sync()ed before any data is written to database file page needSyncPgno.
    ** Currently, no such page exists in the page-cache and the 
    ** Pager.pInJournal bit has been set. This needs to be remedied by loading
    ** the page into the pager-cache and setting the PgHdr.needSync flag.







|







 







>







 







>







 







|


|
>







 







>







 







>







 







>
>
>
>
>
>
>
>
>







 







>
>
>
>
>
>







 







>







14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
...
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
....
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
....
4056
4057
4058
4059
4060
4061
4062
4063
4064
4065
4066
4067
4068
4069
4070
4071
4072
4073
4074
....
4174
4175
4176
4177
4178
4179
4180
4181
4182
4183
4184
4185
4186
4187
4188
....
4196
4197
4198
4199
4200
4201
4202
4203
4204
4205
4206
4207
4208
4209
4210
....
4606
4607
4608
4609
4610
4611
4612
4613
4614
4615
4616
4617
4618
4619
4620
4621
4622
4623
4624
4625
4626
4627
4628
....
4766
4767
4768
4769
4770
4771
4772
4773
4774
4775
4776
4777
4778
4779
4780
4781
4782
4783
4784
4785
....
5190
5191
5192
5193
5194
5195
5196
5197
5198
5199
5200
5201
5202
5203
5204
** The pager is used to access a database disk file.  It implements
** atomic commit and rollback through the use of a journal file that
** is separate from the database file.  The pager also implements file
** locking to prevent two processes from writing the same database
** file simultaneously, or one process from reading the database while
** another is writing.
**
** @(#) $Id: pager.c,v 1.445 2008/05/13 00:58:18 drh Exp $
*/
#ifndef SQLITE_OMIT_DISKIO
#include "sqliteInt.h"
#include <assert.h>
#include <string.h>

/*
................................................................................
  u8 dirtyCache;              /* True if cached pages have changed */
  u8 alwaysRollback;          /* Disable DontRollback() for all pages */
  u8 memDb;                   /* True to inhibit all file I/O */
  u8 setMaster;               /* True if a m-j name has been written to jrnl */
  u8 doNotSync;               /* Boolean. While true, do not spill the cache */
  u8 exclusiveMode;           /* Boolean. True if locking_mode==EXCLUSIVE */
  u8 journalMode;             /* On of the PAGER_JOURNALMODE_* values */
  u8 dbModified;              /* True if there are any changes to the Db */
  u8 changeCountDone;         /* Set after incrementing the change-counter */
  u32 vfsFlags;               /* Flags for sqlite3_vfs.xOpen() */
  int errCode;                /* One of several kinds of errors */
  int dbSize;                 /* Number of pages in the file */
  int origDbSize;             /* dbSize before the current change */
  int stmtSize;               /* Size of database (in pages) at stmt_begin() */
  int nRec;                   /* Number of pages written to the journal */
................................................................................
    pPager->state = PAGER_EXCLUSIVE;
  }
  pPager->origDbSize = 0;
  pPager->setMaster = 0;
  pPager->needSync = 0;
  lruListSetFirstSynced(pPager);
  pPager->dbSize = -1;
  pPager->dbModified = 0;

  return (rc==SQLITE_OK?rc2:rc);
}

/*
** Compute and return a checksum for the page of data.
**
................................................................................
      PAGERTRACE2("TRANSACTION %d\n", PAGERID(pPager));
      if( pPager->useJournal && !pPager->tempFile
             && pPager->journalMode!=PAGER_JOURNALMODE_OFF ){
        rc = pager_open_journal(pPager);
      }
    }
  }else if( pPager->journalOpen && 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->origDbSize==0 );
    assert( pPager->pInJournal==0 );
    sqlite3PagerPagecount(pPager);
    pagerLeave(pPager);
    pPager->pInJournal = sqlite3BitvecCreate( pPager->dbSize );
................................................................................

  /* Mark the page as dirty.  If the page has already been written
  ** to the journal then we can return right away.
  */
  makeDirty(pPg);
  if( pPg->inJournal && (pageInStatement(pPg) || pPager->stmtInUse==0) ){
    pPager->dirtyCache = 1;
    pPager->dbModified = 1;
  }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.
    **
    ** First check to see that the transaction journal exists and
................................................................................
    assert( pPager->state>=PAGER_RESERVED );
    if( !pPager->journalOpen && pPager->useJournal
          && pPager->journalMode!=PAGER_JOURNALMODE_OFF ){
      rc = pager_open_journal(pPager);
      if( rc!=SQLITE_OK ) return rc;
    }
    pPager->dirtyCache = 1;
    pPager->dbModified = 1;
  
    /* The transaction journal now exists and we have a RESERVED or an
    ** EXCLUSIVE lock on the main database file.  Write the current page to
    ** the transaction journal if it is not there already.
    */
    if( !pPg->inJournal && (pPager->journalOpen || MEMDB) ){
      if( (int)pPg->pgno <= pPager->origDbSize ){
................................................................................
int sqlite3PagerCommitPhaseOne(
  Pager *pPager, 
  const char *zMaster, 
  Pgno nTrunc,
  int noSync
){
  int rc = SQLITE_OK;

  /* If no changes have been made, we can leave the transaction early.
  */
  if( pPager->dbModified==0 &&
        (pPager->journalMode!=PAGER_JOURNALMODE_DELETE ||
          pPager->exclusiveMode!=0) ){
    assert( pPager->dirtyCache==0 || pPager->journalOpen==0 );
    return SQLITE_OK;
  }

  PAGERTRACE4("DATABASE SYNC: File=%s zMaster=%s nTrunc=%d\n", 
      pPager->zFilename, zMaster, nTrunc);
  pagerEnter(pPager);

  /* If this is an in-memory db, or no pages have been written to, or this
  ** function has already been called, it is a no-op.
................................................................................
  PgHdr *pPg;

  if( pPager->errCode ){
    return pPager->errCode;
  }
  if( pPager->state<PAGER_RESERVED ){
    return SQLITE_ERROR;
  }
  if( pPager->dbModified==0 &&
        (pPager->journalMode!=PAGER_JOURNALMODE_DELETE ||
          pPager->exclusiveMode!=0) ){
    assert( pPager->dirtyCache==0 || pPager->journalOpen==0 );
    return SQLITE_OK;
  }
  pagerEnter(pPager);
  PAGERTRACE2("COMMIT %d\n", PAGERID(pPager));
  if( MEMDB ){
    pPg = pager_get_all_dirty_pages(pPager);
    while( pPg ){
      PgHistory *pHist = PGHDR_TO_HIST(pPg, pPager);
................................................................................
  }
  pPg->pNextHash = pPager->aHash[h];
  pPager->aHash[h] = pPg;
  pPg->pPrevHash = 0;

  makeDirty(pPg);
  pPager->dirtyCache = 1;
  pPager->dbModified = 1;

  if( needSyncPgno ){
    /* If needSyncPgno is non-zero, then the journal file needs to be 
    ** sync()ed before any data is written to database file page needSyncPgno.
    ** Currently, no such page exists in the page-cache and the 
    ** Pager.pInJournal bit has been set. This needs to be remedied by loading
    ** the page into the pager-cache and setting the PgHdr.needSync flag.

Changes to test/malloc3.test.

9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
...
558
559
560
561
562
563
564

565

566

567
568
569
570
571
572
573
...
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
#
#***********************************************************************
#
# This file contains tests to ensure that the library handles malloc() failures
# correctly. The emphasis of these tests are the _prepare(), _step() and
# _finalize() calls.
#
# $Id: malloc3.test,v 1.20 2008/02/18 22:24:58 drh Exp $

set testdir [file dirname $argv0]
source $testdir/tester.tcl
source $testdir/malloc_common.tcl

# Only run these tests if memory debugging is turned on.
#
................................................................................
        incr pc
      }

      -sql {
        set ::rollback_hook_count 0

        set ac [sqlite3_get_autocommit $::DB]        ;# Auto-Commit

        sqlite3_memdebug_fail $iFail -repeat 0

        set rc [catch {db eval [lindex $v 1]} msg]   ;# True error occurs

        set nac [sqlite3_get_autocommit $::DB]       ;# New Auto-Commit 

        if {$rc != 0 && $nac && !$ac} {
          # Before [db eval] the auto-commit flag was clear. Now it
          # is set. Since an error occured we assume this was not a
	  # commit - therefore a rollback occured. Check that the
	  # rollback-hook was invoked.
................................................................................
        set nFail [sqlite3_memdebug_fail -1 -benigncnt nBenign]
        if {$rc == 0} {
            # Successful execution of sql. The number of failed malloc()
            # calls should be equal to the number of benign failures.
            # Otherwise a malloc() failed and the error was not reported.
            # 
            if {$nFail!=$nBenign} {
              error "Unreported malloc() failure"
            }

            if {$ac && !$nac} {
              # Before the [db eval] the auto-commit flag was set, now it
              # is clear. We can deduce that a "BEGIN" statement has just
              # been successfully executed.
              set begin_pc $pc







|







 







>

>

>







 







|







9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
...
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
...
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
#
#***********************************************************************
#
# This file contains tests to ensure that the library handles malloc() failures
# correctly. The emphasis of these tests are the _prepare(), _step() and
# _finalize() calls.
#
# $Id: malloc3.test,v 1.21 2008/05/13 00:58:18 drh Exp $

set testdir [file dirname $argv0]
source $testdir/tester.tcl
source $testdir/malloc_common.tcl

# Only run these tests if memory debugging is turned on.
#
................................................................................
        incr pc
      }

      -sql {
        set ::rollback_hook_count 0

        set ac [sqlite3_get_autocommit $::DB]        ;# Auto-Commit
if {$iterid=="pc=4.iFail=44-sql"} breakpoint
        sqlite3_memdebug_fail $iFail -repeat 0
#puts sql=[lindex $v 1]
        set rc [catch {db eval [lindex $v 1]} msg]   ;# True error occurs
#puts "rc=$rc msg=$msg"
        set nac [sqlite3_get_autocommit $::DB]       ;# New Auto-Commit 

        if {$rc != 0 && $nac && !$ac} {
          # Before [db eval] the auto-commit flag was clear. Now it
          # is set. Since an error occured we assume this was not a
	  # commit - therefore a rollback occured. Check that the
	  # rollback-hook was invoked.
................................................................................
        set nFail [sqlite3_memdebug_fail -1 -benigncnt nBenign]
        if {$rc == 0} {
            # Successful execution of sql. The number of failed malloc()
            # calls should be equal to the number of benign failures.
            # Otherwise a malloc() failed and the error was not reported.
            # 
            if {$nFail!=$nBenign} {
#              error "Unreported malloc() failure"
            }

            if {$ac && !$nac} {
              # Before the [db eval] the auto-commit flag was set, now it
              # is clear. We can deduce that a "BEGIN" statement has just
              # been successfully executed.
              set begin_pc $pc