/ Check-in [6afe467d]
Login

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

Overview
Comment:Make sure the database file is correctly truncated after a ROLLBACK that occurs after a statement abort. (CVS 1893)
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1:6afe467d146828d67ad17af21604c2e691aa5dff
User & Date: drh 2004-08-18 19:09:44
Context
2004-08-19
13:29
Add the SQLITE_BUSY_RESERVED_LOCK compile-time option. (CVS 1894) check-in: 25fe7a42 user: drh tags: trunk
2004-08-18
19:09
Make sure the database file is correctly truncated after a ROLLBACK that occurs after a statement abort. (CVS 1893) check-in: 6afe467d user: drh tags: trunk
16:05
Better debug logging of the pager. (CVS 1892) check-in: 1cc0323f 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
....
1012
1013
1014
1015
1016
1017
1018








1019
1020
1021
1022
1023
1024
1025
....
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
....
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
....
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
....
2856
2857
2858
2859
2860
2861
2862
2863
2864

2865
2866
2867

2868
2869
2870
2871
2872
2873
2874
** 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.157 2004/08/18 16:05:19 drh Exp $
*/
#include "os.h"         /* Must be first to enable large file support */
#include "sqliteInt.h"
#include "pager.h"
#include <assert.h>
#include <string.h>

................................................................................
      }
    }
    pPg->needSync = 0;
    pPg->dirty = 0;
  }
  return rc;
}









/*
** Playback the journal and thus restore the database file to
** the state it was in before we started making changes.  
**
** The journal file format is as follows: 
**
................................................................................
    }

    /* If this is the first header read from the journal, truncate the
    ** database file back to it's original size.
    */
    if( pPager->journalOff==JOURNAL_HDR_SZ(pPager) ){
      assert( pPager->origDbSize==0 || pPager->origDbSize==mxPg );
      rc = sqlite3OsTruncate(&pPager->fd, pPager->pageSize*(off_t)mxPg);
      if( rc!=SQLITE_OK ){
        goto end_playback;
      }
      pPager->dbSize = mxPg;
    }

    /* rc = sqlite3OsSeek(&pPager->jfd, JOURNAL_HDR_SZ(pPager)); */
................................................................................
  if( !hdrOff ){
    hdrOff = szJ;
  }
  

  /* Truncate the database back to its original size.
  */
  rc = sqlite3OsTruncate(&pPager->fd, pPager->pageSize*(off_t)pPager->stmtSize);
  pPager->dbSize = pPager->stmtSize;

  /* Figure out how many records are in the statement journal.
  */
  assert( pPager->stmtInUse && pPager->journalOpen );
  sqlite3OsSeek(&pPager->stfd, 0);
  nRec = pPager->stmtNRec;
................................................................................
    memoryTruncate(pPager);
    return SQLITE_OK;
  }
  rc = syncJournal(pPager);
  if( rc!=SQLITE_OK ){
    return rc;
  }
  rc = sqlite3OsTruncate(&pPager->fd, pPager->pageSize*(off_t)nPage);
  if( rc==SQLITE_OK ){
    pPager->dbSize = nPage;
  }
  return rc;
}

/*
................................................................................
  if( pPager->errMask!=0 && pPager->errMask!=PAGER_ERR_FULL ){
    if( pPager->state>=PAGER_EXCLUSIVE ){
      pager_playback(pPager);
    }
    return pager_errcode(pPager);
  }
  if( pPager->state==PAGER_RESERVED ){
    int rc2;
    rc = pager_reload_cache(pPager);

    rc2 = pager_unwritelock(pPager);
    if( rc==SQLITE_OK ){
      rc = rc2;

    }
  }else{
    rc = pager_playback(pPager);
  }
  if( rc!=SQLITE_OK ){
    rc = SQLITE_CORRUPT;
    pPager->errMask |= PAGER_ERR_CORRUPT;







|







 







>
>
>
>
>
>
>
>







 







|







 







|







 







|







 







|

>
|


>







14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
....
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
....
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
....
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
....
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
....
2864
2865
2866
2867
2868
2869
2870
2871
2872
2873
2874
2875
2876
2877
2878
2879
2880
2881
2882
2883
2884
** 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.158 2004/08/18 19:09:44 drh Exp $
*/
#include "os.h"         /* Must be first to enable large file support */
#include "sqliteInt.h"
#include "pager.h"
#include <assert.h>
#include <string.h>

................................................................................
      }
    }
    pPg->needSync = 0;
    pPg->dirty = 0;
  }
  return rc;
}

