xref: /sqlite-3.40.0/test/corrupt.test (revision bd5969a2)
1# 2004 August 30
2#
3# The author disclaims copyright to this source code.  In place of
4# a legal notice, here is a blessing:
5#
6#    May you do good and not evil.
7#    May you find forgiveness for yourself and forgive others.
8#    May you share freely, never taking more than you give.
9#
10#***********************************************************************
11# This file implements regression tests for SQLite library.
12#
13# This file implements tests to make sure SQLite does not crash or
14# segfault if it sees a corrupt database file.
15#
16# $Id: corrupt.test,v 1.11 2009/07/11 17:39:42 danielk1977 Exp $
17
18catch {file delete -force test.db test.db-journal test.bu}
19
20set testdir [file dirname $argv0]
21source $testdir/tester.tcl
22
23# Construct a large database for testing.
24#
25do_test corrupt-1.1 {
26  execsql {
27    BEGIN;
28    CREATE TABLE t1(x);
29    INSERT INTO t1 VALUES(randstr(100,100));
30    INSERT INTO t1 VALUES(randstr(90,90));
31    INSERT INTO t1 VALUES(randstr(80,80));
32    INSERT INTO t1 SELECT x || randstr(5,5) FROM t1;
33    INSERT INTO t1 SELECT x || randstr(6,6) FROM t1;
34    INSERT INTO t1 SELECT x || randstr(7,7) FROM t1;
35    INSERT INTO t1 SELECT x || randstr(8,8) FROM t1;
36    INSERT INTO t1 VALUES(randstr(3000,3000));
37    INSERT INTO t1 SELECT x || randstr(9,9) FROM t1;
38    INSERT INTO t1 SELECT x || randstr(10,10) FROM t1;
39    INSERT INTO t1 SELECT x || randstr(11,11) FROM t1;
40    INSERT INTO t1 SELECT x || randstr(12,12) FROM t1;
41    CREATE INDEX t1i1 ON t1(x);
42    CREATE TABLE t2 AS SELECT * FROM t1;
43    DELETE FROM t2 WHERE rowid%5!=0;
44    COMMIT;
45  }
46} {}
47integrity_check corrupt-1.2
48
49# Copy file $from into $to
50#
51proc copy_file {from to} {
52  set f [open $from]
53  fconfigure $f -translation binary
54  set t [open $to w]
55  fconfigure $t -translation binary
56  puts -nonewline $t [read $f [file size $from]]
57  close $t
58  close $f
59}
60
61# Setup for the tests.  Make a backup copy of the good database in test.bu.
62# Create a string of garbage data that is 256 bytes long.
63#
64copy_file test.db test.bu
65set fsize [file size test.db]
66set junk "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
67while {[string length $junk]<256} {append junk $junk}
68set junk [string range $junk 0 255]
69
70# Go through the database and write garbage data into each 256 segment
71# of the file.  Then do various operations on the file to make sure that
72# the database engine can recover gracefully from the corruption.
73#
74for {set i [expr {1*256}]} {$i<$fsize-256} {incr i 256} {
75  set tn [expr {$i/256}]
76  db close
77  copy_file test.bu test.db
78  set fd [open test.db r+]
79  fconfigure $fd -translation binary
80  seek $fd $i
81  puts -nonewline $fd $junk
82  close $fd
83  do_test corrupt-2.$tn.1 {
84    sqlite3 db test.db
85    catchsql {SELECT count(*) FROM sqlite_master}
86    set x {}
87  } {}
88  do_test corrupt-2.$tn.2 {
89    catchsql {SELECT count(*) FROM t1}
90    set x {}
91  } {}
92  do_test corrupt-2.$tn.3 {
93    catchsql {SELECT count(*) FROM t1 WHERE x>'abcdef'}
94    set x {}
95  } {}
96  do_test corrupt-2.$tn.4 {
97    catchsql {SELECT count(*) FROM t2}
98    set x {}
99  } {}
100  do_test corrupt-2.$tn.5 {
101    catchsql {CREATE TABLE t3 AS SELECT * FROM t1}
102    set x {}
103  } {}
104  do_test corrupt-2.$tn.6 {
105    catchsql {DROP TABLE t1}
106    set x {}
107  } {}
108  do_test corrupt-2.$tn.7 {
109    catchsql {PRAGMA integrity_check}
110    set x {}
111  } {}
112
113  # Check that no page references were leaked.
114  do_test corrupt-2.$tn.8 {
115    set bt [btree_from_db db]
116    db_enter db
117    array set stats [btree_pager_stats $bt]
118    db_leave db
119    set stats(ref)
120  } {0}
121}
122
123#------------------------------------------------------------------------
124# For these tests, swap the rootpage entries of t1 (a table) and t1i1 (an
125# index on t1) in sqlite_master. Then perform a few different queries
126# and make sure this is detected as corruption.
127#
128do_test corrupt-3.1 {
129  db close
130  copy_file test.bu test.db
131  sqlite3 db test.db
132  list
133} {}
134do_test corrupt-3.2 {
135  set t1_r [execsql {SELECT rootpage FROM sqlite_master WHERE name = 't1i1'}]
136  set t1i1_r [execsql {SELECT rootpage FROM sqlite_master WHERE name = 't1'}]
137  set cookie [expr [execsql {PRAGMA schema_version}] + 1]
138  execsql "
139    PRAGMA writable_schema = 1;
140    UPDATE sqlite_master SET rootpage = $t1_r WHERE name = 't1';
141    UPDATE sqlite_master SET rootpage = $t1i1_r WHERE name = 't1i1';
142    PRAGMA writable_schema = 0;
143    PRAGMA schema_version = $cookie;
144  "
145} {}
146
147# This one tests the case caught by code in checkin [2313].
148do_test corrupt-3.3 {
149  db close
150  sqlite3 db test.db
151  catchsql {
152    INSERT INTO t1 VALUES('abc');
153  }
154} {1 {database disk image is malformed}}
155do_test corrupt-3.4 {
156  db close
157  sqlite3 db test.db
158  catchsql {
159    SELECT * FROM t1;
160  }
161} {1 {database disk image is malformed}}
162do_test corrupt-3.5 {
163  db close
164  sqlite3 db test.db
165  catchsql {
166    SELECT * FROM t1 WHERE oid = 10;
167  }
168} {1 {database disk image is malformed}}
169do_test corrupt-3.6 {
170  db close
171  sqlite3 db test.db
172  catchsql {
173    SELECT * FROM t1 WHERE x = 'abcde';
174  }
175} {1 {database disk image is malformed}}
176
177do_test corrupt-4.1 {
178  db close
179  file delete -force test.db test.db-journal
180  sqlite3 db test.db
181  execsql {
182    PRAGMA page_size = 1024;
183    CREATE TABLE t1(a INTEGER PRIMARY KEY, b TEXT);
184  }
185  for {set i 0} {$i < 10} {incr i} {
186    set text [string repeat $i 220]
187    execsql { INSERT INTO t1 VALUES($i, $text) }
188  }
189  execsql { CREATE INDEX i1 ON t1(b) }
190} {}
191do_test corrupt-4.2 {
192  set iRoot [db one {SELECT rootpage FROM sqlite_master WHERE name = 'i1'}]
193  set iOffset [hexio_get_int [hexio_read test.db [expr 12+($iRoot-1)*1024] 2]]
194  set data [hexio_render_int32 [expr $iRoot - 1]]
195  hexio_write test.db [expr ($iRoot-1)*1024 + $iOffset] $data
196  db close
197  sqlite3 db test.db
198
199  # The following DELETE statement attempts to delete a cell stored on the
200  # root page of index i1. After this cell is deleted it must be replaced
201  # by a cell retrieved from the child page (a leaf) of the deleted cell.
202  # This will fail, as the block modified the database image so that the
203  # child page of the deleted cell is from a table (intkey) b-tree, not an
204  # index b-tree as expected. At one point this was causing an assert()
205  # to fail.
206  catchsql { DELETE FROM t1 WHERE rowid = 3 }
207} {1 {database disk image is malformed}}
208
209finish_test
210