SQLite

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
Timelines: family | ancestors | descendants | both | sessions
Files: files | file ages | folders
SHA1: bb6a75f4bbcaaabc3ad47992c7c87868c70e8daf
User & Date: drh 2014-03-07 14:36:50.554
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
Unified Diff 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
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);







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







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
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);
48
49
50
51
52
53
54

55
56
57
58
59
60
61
  }
  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}}







>







60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
  }
  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}}
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
  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;







<
<
<









<
<
<
<
<

<
<
<







106
107
108
109
110
111
112



113
114
115
116
117
118
119
120
121





122



123
124
125
126
127
128
129
  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 {