SQLite

Check-in [b908048b6c]
Login

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

Overview
Comment:Add tests to ensure that an sqlite3_snapshot_open() client cannot be tricked into reading a corrupt snapshot even if another process fails mid-checkpoint.
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | snapshot-get
Files: files | file ages | folders
SHA1: b908048b6cfa1ee2fe1f7a17bae475ddd9d0376c
User & Date: dan 2015-12-10 18:06:21.050
Context
2015-12-10
19:11
Return SQLITE_BUSY (not SQLITE_BUSY_SNAPSHOT) if sqlite3_snapshot_open() fails to obtain the shared checkpointer lock. (check-in: 5343060bcc user: dan tags: snapshot-get)
18:06
Add tests to ensure that an sqlite3_snapshot_open() client cannot be tricked into reading a corrupt snapshot even if another process fails mid-checkpoint. (check-in: b908048b6c user: dan tags: snapshot-get)
15:45
Have sqlite3_snapshot_open() avoid a race condition by taking a shared CHECKPOINTER lock while checking pInfo->nBackfillAttempted. (check-in: 8084eae0bc user: dan tags: snapshot-get)
Changes
Unified Diff Ignore Whitespace Patch
Changes to test/snapshot.test.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 2015 December 7
#
# 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 file is testing the SELECT statement.
#
# $Id: select1.test,v 1.70 2009/05/28 01:00:56 drh Exp $

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

#-------------------------------------------------------------------------
# Check some error conditions in snapshot_get(). It is an error if:










|
|

<







1
2
3
4
5
6
7
8
9
10
11
12
13

14
15
16
17
18
19
20
# 2015 December 7
#
# 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 file is the sqlite3_snapshot_xxx() APIs.
#


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

#-------------------------------------------------------------------------
# Check some error conditions in snapshot_get(). It is an error if:
Added test/snapshot_fault.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
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
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
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
# 2015 December 10
#
# 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 file is the sqlite3_snapshot_xxx() APIs.
#

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

#-------------------------------------------------------------------------
# Check that an sqlite3_snapshot_open() client cannot be tricked into
# reading a corrupt snapshot even if a second client fails while 
# checkpointing the db.
#
do_faultsim_test 1.0 -prep {
  faultsim_delete_and_reopen
  sqlite3 db2 test.db
  db2 eval { 
    CREATE TABLE t1(a, b UNIQUE, c UNIQUE);
    INSERT INTO t1 VALUES(1, randomblob(500), randomblob(500));
    INSERT INTO t1 VALUES(2, randomblob(500), randomblob(500));
    PRAGMA journal_mode = wal;
    INSERT INTO t1 VALUES(3, randomblob(500), randomblob(500));
    BEGIN;
      SELECT a FROM t1;
  }
  set ::snapshot [sqlite3_snapshot_get db2 main] 
  db2 eval COMMIT
  db2 eval {
    UPDATE t1 SET b=randomblob(501), c=randomblob(501) WHERE a=1;
    INSERT INTO t1 VALUES(4, randomblob(500), randomblob(500));
    INSERT INTO t1 VALUES(5, randomblob(500), randomblob(500));
    INSERT INTO t1 VALUES(6, randomblob(500), randomblob(500));
  }
} -body {
  db eval { PRAGMA wal_checkpoint }
} -test {
  db2 eval BEGIN
  if {[catch { sqlite3_snapshot_open db2 main $::snapshot } msg]} {
    if {$msg != "SQLITE_BUSY_SNAPSHOT" && $msg != "SQLITE_BUSY"} {
      error "error is $msg" 
    }
  } else {
    set res [db2 eval { 
      SELECT a FROM t1;
      PRAGMA integrity_check;
    }]
    if {$res != "1 2 3 ok"} { error "res is $res" }
  }

  sqlite3_snapshot_free $::snapshot
}

#-------------------------------------------------------------------------
# This test is similar to the previous one. Except, after the 
# "PRAGMA wal_checkpoint" command fails the db is closed and reopened
# so as to require wal file recovery. It should not be possible to open
# a snapshot that is part of the body of a recovered wal file.
#
do_faultsim_test 2.0 -prep {
  faultsim_delete_and_reopen
  db eval { 
    CREATE TABLE t1(a, b UNIQUE, c UNIQUE);
    INSERT INTO t1 VALUES(1, randomblob(500), randomblob(500));
    INSERT INTO t1 VALUES(2, randomblob(500), randomblob(500));
    PRAGMA journal_mode = wal;
    INSERT INTO t1 VALUES(3, randomblob(500), randomblob(500));
    BEGIN;
      SELECT a FROM t1;
  }
  set ::snapshot [sqlite3_snapshot_get db main] 
  db eval COMMIT

  db eval {
    UPDATE t1 SET b=randomblob(501), c=randomblob(501) WHERE a=1;
    INSERT INTO t1 VALUES(4, randomblob(500), randomblob(500));
    INSERT INTO t1 VALUES(5, randomblob(500), randomblob(500));
    INSERT INTO t1 VALUES(6, randomblob(500), randomblob(500));
  }
} -body {
  db eval { PRAGMA wal_checkpoint }
} -test {

  db_save
  db close
  db_restore_and_reopen
  db eval { SELECT * FROM t1 }
  
  db eval BEGIN
  if {[catch { sqlite3_snapshot_open db main $::snapshot } msg]} {
    if {$msg != "SQLITE_BUSY_SNAPSHOT" && $msg != "SQLITE_BUSY"} {
      error "error is $msg" 
    }
  } else {
    # This branch should actually never be taken. But it was useful in
    # determining whether or not this test was actually working (by 
    # running a modified version of SQLite that allowed snapshots to be
    # opened following a recovery).
    error "TEST HAS FAILED"

    set res [db eval { 
      SELECT a FROM t1;
      PRAGMA integrity_check;
    }]
    if {$res != "1 2 3 ok"} { error "res is $res" }
  }

  sqlite3_snapshot_free $::snapshot
}

#-------------------------------------------------------------------------
# Test the handling of faults that occur within sqlite3_snapshot_open().
#
do_faultsim_test 3.0 -prep {
  faultsim_delete_and_reopen
  db eval { 
    CREATE TABLE t1(a, b UNIQUE, c UNIQUE);
    INSERT INTO t1 VALUES(1, randomblob(500), randomblob(500));
    INSERT INTO t1 VALUES(2, randomblob(500), randomblob(500));
    PRAGMA journal_mode = wal;
    INSERT INTO t1 VALUES(3, randomblob(500), randomblob(500));
    BEGIN;
      SELECT a FROM t1;
  }
  set ::snapshot [sqlite3_snapshot_get db main] 
  db eval COMMIT
  db eval {
    UPDATE t1 SET b=randomblob(501), c=randomblob(501) WHERE a=1;
    INSERT INTO t1 VALUES(4, randomblob(500), randomblob(500));
    INSERT INTO t1 VALUES(5, randomblob(500), randomblob(500));
    INSERT INTO t1 VALUES(6, randomblob(500), randomblob(500));
    BEGIN;
  }
} -body {
  if { [catch { sqlite3_snapshot_open db main $::snapshot } msg] } {
    error $msg
  }
} -test {
  faultsim_test_result {0 {}} {1 SQLITE_IOERR} \
                              {1 SQLITE_IOERR_NOMEM} {1 SQLITE_IOERR_READ}
  if {$testrc==0} {
    set res [db eval { 
      SELECT a FROM t1;
      PRAGMA integrity_check;
    }]
    if {$res != "1 2 3 ok"} { error "res is $res" }
  }

  sqlite3_snapshot_free $::snapshot
}



finish_test