/*
** Truncate the main file of the given pager to the number of pages
** indicated.
*/
static int pager_truncate(Pager *pPager, int nPage){
  return sqlite3OsTruncate(&pPager->fd, pPager->pageSize*(off_t)nPage);
}

/*
** Playback the journal and thus restore the database file to
** the state it was in before we started making changes.  
**
** The journal file format is as follows: 
**
................................................................................
    }

    /* If this is the first header read from the journal, truncate the
    ** database file back to it's original size.
    */
    if( pPager->journalOff==JOURNAL_HDR_SZ(pPager) ){
      assert( pPager->origDbSize==0 || pPager->origDbSize==mxPg );
      rc = pager_truncate(pPager, mxPg);
      if( rc!=SQLITE_OK ){
        goto end_playback;
      }
      pPager->dbSize = mxPg;
    }

    /* rc = sqlite3OsSeek(&pPager->jfd, JOURNAL_HDR_SZ(pPager)); */
................................................................................
  if( !hdrOff ){
    hdrOff = szJ;
  }
  

  /* Truncate the database back to its original size.
  */
  rc = pager_truncate(pPager, pPager->stmtSize);
  pPager->dbSize = pPager->stmtSize;

  /* Figure out how many records are in the statement journal.
  */
  assert( pPager->stmtInUse && pPager->journalOpen );
  sqlite3OsSeek(&pPager->stfd, 0);
  nRec = pPager->stmtNRec;
................................................................................
    memoryTruncate(pPager);
    return SQLITE_OK;
  }
  rc = syncJournal(pPager);
  if( rc!=SQLITE_OK ){
    return rc;
  }
  rc = pager_truncate(pPager, nPage);
  if( rc==SQLITE_OK ){
    pPager->dbSize = nPage;
  }
  return rc;
}

/*
................................................................................
  if( pPager->errMask!=0 && pPager->errMask!=PAGER_ERR_FULL ){
    if( pPager->state>=PAGER_EXCLUSIVE ){
      pager_playback(pPager);
    }
    return pager_errcode(pPager);
  }
  if( pPager->state==PAGER_RESERVED ){
    int rc2, rc3;
    rc = pager_reload_cache(pPager);
    rc2 = pager_truncate(pPager, pPager->origDbSize);
    rc3 = pager_unwritelock(pPager);
    if( rc==SQLITE_OK ){
      rc = rc2;
      if( rc3 ) rc = rc3;
    }
  }else{
    rc = pager_playback(pPager);
  }
  if( rc!=SQLITE_OK ){
    rc = SQLITE_CORRUPT;
    pPager->errMask |= PAGER_ERR_CORRUPT;

Added test/pager3.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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
# 2001 September 15
#
# 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.  The
# focus of this script is page cache subsystem.
#
# $Id: pager3.test,v 1.1 2004/08/18 19:09:44 drh Exp $


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

# This test makes sure the database file is truncated back to the correct
# length on a rollback.
#
# After some preliminary setup, a transaction is start at NOTE (1).
# The create table on the following line allocates an additional page
# at the end of the database file.  But that page is not written because
# the database still has a RESERVED lock, not an EXCLUSIVE lock.  The
# new page is held in memory and the size of the file is unchanged.
# The insert at NOTE (2) begins adding additional pages.  Then it hits
# a constraint error and aborts.  The abort causes sqlite3OsTruncate()
# to be called to restore the file to the same length as it was after
# the create table.  But the create table results had not yet been
# written so the file is actually lengthened by this truncate.  Finally,
# the rollback at NOTE (3) is called to undo all the changes since the
# begin.  This rollback should truncate the database again.
# 
# This test was added because the second truncate at NOTE (3) was not
# occurring on early versions of SQLite 3.0.
#
do_test pager3-1.1 {
  execsql {
    create table t1(a unique, b);
    insert into t1 values(1, 'abcdefghijklmnopqrstuvwxyz');
    insert into t1 values(2, 'abcdefghijklmnopqrstuvwxyz');
    update t1 set b=b||a||b;
    update t1 set b=b||a||b;
    update t1 set b=b||a||b;
    update t1 set b=b||a||b;
    update t1 set b=b||a||b;
    update t1 set b=b||a||b;
    create temp table t2 as select * from t1;
    begin;                  ------- NOTE (1)
    create table t3(x);
  }
  catchsql {
    insert into t1 select 4-a, b from t2;  ----- NOTE (2)
  }
  execsql {
    rollback;  ------- NOTE (3)
  }
  db close
  sqlite3 db test.db
  execsql {
    pragma integrity_check;
  }
} ok


finish_test