/ Check-in [faded96f]
Login

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

Overview
Comment:On a ROLLBACK, if there page cache entries which are dirty but not in the rollback journal, make sure they get reinitialized in the btree layer. (CVS 5936)
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1:faded96f36229ee85039276db693391d0f10648c
User & Date: drh 2008-11-21 03:23:43
Context
2008-11-21
08:50
Add another test case to tkt35xx.test showing that a statement rollback can also trigger the problem. (CVS 5937) check-in: 74c08b8d user: danielk1977 tags: trunk
03:23
On a ROLLBACK, if there page cache entries which are dirty but not in the rollback journal, make sure they get reinitialized in the btree layer. (CVS 5936) check-in: faded96f user: drh tags: trunk
00:24
Fixes to the proxy locking so that os_unix.c compiles on linux with proxy locking omitted. (CVS 5935) check-in: 6f910b70 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
....
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
....
1169
1170
1171
1172
1173
1174
1175
1176


1177
1178
1179
1180
1181
1182
1183
....
1514
1515
1516
1517
1518
1519
1520














1521
1522
1523
1524
1525
1526
1527
....
3089
3090
3091
3092
3093
3094
3095
3096
3097
3098
3099
3100
3101
3102
3103
3104
3105
3106
3107
3108
3109
3110
3111
3112
3113
3114
3115
3116
3117
3118
3119
....
3158
3159
3160
3161
3162
3163
3164
3165
3166
3167
3168
3169
3170
3171
3172
....
4087
4088
4089
4090
4091
4092
4093
4094
4095
4096
4097
4098
4099
4100
4101
....
4122
4123
4124
4125
4126
4127
4128
4129
4130
4131
4132
4133
4134
4135
4136
** 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.506 2008/11/19 18:30:29 drh Exp $
*/
#ifndef SQLITE_OMIT_DISKIO
#include "sqliteInt.h"

/*
** Macros for troubleshooting.  Normally turned off
*/
................................................................................
  while( i>0 ){
    cksum += aData[i];
    i -= 200;
  }
  return cksum;
}

/* Forward declaration */
static void makeClean(PgHdr*);

/*
** Read a single page from the journal file opened on file descriptor
** jfd.  Playback this one page.
**
** The isMainJrnl flag is true if this is the main rollback journal and
** false for the statement journal.  The main rollback journal uses
** checksums - the statement journal does not.
................................................................................
    */
    void *pData;
    pData = pPg->pData;
    memcpy(pData, aData, pPager->pageSize);
    if( pPager->xReiniter ){
      pPager->xReiniter(pPg);
    }
    if( isMainJrnl ) makeClean(pPg);


#ifdef SQLITE_CHECK_PAGES
    pPg->pageHash = pager_pagehash(pPg);
#endif
    /* If this was page 1, then restore the value of Pager.dbFileVers.
    ** Do this before any decoding. */
    if( pgno==1 ){
      memcpy(&pPager->dbFileVers, &((u8*)pData)[24],sizeof(pPager->dbFileVers));
................................................................................
      }
    }
  }
  /*NOTREACHED*/
  assert( 0 );

end_playback:














  if( rc==SQLITE_OK ){
    zMaster = pPager->pTmpSpace;
    rc = readMasterJournal(pPager->jfd, zMaster, pPager->pVfs->mxPathname+1);
  }
  if( rc==SQLITE_OK ){
    rc = pager_end_transaction(pPager, zMaster[0]!='\0');
  }
................................................................................
      pPager->origDbSize = pPager->dbSize;
      rc = writeJournalHdr(pPager);
    }
  }
  assert( !pPager->journalOpen || pPager->journalOff>0 || rc!=SQLITE_OK );
  return rc;
}

/*
** Make a page dirty.  Set its dirty flag and add it to the dirty
** page list.
*/
static void makeDirty(PgHdr *pPg){
  sqlite3PcacheMakeDirty(pPg);
}

/*
** Make a page clean.  Clear its dirty bit and remove it from the
** dirty page list.
*/
static void makeClean(PgHdr *pPg){
  sqlite3PcacheMakeClean(pPg);
}


/*
** Mark a data page as writeable.  The page is written into the journal 
** if it is not there already.  This routine must be called before making
** changes to a page.
**
** The first time this routine is called, the pager creates a new
................................................................................
  if( rc ){
    return rc;
  }

  /* Mark the page as dirty.  If the page has already been written
  ** to the journal then we can return right away.
  */
  makeDirty(pPg);
  if( pageInJournal(pPg) && (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
................................................................................
  }

  sqlite3PcacheMove(pPg, pgno);
  if( pPgOld ){
    sqlite3PcacheDrop(pPgOld);
  }

  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 
................................................................................
        sqlite3BitvecClear(pPager->pInJournal, needSyncPgno);
      }
      return rc;
    }
    pPager->needSync = 1;
    assert( pPager->noSync==0 && !MEMDB );
    pPgHdr->flags |= PGHDR_NEED_SYNC;
    makeDirty(pPgHdr);
    sqlite3PagerUnref(pPgHdr);
  }

  return SQLITE_OK;
}
#endif








|







 







<
<
<







 







|
>
>







 







>
>
>
>
>
>
>
>
>
>
>
>
>
>







 







<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<







 







|







 







|







 







|







14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
....
1057
1058
1059
1060
1061
1062
1063



1064
1065
1066
1067
1068
1069
1070
....
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
....
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
....
3102
3103
3104
3105
3106
3107
3108

















