/ Check-in [bb6a75f4bb]
Login

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

Overview
Comment:Merge the fix for the journal_mode=PERSIST error recovery delay bug.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | sessions
Files: files | file ages | folders
SHA1: bb6a75f4bbcaaabc3ad47992c7c87868c70e8daf
User & Date: drh 2014-03-07 14:36:50
Context
2014-03-11
01:48
Version 3.8.4 for sessions (plus two minor fixes). check-in: 917c410808 user: drh tags: sessions
2014-03-07
14:36
Merge the fix for the journal_mode=PERSIST error recovery delay bug. check-in: bb6a75f4bb user: drh tags: sessions
03:31
Revise change from the previous check-in to clarify the situation when handling open journal files, regardless of journal mode. check-in: 1c318ef3b7 user: mistachkin tags: trunk
2014-03-06
15:01
Fix a harmless compiler warning in the sessions test harness. check-in: d389e20ab0 user: drh tags: sessions
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to src/pager.c.

4885
4886
4887
4888
4889
4890
4891
4892

4893
4894
4895
4896
4897
4898
4899
4900
4901
4902
4903
4904
4905
4906
4907
    ** in fact there is none.  This results in a false-positive which will
    ** be dealt with by the playback routine.  Ticket #3883.
    */
    rc = sqlite3OsCheckReservedLock(pPager->fd, &locked);
    if( rc==SQLITE_OK && !locked ){
      Pgno nPage;                 /* Number of pages in database file */

      /* Check the size of the database file. If it consists of 0 pages,

      ** then delete the journal file. See the header comment above for 
      ** the reasoning here.  Delete the obsolete journal file under
      ** a RESERVED lock to avoid race conditions and to avoid violating
      ** [H33020].
      */
      rc = pagerPagecount(pPager, &nPage);
      if( rc==SQLITE_OK ){
        if( nPage==0 ){
          sqlite3BeginBenignMalloc();
          if( pagerLockDb(pPager, RESERVED_LOCK)==SQLITE_OK ){
            sqlite3OsDelete(pVfs, pPager->zJournal, 0);
            if( !pPager->exclusiveMode ) pagerUnlockDb(pPager, SHARED_LOCK);
          }
          sqlite3EndBenignMalloc();
        }else{







|
>
|
|
|
<



|







4885
4886
4887
4888
4889
4890
4891
4892
4893
4894
4895
4896

4897
4898
4899
4900
4901
4902
4903
4904
4905
4906
4907
    ** in fact there is none.  This results in a false-positive which will
    ** be dealt with by the playback routine.  Ticket #3883.
    */
    rc = sqlite3OsCheckReservedLock(pPager->fd, &locked);
    if( rc==SQLITE_OK && !locked ){
      Pgno nPage;                 /* Number of pages in database file */

      /* Check the size of the database file. If it consists of 0 pages
      ** and the journal is not being persisted, then delete the journal
      ** file.  See the header comment above for the reasoning here.
      ** Delete the obsolete journal file under a RESERVED lock to avoid
      ** race conditions and to avoid violating [H33020].

      */
      rc = pagerPagecount(pPager, &nPage);
      if( rc==SQLITE_OK ){
        if( nPage==0 && !jrnlOpen ){
          sqlite3BeginBenignMalloc();
          if( pagerLockDb(pPager, RESERVED_LOCK)==SQLITE_OK ){
            sqlite3OsDelete(pVfs, pPager->zJournal, 0);
            if( !pPager->exclusiveMode ) pagerUnlockDb(pPager, SHARED_LOCK);
          }
          sqlite3EndBenignMalloc();
        }else{

Changes to test/corruptH.test.

15
16
17
18
19
20
21












22
23
24
25
26
27
28
..
48
49
50
51
52
53
54

55
56
57
58
59
60
61
..
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
set testprefix corruptH

# Do not use a codec for tests in this file, as the database file is
# manipulated directly using tcl scripts (using the [hexio_write] command).
#
do_not_use_codec
database_may_be_corrupt













# Initialize the database.
#
do_execsql_test 1.1 {
  PRAGMA page_size=1024;

  CREATE TABLE t1(a INTEGER PRIMARY KEY, b);
................................................................................
  }
  db close
  hexio_write test.db [expr {($r(t2)-1)*1024 + 11}] [format %.2X $r(t1)]
  sqlite3 db test.db
} {}

do_test 1.3 {

  db eval { PRAGMA secure_delete=1 }
  list [catch {
    db eval { SELECT * FROM t1 WHERE a IN (1, 2) } {
      db eval { DELETE FROM t2 }
    }
  } msg] $msg
} {1 {database disk image is malformed}}
................................................................................
  hexio_write test.db [expr {($fl-1) * 1024 + 8}] [format %.8X $r(t1)]
  hexio_write test.db 36 00000002

  sqlite3 db test.db
} {}


# The corruption migration caused by the test case below does not 
# cause corruption to be detected in mmap mode.
#
# The trick here is that the root page of the tree scanned by the outer 
# query is also currently on the free-list. So while the first seek on
# the table (for a==1) works, by the time the second is attempted The 
# "INSERT INTO t2..." statements have recycled the root page of t1 and
# used it as an index leaf. Normally, BtreeMovetoUnpacked() detects
# that the PgHdr object associated with said root page does not match
# the cursor (as it is now marked with PgHdr.intKey==0) and returns
# SQLITE_CORRUPT. 
#
# However, in mmap mode, the outer query and the inner queries use 
# different PgHdr objects (same data, but different PgHdr container 
# objects). And so the corruption is not detected. Instead, the second
# seek fails to find anything and only a single row is returned.
#
set res23 {1 {database disk image is malformed}}
if {[permutation]=="mmap"} {
  set res23 {0 one}
}
do_test 2.3 {
  list [catch {
  set res [list]
  db eval { SELECT * FROM t1 WHERE a IN (1, 2) } {
    db eval { 
      INSERT INTO t2 SELECT randomblob(100) FROM t2;
      INSERT INTO t2 SELECT randomblob(100) FROM t2;







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







 







>







 







<
<
<









<
<
<
<
<

<
<
<







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
..
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
...
106
107
108
109
110
111
112



113
114
115
116
117
118
119
120
121





122



123
124
125
126
127
128
129
set testprefix corruptH

# Do not use a codec for tests in this file, as the database file is
# manipulated directly using tcl scripts (using the [hexio_write] command).
#
do_not_use_codec
database_may_be_corrupt

# The corruption migrations tested by the code in this file are not detected
# mmap mode.
#
# The reason is that in mmap mode, the different queries may use different
# PgHdr objects for the same page (same data, but different PgHdr container 
# objects). And so the corruption is not detected. 
#
if {[permutation]=="mmap"} {
  finish_test
  return
}

# Initialize the database.
#
do_execsql_test 1.1 {
  PRAGMA page_size=1024;

  CREATE TABLE t1(a INTEGER PRIMARY KEY, b);
................................................................................
  }
  db close
  hexio_write test.db [expr {($r(t2)-1)*1024 + 11}] [format %.2X $r(t1)]
  sqlite3 db test.db
} {}

do_test 1.3 {
breakpoint
  db eval { PRAGMA secure_delete=1 }
  list [catch {
    db eval { SELECT * FROM t1 WHERE a IN (1, 2) } {
      db eval { DELETE FROM t2 }
    }
  } msg] $msg
} {1 {database disk image is malformed}}
................................................................................
  hexio_write test.db [expr {($fl-1) * 1024 + 8}] [format %.8X $r(t1)]
  hexio_write test.db 36 00000002

  sqlite3 db test.db
} {}





# The trick here is that the root page of the tree scanned by the outer 
# query is also currently on the free-list. So while the first seek on
# the table (for a==1) works, by the time the second is attempted The 
# "INSERT INTO t2..." statements have recycled the root page of t1 and
# used it as an index leaf. Normally, BtreeMovetoUnpacked() detects
# that the PgHdr object associated with said root page does not match
# the cursor (as it is now marked with PgHdr.intKey==0) and returns
# SQLITE_CORRUPT. 
#





set res23 {1 {database disk image is malformed}}



do_test 2.3 {
  list [catch {
  set res [list]
  db eval { SELECT * FROM t1 WHERE a IN (1, 2) } {
    db eval { 
      INSERT INTO t2 SELECT randomblob(100) FROM t2;
      INSERT INTO t2 SELECT randomblob(100) FROM t2;

Changes to test/uri.test.

234
235
236
237
238
239
240

241
242
243
244
245
246

247
248
249
250
251
252
253
#
ifcapable wal {
  testvfs tvfs1 
  tvfs1 filter {xOpen xDelete xAccess xFullPathname}
  tvfs1 script tvfs1_callback
  proc tvfs1_callback {method filename args} { 
    set ::T1([file tail $filename]) 1 

  }
  testvfs tvfs2 
  tvfs2 filter {xOpen xDelete xAccess xFullPathname}
  tvfs2 script tvfs2_callback
  proc tvfs2_callback {method filename args} { 
    set ::T2([file tail $filename]) 1 

  }
  
  catch {db close}
  eval forcedelete [glob test.db*]
  do_test 5.1.1 {
    sqlite3 db file:test.db1?vfs=tvfs1
    execsql {







>






>







234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
#
ifcapable wal {
  testvfs tvfs1 
  tvfs1 filter {xOpen xDelete xAccess xFullPathname}
  tvfs1 script tvfs1_callback
  proc tvfs1_callback {method filename args} { 
    set ::T1([file tail $filename]) 1 
    return SQLITE_OK
  }
  testvfs tvfs2 
  tvfs2 filter {xOpen xDelete xAccess xFullPathname}
  tvfs2 script tvfs2_callback
  proc tvfs2_callback {method filename args} { 
    set ::T2([file tail $filename]) 1 
    return SQLITE_OK
  }
  
  catch {db close}
  eval forcedelete [glob test.db*]
  do_test 5.1.1 {
    sqlite3 db file:test.db1?vfs=tvfs1
    execsql {