3109
3110
3111
3112
3113
3114
3115
....
3154
3155
3156
3157
3158
3159
3160
3161
3162
3163
3164
3165
3166
3167
3168
....
4083
4084
4085
4086
4087
4088
4089
4090
4091
4092
4093
4094
4095
4096
4097
....
4118
4119
4120
4121
4122
4123
4124
4125
4126
4127
4128
4129
4130
4131
4132
** 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.507 2008/11/21 03:23:43 drh Exp $
*/
#ifndef SQLITE_OMIT_DISKIO
#include "sqliteInt.h"

/*
** Macros for troubleshooting.  Normally turned off
*/
................................................................................
  while( i>0 ){
    cksum += aData[i];
    i -= 200;
  }
  return cksum;
}




/*
** Read a single page from the journal file opened on file descriptor
** jfd.  Playback this one page.
**
** The isMainJrnl flag is true if this is the main rollback journal and
** false for the statement journal.  The main rollback journal uses
** checksums - the statement journal does not.
................................................................................
    */
    void *pData;
    pData = pPg->pData;
    memcpy(pData, aData, pPager->pageSize);
    if( pPager->xReiniter ){
      pPager->xReiniter(pPg);
    }
    if( isMainJrnl ){
      sqlite3PcacheMakeClean(pPg);
    }
#ifdef SQLITE_CHECK_PAGES
    pPg->pageHash = pager_pagehash(pPg);
#endif
    /* If this was page 1, then restore the value of Pager.dbFileVers.
    ** Do this before any decoding. */
    if( pgno==1 ){
      memcpy(&pPager->dbFileVers, &((u8*)pData)[24],sizeof(pPager->dbFileVers));
................................................................................
      }
    }
  }
  /*NOTREACHED*/
  assert( 0 );

end_playback:
  /* There may be pages in cache that are dirty but which were not in
  ** the rollback journal.  Such pages would have been taken out of the
  ** freelist (and hence marked as DontRollback).  All such pages must
  ** be reinitialized.
  */
  if( rc==SQLITE_OK && pPager->xReiniter ){
    PgHdr *pDirty;     /* List of page that are still dirty */
    pDirty = sqlite3PcacheDirtyList(pPager->pPCache);
    while( pDirty ){
      pPager->xReiniter(pDirty);
      sqlite3PcacheMakeClean(pDirty);
      pDirty = pDirty->pDirty;
    }
  }
  if( rc==SQLITE_OK ){
    zMaster = pPager->pTmpSpace;
    rc = readMasterJournal(pPager->jfd, zMaster, pPager->pVfs->mxPathname+1);
  }
  if( rc==SQLITE_OK ){
    rc = pager_end_transaction(pPager, zMaster[0]!='\0');
  }
................................................................................
      pPager->origDbSize = pPager->dbSize;
      rc = writeJournalHdr(pPager);
    }
  }
  assert( !pPager->journalOpen || pPager->journalOff>0 || rc!=SQLITE_OK );
  return rc;
}


















/*
** Mark a data page as writeable.  The page is written into the journal 
** if it is not there already.  This routine must be called before making
** changes to a page.
**
** The first time this routine is called, the pager creates a new
................................................................................
  if( rc ){
    return rc;
  }

  /* Mark the page as dirty.  If the page has already been written
  ** to the journal then we can return right away.
  */
  sqlite3PcacheMakeDirty(pPg);
  if( pageInJournal(pPg) && (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
................................................................................
  }

  sqlite3PcacheMove(pPg, pgno);
  if( pPgOld ){
    sqlite3PcacheDrop(pPgOld);
  }

  sqlite3PcacheMakeDirty(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 
................................................................................
        sqlite3BitvecClear(pPager->pInJournal, needSyncPgno);
      }
      return rc;
    }
    pPager->needSync = 1;
    assert( pPager->noSync==0 && !MEMDB );
    pPgHdr->flags |= PGHDR_NEED_SYNC;
    sqlite3PcacheMakeDirty(pPgHdr);
    sqlite3PagerUnref(pPgHdr);
  }

  return SQLITE_OK;
}
#endif

Added test/tkt35xx.test.















































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# 2008 November 20
#
# The author disclaims copyright to this source code.  In place of
# a legal notice, here is a blessing:
#
#    May you do good and not evil.
#    May you find forgiveness for yourself and forgive others.
#    May you share freely, never taking more than you give.
#
#***********************************************************************
# This file implements regression tests for SQLite library.
#
# When a transaction rolls back, make sure that dirty pages in the
# page cache which are not in the rollback journal are reinitialized
# in the btree layer.
#
# $Id: tkt35xx.test,v 1.1 2008/11/21 03:23:43 drh Exp $

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

do_test tkt35xx-1.1 {
  execsql {
    PRAGMA auto_vacuum = 0;
    CREATE TABLE t1(a,b,c);
    CREATE INDEX i1 ON t1(c);
    INSERT INTO t1 VALUES(0, 0, zeroblob(676));
    INSERT INTO t1 VALUES(1, 1, zeroblob(676));
    DELETE FROM t1;
    BEGIN;
    INSERT INTO t1 VALUES(0, 0, zeroblob(676));
    INSERT INTO t1 VALUES(1, 1, zeroblob(676));
    ROLLBACK;
    INSERT INTO t1 VALUES(0, 0, zeroblob(676));
  }
  execsql {
    INSERT INTO t1 VALUES(1, 1, zeroblob(676));
  }
} {}