xref: /sqlite-3.40.0/test/corrupt2.test (revision 68133509)
12812956bSdanielk1977# 2004 August 30
22812956bSdanielk1977#
32812956bSdanielk1977# The author disclaims copyright to this source code.  In place of
42812956bSdanielk1977# a legal notice, here is a blessing:
52812956bSdanielk1977#
62812956bSdanielk1977#    May you do good and not evil.
72812956bSdanielk1977#    May you find forgiveness for yourself and forgive others.
82812956bSdanielk1977#    May you share freely, never taking more than you give.
92812956bSdanielk1977#
102812956bSdanielk1977#***********************************************************************
112812956bSdanielk1977# This file implements regression tests for SQLite library.
122812956bSdanielk1977#
132812956bSdanielk1977# This file implements tests to make sure SQLite does not crash or
142812956bSdanielk1977# segfault if it sees a corrupt database file.
152812956bSdanielk1977#
16ef165cedSdanielk1977# $Id: corrupt2.test,v 1.20 2009/04/06 17:50:03 danielk1977 Exp $
172812956bSdanielk1977
182812956bSdanielk1977set testdir [file dirname $argv0]
192812956bSdanielk1977source $testdir/tester.tcl
20ad41f5edSdanset testprefix corrupt2
212812956bSdanielk1977
2268928b6cSdan# Do not use a codec for tests in this file, as the database file is
2368928b6cSdan# manipulated directly using tcl scripts (using the [hexio_write] command).
2468928b6cSdan#
2568928b6cSdando_not_use_codec
2668928b6cSdan
2709fe6143Sdrh# These tests deal with corrupt database files
2809fe6143Sdrh#
2909fe6143Sdrhdatabase_may_be_corrupt
3009fe6143Sdrh
31cb354603Sdanset presql ""
32cb354603Sdancatch { set presql "$::G(perm:presql);" }
33cb354603Sdanunset -nocomplain ::G(perm:presql)
34cb354603Sdan
352812956bSdanielk1977# The following tests - corrupt2-1.* - create some databases corrupted in
362812956bSdanielk1977# specific ways and ensure that SQLite detects them as corrupt.
372812956bSdanielk1977#
382812956bSdanielk1977do_test corrupt2-1.1 {
392812956bSdanielk1977  execsql {
40e6a64648Sdrh    PRAGMA auto_vacuum=0;
41e6a64648Sdrh    PRAGMA page_size=1024;
422812956bSdanielk1977    CREATE TABLE abc(a, b, c);
432812956bSdanielk1977  }
442812956bSdanielk1977} {}
452812956bSdanielk1977
462812956bSdanielk1977do_test corrupt2-1.2 {
472812956bSdanielk1977
482812956bSdanielk1977  # Corrupt the 16 byte magic string at the start of the file
49fda06befSmistachkin  forcedelete corrupt.db
50fda06befSmistachkin  forcedelete corrupt.db-journal
51fda06befSmistachkin  forcecopy test.db corrupt.db
52920769d3Sdrh  set f [open corrupt.db RDWR]
532812956bSdanielk1977  seek $f 8 start
542812956bSdanielk1977  puts $f blah
552812956bSdanielk1977  close $f
562812956bSdanielk1977
572812956bSdanielk1977  sqlite3 db2 corrupt.db
58cb354603Sdan  catchsql "
59cb354603Sdan    $::presql
602812956bSdanielk1977    SELECT * FROM sqlite_master;
61cb354603Sdan  " db2
62ff4fa772Sdrh} {1 {file is not a database}}
632812956bSdanielk1977
642812956bSdanielk1977do_test corrupt2-1.3 {
652812956bSdanielk1977  db2 close
662812956bSdanielk1977
672812956bSdanielk1977  # Corrupt the page-size (bytes 16 and 17 of page 1).
68fda06befSmistachkin  forcedelete corrupt.db
69fda06befSmistachkin  forcedelete corrupt.db-journal
70fda06befSmistachkin  forcecopy test.db corrupt.db
71920769d3Sdrh  set f [open corrupt.db RDWR]
722812956bSdanielk1977  fconfigure $f -encoding binary
732812956bSdanielk1977  seek $f 16 start
742812956bSdanielk1977  puts -nonewline $f "\x00\xFF"
752812956bSdanielk1977  close $f
762812956bSdanielk1977
772812956bSdanielk1977  sqlite3 db2 corrupt.db
78cb354603Sdan  catchsql "
79cb354603Sdan    $::presql
802812956bSdanielk1977    SELECT * FROM sqlite_master;
81cb354603Sdan  " db2
82ff4fa772Sdrh} {1 {file is not a database}}
832812956bSdanielk1977
842812956bSdanielk1977do_test corrupt2-1.4 {
852812956bSdanielk1977  db2 close
862812956bSdanielk1977
872812956bSdanielk1977  # Corrupt the free-block list on page 1.
88fda06befSmistachkin  forcedelete corrupt.db
89fda06befSmistachkin  forcedelete corrupt.db-journal
90fda06befSmistachkin  forcecopy test.db corrupt.db
91920769d3Sdrh  set f [open corrupt.db RDWR]
922812956bSdanielk1977  fconfigure $f -encoding binary
932812956bSdanielk1977  seek $f 101 start
942812956bSdanielk1977  puts -nonewline $f "\xFF\xFF"
952812956bSdanielk1977  close $f
962812956bSdanielk1977
972812956bSdanielk1977  sqlite3 db2 corrupt.db
98*68133509Sdrh  # Note: This test is no longer meaningful due to the deferred computation
99*68133509Sdrh  # of MemPage.nFree
100*68133509Sdrh  catchsql {PRAGMA quick_check} db2
101*68133509Sdrh} {0 {{*** in database main ***
102*68133509SdrhPage 1: free space corruption}}}
1032812956bSdanielk1977
1042812956bSdanielk1977do_test corrupt2-1.5 {
1052812956bSdanielk1977  db2 close
1062812956bSdanielk1977
1072812956bSdanielk1977  # Corrupt the free-block list on page 1.
108fda06befSmistachkin  forcedelete corrupt.db
109fda06befSmistachkin  forcedelete corrupt.db-journal
110fda06befSmistachkin  forcecopy test.db corrupt.db
111920769d3Sdrh  set f [open corrupt.db RDWR]
1122812956bSdanielk1977  fconfigure $f -encoding binary
1132812956bSdanielk1977  seek $f 101 start
1142812956bSdanielk1977  puts -nonewline $f "\x00\xC8"
1152812956bSdanielk1977  seek $f 200 start
1162812956bSdanielk1977  puts -nonewline $f "\x00\x00"
1172812956bSdanielk1977  puts -nonewline $f "\x10\x00"
1182812956bSdanielk1977  close $f
1192812956bSdanielk1977
1202812956bSdanielk1977  sqlite3 db2 corrupt.db
121*68133509Sdrh  catchsql {PRAGMA quick_check} db2
122*68133509Sdrh} {0 {{*** in database main ***
123*68133509SdrhPage 1: free space corruption}}}
1242812956bSdanielk1977db2 close
1252812956bSdanielk1977
126d45a0315Sdanielk1977# Corrupt a database by having 2 indices of the same name:
127d45a0315Sdanielk1977do_test corrupt2-2.1 {
128d45a0315Sdanielk1977
129fda06befSmistachkin  forcedelete corrupt.db
130fda06befSmistachkin  forcedelete corrupt.db-journal
131fda06befSmistachkin  forcecopy test.db corrupt.db
132d45a0315Sdanielk1977
133d45a0315Sdanielk1977  sqlite3 db2 corrupt.db
1340f0d3ddfSdrh  sqlite3_db_config db2 DEFENSIVE 0
135cb354603Sdan  execsql "
136cb354603Sdan    $::presql
137d45a0315Sdanielk1977    CREATE INDEX a1 ON abc(a);
138d45a0315Sdanielk1977    CREATE INDEX a2 ON abc(b);
139d45a0315Sdanielk1977    PRAGMA writable_schema = 1;
140d45a0315Sdanielk1977    UPDATE sqlite_master
141d45a0315Sdanielk1977      SET name = 'a3', sql = 'CREATE INDEX a3' || substr(sql, 16, 10000)
142d45a0315Sdanielk1977      WHERE type = 'index';
143d45a0315Sdanielk1977    PRAGMA writable_schema = 0;
144cb354603Sdan  " db2
145d45a0315Sdanielk1977
146d45a0315Sdanielk1977  db2 close
147d45a0315Sdanielk1977  sqlite3 db2 corrupt.db
148cb354603Sdan  catchsql "
149cb354603Sdan    $::presql
150d45a0315Sdanielk1977    SELECT * FROM sqlite_master;
151cb354603Sdan  " db2
15234533150Sdrh} {1 {malformed database schema (a3) - index a3 already exists}}
153d45a0315Sdanielk1977
154d45a0315Sdanielk1977db2 close
155d45a0315Sdanielk1977
1562b76b05dSdanielk1977do_test corrupt2-3.1 {
157fda06befSmistachkin  forcedelete corrupt.db
158fda06befSmistachkin  forcedelete corrupt.db-journal
1592b76b05dSdanielk1977  sqlite3 db2 corrupt.db
1602b76b05dSdanielk1977
161cb354603Sdan  execsql "
162cb354603Sdan    $::presql
1632b76b05dSdanielk1977    PRAGMA auto_vacuum = 1;
1642b76b05dSdanielk1977    PRAGMA page_size = 1024;
1652b76b05dSdanielk1977    CREATE TABLE t1(a, b, c);
1662b76b05dSdanielk1977    CREATE TABLE t2(a, b, c);
1672b76b05dSdanielk1977    INSERT INTO t2 VALUES(randomblob(100), randomblob(100), randomblob(100));
1682b76b05dSdanielk1977    INSERT INTO t2 SELECT * FROM t2;
1692b76b05dSdanielk1977    INSERT INTO t2 SELECT * FROM t2;
1702b76b05dSdanielk1977    INSERT INTO t2 SELECT * FROM t2;
1712b76b05dSdanielk1977    INSERT INTO t2 SELECT * FROM t2;
172cb354603Sdan  " db2
1732b76b05dSdanielk1977
1742b76b05dSdanielk1977  db2 close
1752b76b05dSdanielk1977
1762b76b05dSdanielk1977  # On the root page of table t2 (page 4), set one of the child page-numbers
1772b76b05dSdanielk1977  # to 0. This corruption will be detected when SQLite attempts to update
1782b76b05dSdanielk1977  # the pointer-map after moving the content of page 4 to page 3 as part
1792b76b05dSdanielk1977  # of the DROP TABLE operation below.
1802b76b05dSdanielk1977  #
1812b76b05dSdanielk1977  set fd [open corrupt.db r+]
1822b76b05dSdanielk1977  fconfigure $fd -encoding binary -translation binary
1832b76b05dSdanielk1977  seek $fd [expr 1024*3 + 12]
1842b76b05dSdanielk1977  set zCelloffset [read $fd 2]
1852b76b05dSdanielk1977  binary scan $zCelloffset S iCelloffset
1862b76b05dSdanielk1977  seek $fd [expr 1024*3 + $iCelloffset]
1872b76b05dSdanielk1977  puts -nonewline $fd "\00\00\00\00"
1882b76b05dSdanielk1977  close $fd
1892b76b05dSdanielk1977
1902b76b05dSdanielk1977  sqlite3 db2 corrupt.db
191cb354603Sdan  catchsql "
192cb354603Sdan    $::presql
1932b76b05dSdanielk1977    DROP TABLE t1;
194cb354603Sdan  " db2
195b34a4edeSdanielk1977} {1 {database disk image is malformed}}
196b34a4edeSdanielk1977
197b34a4edeSdanielk1977do_test corrupt2-4.1 {
19875c5fa88Sdanielk1977  catchsql {
19975c5fa88Sdanielk1977    SELECT * FROM t2;
20075c5fa88Sdanielk1977  } db2
20175c5fa88Sdanielk1977} {1 {database disk image is malformed}}
20275c5fa88Sdanielk1977
20338816ecfSdanielk1977db2 close
20438816ecfSdanielk1977
2058af69063Sdrhunset -nocomplain result
2063aa4b67fSdanielk1977do_test corrupt2-5.1 {
207fda06befSmistachkin  forcedelete corrupt.db
208fda06befSmistachkin  forcedelete corrupt.db-journal
209b34a4edeSdanielk1977  sqlite3 db2 corrupt.db
210b34a4edeSdanielk1977
211cb354603Sdan  execsql "
212cb354603Sdan    $::presql
213e6a64648Sdrh    PRAGMA auto_vacuum = 0;
214b34a4edeSdanielk1977    PRAGMA page_size = 1024;
215b34a4edeSdanielk1977    CREATE TABLE t1(a, b, c);
216b34a4edeSdanielk1977    CREATE TABLE t2(a, b, c);
217b34a4edeSdanielk1977    INSERT INTO t2 VALUES(randomblob(100), randomblob(100), randomblob(100));
218b34a4edeSdanielk1977    INSERT INTO t2 SELECT * FROM t2;
219b34a4edeSdanielk1977    INSERT INTO t2 SELECT * FROM t2;
220b34a4edeSdanielk1977    INSERT INTO t2 SELECT * FROM t2;
221b34a4edeSdanielk1977    INSERT INTO t2 SELECT * FROM t2;
222b34a4edeSdanielk1977    INSERT INTO t1 SELECT * FROM t2;
223cb354603Sdan  " db2
224b34a4edeSdanielk1977
225b34a4edeSdanielk1977  db2 close
226b34a4edeSdanielk1977
227b34a4edeSdanielk1977  # This block links a page from table t2 into the t1 table structure.
228b34a4edeSdanielk1977  #
229b34a4edeSdanielk1977  set fd [open corrupt.db r+]
230b34a4edeSdanielk1977  fconfigure $fd -encoding binary -translation binary
231b34a4edeSdanielk1977  seek $fd [expr 1024 + 12]
232b34a4edeSdanielk1977  set zCelloffset [read $fd 2]
233b34a4edeSdanielk1977  binary scan $zCelloffset S iCelloffset
234b34a4edeSdanielk1977  seek $fd [expr 1024 + $iCelloffset]
235b34a4edeSdanielk1977  set zChildPage [read $fd 4]
236b34a4edeSdanielk1977  seek $fd [expr 2*1024 + 12]
237b34a4edeSdanielk1977  set zCelloffset [read $fd 2]
238b34a4edeSdanielk1977  binary scan $zCelloffset S iCelloffset
239b34a4edeSdanielk1977  seek $fd [expr 2*1024 + $iCelloffset]
240b34a4edeSdanielk1977  puts -nonewline $fd $zChildPage
241b34a4edeSdanielk1977  close $fd
242b34a4edeSdanielk1977
243b34a4edeSdanielk1977  sqlite3 db2 corrupt.db
244cb354603Sdan  db2 eval $::presql
245b34a4edeSdanielk1977  db2 eval {SELECT rowid FROM t1} {
246b34a4edeSdanielk1977    set result [db2 eval {pragma integrity_check}]
247b34a4edeSdanielk1977    break
248b34a4edeSdanielk1977  }
249b34a4edeSdanielk1977  set result
250b34a4edeSdanielk1977} {{*** in database main ***
251b34a4edeSdanielk1977On tree page 2 cell 0: 2nd reference to page 10
252b34a4edeSdanielk1977Page 4 is never used}}
2532b76b05dSdanielk1977
2542b76b05dSdanielk1977db2 close
2552b76b05dSdanielk1977
2563aa4b67fSdanielk1977proc corruption_test {args} {
257ff9b2e75Sdanielk1977  set A(-corrupt) {}
258ef165cedSdanielk1977  set A(-sqlprep) {}
259ef165cedSdanielk1977  set A(-tclprep) {}
2603aa4b67fSdanielk1977  array set A $args
2613aa4b67fSdanielk1977
2623aa4b67fSdanielk1977  catch {db close}
263fda06befSmistachkin  forcedelete corrupt.db
264fda06befSmistachkin  forcedelete corrupt.db-journal
2653aa4b67fSdanielk1977
2663aa4b67fSdanielk1977  sqlite3 db corrupt.db
2670f0d3ddfSdrh  sqlite3_db_config db DEFENSIVE 0
268cb354603Sdan  db eval $::presql
269ef165cedSdanielk1977  eval $A(-tclprep)
2703aa4b67fSdanielk1977  db eval $A(-sqlprep)
2713aa4b67fSdanielk1977  db close
2723aa4b67fSdanielk1977
2733aa4b67fSdanielk1977  eval $A(-corrupt)
2743aa4b67fSdanielk1977
2753aa4b67fSdanielk1977  sqlite3 db corrupt.db
2763aa4b67fSdanielk1977  eval $A(-test)
2773aa4b67fSdanielk1977}
2783aa4b67fSdanielk1977
2793aa4b67fSdanielk1977ifcapable autovacuum {
2803aa4b67fSdanielk1977  # The tests within this block - corrupt2-6.* - aim to test corruption
2813aa4b67fSdanielk1977  # detection within an incremental-vacuum. When an incremental-vacuum
2823aa4b67fSdanielk1977  # step is executed, the last non-free page of the database file is
2833aa4b67fSdanielk1977  # moved into a free space in the body of the file. After doing so,
2843aa4b67fSdanielk1977  # the page reference in the parent page must be updated to refer
2853aa4b67fSdanielk1977  # to the new location. These tests test the outcome of corrupting
2863aa4b67fSdanielk1977  # that page reference before performing the incremental vacuum.
2873aa4b67fSdanielk1977  #
2883aa4b67fSdanielk1977
2893aa4b67fSdanielk1977  # The last page in the database page is the second page
2903aa4b67fSdanielk1977  # in an overflow chain.
2913aa4b67fSdanielk1977  #
2923aa4b67fSdanielk1977  corruption_test -sqlprep {
2933aa4b67fSdanielk1977    PRAGMA auto_vacuum = incremental;
2943aa4b67fSdanielk1977    PRAGMA page_size = 1024;
2953aa4b67fSdanielk1977    CREATE TABLE t1(a, b);
2963aa4b67fSdanielk1977    INSERT INTO t1 VALUES(1, randomblob(2500));
2973aa4b67fSdanielk1977    INSERT INTO t1 VALUES(2, randomblob(2500));
2983aa4b67fSdanielk1977    DELETE FROM t1 WHERE a = 1;
2993aa4b67fSdanielk1977  } -corrupt {
3003aa4b67fSdanielk1977    hexio_write corrupt.db [expr 1024*5] 00000008
3013aa4b67fSdanielk1977  } -test {
3023aa4b67fSdanielk1977    do_test corrupt2-6.1 {
303cb354603Sdan      catchsql " $::presql pragma incremental_vacuum = 1 "
3043aa4b67fSdanielk1977    } {1 {database disk image is malformed}}
3053aa4b67fSdanielk1977  }
3063aa4b67fSdanielk1977
3073aa4b67fSdanielk1977  # The last page in the database page is a non-root b-tree page.
3083aa4b67fSdanielk1977  #
3093aa4b67fSdanielk1977  corruption_test -sqlprep {
3103aa4b67fSdanielk1977    PRAGMA auto_vacuum = incremental;
3113aa4b67fSdanielk1977    PRAGMA page_size = 1024;
3123aa4b67fSdanielk1977    CREATE TABLE t1(a INTEGER PRIMARY KEY, b);
3133aa4b67fSdanielk1977    INSERT INTO t1 VALUES(1, randomblob(2500));
3143aa4b67fSdanielk1977    INSERT INTO t1 VALUES(2, randomblob(50));
3153aa4b67fSdanielk1977    INSERT INTO t1 SELECT NULL, randomblob(50) FROM t1;
3163aa4b67fSdanielk1977    INSERT INTO t1 SELECT NULL, randomblob(50) FROM t1;
3173aa4b67fSdanielk1977    INSERT INTO t1 SELECT NULL, randomblob(50) FROM t1;
3183aa4b67fSdanielk1977    INSERT INTO t1 SELECT NULL, randomblob(50) FROM t1;
3193aa4b67fSdanielk1977    DELETE FROM t1 WHERE a = 1;
3203aa4b67fSdanielk1977  } -corrupt {
3213aa4b67fSdanielk1977    hexio_write corrupt.db [expr 1024*2 + 8] 00000009
3223aa4b67fSdanielk1977  } -test {
3233aa4b67fSdanielk1977    do_test corrupt2-6.2 {
324cb354603Sdan      catchsql " $::presql pragma incremental_vacuum = 1 "
3253aa4b67fSdanielk1977    } {1 {database disk image is malformed}}
3263aa4b67fSdanielk1977  }
3273aa4b67fSdanielk1977
3283aa4b67fSdanielk1977  # Set up a pointer-map entry so that the last page of the database
3293aa4b67fSdanielk1977  # file appears to be a b-tree root page. This should be detected
3303aa4b67fSdanielk1977  # as corruption.
3313aa4b67fSdanielk1977  #
3323aa4b67fSdanielk1977  corruption_test -sqlprep {
3333aa4b67fSdanielk1977    PRAGMA auto_vacuum = incremental;
3343aa4b67fSdanielk1977    PRAGMA page_size = 1024;
3353aa4b67fSdanielk1977    CREATE TABLE t1(a INTEGER PRIMARY KEY, b);
3363aa4b67fSdanielk1977    INSERT INTO t1 VALUES(1, randomblob(2500));
3373aa4b67fSdanielk1977    INSERT INTO t1 VALUES(2, randomblob(2500));
3383aa4b67fSdanielk1977    INSERT INTO t1 VALUES(3, randomblob(2500));
3393aa4b67fSdanielk1977    DELETE FROM t1 WHERE a = 1;
3403aa4b67fSdanielk1977  } -corrupt {
3413aa4b67fSdanielk1977    set nPage [expr [file size corrupt.db] / 1024]
3423aa4b67fSdanielk1977    hexio_write corrupt.db [expr 1024 + ($nPage-3)*5] 010000000
3433aa4b67fSdanielk1977  } -test {
3443aa4b67fSdanielk1977    do_test corrupt2-6.3 {
345cb354603Sdan      catchsql " $::presql pragma incremental_vacuum = 1 "
3463aa4b67fSdanielk1977    } {1 {database disk image is malformed}}
3473aa4b67fSdanielk1977  }
3483aa4b67fSdanielk1977
34982f52540Sdrh  if {![nonzero_reserved_bytes]} {
350a961339cSdanielk1977    corruption_test -sqlprep {
351a961339cSdanielk1977      PRAGMA auto_vacuum = 1;
352a961339cSdanielk1977      PRAGMA page_size = 1024;
353a961339cSdanielk1977      CREATE TABLE t1(a INTEGER PRIMARY KEY, b);
354a961339cSdanielk1977      INSERT INTO t1 VALUES(1, randomblob(2500));
355a961339cSdanielk1977      DELETE FROM t1 WHERE a = 1;
356a961339cSdanielk1977    } -corrupt {
357a961339cSdanielk1977      set nAppend [expr 1024*207 - [file size corrupt.db]]
358a961339cSdanielk1977      set fd [open corrupt.db r+]
359a961339cSdanielk1977      seek $fd 0 end
360a961339cSdanielk1977      puts -nonewline $fd [string repeat x $nAppend]
361a961339cSdanielk1977      close $fd
362dd3cd977Sdrh      hexio_write corrupt.db 28 00000000
363a961339cSdanielk1977    } -test {
364a961339cSdanielk1977      do_test corrupt2-6.4 {
365cb354603Sdan        catchsql "
366cb354603Sdan          $::presql
367a961339cSdanielk1977          BEGIN EXCLUSIVE;
368a961339cSdanielk1977          COMMIT;
369cb354603Sdan        "
370a961339cSdanielk1977      } {1 {database disk image is malformed}}
371a961339cSdanielk1977    }
37235af9ba0Sdanielk1977  }
37382f52540Sdrh}
374a961339cSdanielk1977
3759ffe5d72Sdanielk1977
3761bc71590Sdanielk1977set sqlprep {
377e6a64648Sdrh  PRAGMA auto_vacuum = 0;
37835af9ba0Sdanielk1977  PRAGMA page_size = 1024;
37935af9ba0Sdanielk1977  CREATE TABLE t1(a INTEGER PRIMARY KEY, b);
38035af9ba0Sdanielk1977  CREATE INDEX i1 ON t1(b);
38135af9ba0Sdanielk1977  INSERT INTO t1 VALUES(1, randomblob(50));
38235af9ba0Sdanielk1977  INSERT INTO t1 SELECT NULL, randomblob(50) FROM t1;
38335af9ba0Sdanielk1977  INSERT INTO t1 SELECT NULL, randomblob(50) FROM t1;
38435af9ba0Sdanielk1977  INSERT INTO t1 SELECT NULL, randomblob(50) FROM t1;
38535af9ba0Sdanielk1977  INSERT INTO t1 SELECT NULL, randomblob(50) FROM t1;
38635af9ba0Sdanielk1977  INSERT INTO t1 SELECT NULL, randomblob(50) FROM t1;
38735af9ba0Sdanielk1977  INSERT INTO t1 SELECT NULL, randomblob(50) FROM t1;
3881bc71590Sdanielk1977}
3891bc71590Sdanielk1977
3901bc71590Sdanielk1977corruption_test -sqlprep $sqlprep -corrupt {
39135af9ba0Sdanielk1977  # Set the page-flags of one of the leaf pages of the index B-Tree to
39235af9ba0Sdanielk1977  # 0x0D (interpreted by SQLite as "leaf page of a table B-Tree").
39335af9ba0Sdanielk1977  #
39435af9ba0Sdanielk1977  set fd [open corrupt.db r+]
39535af9ba0Sdanielk1977  fconfigure $fd -translation binary -encoding binary
39635af9ba0Sdanielk1977  seek $fd [expr 1024*2 + 8]
39735af9ba0Sdanielk1977  set zRightChild [read $fd 4]
39835af9ba0Sdanielk1977  binary scan $zRightChild I iRightChild
39935af9ba0Sdanielk1977  seek $fd [expr 1024*($iRightChild-1)]
40035af9ba0Sdanielk1977  puts -nonewline $fd "\x0D"
40135af9ba0Sdanielk1977  close $fd
40235af9ba0Sdanielk1977} -test {
40335af9ba0Sdanielk1977  do_test corrupt2-7.1 {
404cb354603Sdan    catchsql " $::presql SELECT b FROM t1 ORDER BY b ASC "
4051bc71590Sdanielk1977  } {1 {database disk image is malformed}}
4061bc71590Sdanielk1977}
4071bc71590Sdanielk1977
4081bc71590Sdanielk1977corruption_test -sqlprep $sqlprep -corrupt {
4091bc71590Sdanielk1977  # Mess up the page-header of one of the leaf pages of the index B-Tree.
4101bc71590Sdanielk1977  # The corruption is detected as part of an OP_Prev opcode.
4111bc71590Sdanielk1977  #
4121bc71590Sdanielk1977  set fd [open corrupt.db r+]
4131bc71590Sdanielk1977  fconfigure $fd -translation binary -encoding binary
4141bc71590Sdanielk1977  seek $fd [expr 1024*2 + 12]
4151bc71590Sdanielk1977  set zCellOffset [read $fd 2]
4161bc71590Sdanielk1977  binary scan $zCellOffset S iCellOffset
4171bc71590Sdanielk1977  seek $fd [expr 1024*2 + $iCellOffset]
4181bc71590Sdanielk1977  set zChild [read $fd 4]
4191bc71590Sdanielk1977  binary scan $zChild I iChild
4201bc71590Sdanielk1977  seek $fd [expr 1024*($iChild-1)+3]
4211bc71590Sdanielk1977  puts -nonewline $fd "\xFFFF"
4221bc71590Sdanielk1977  close $fd
4231bc71590Sdanielk1977} -test {
4241bc71590Sdanielk1977  do_test corrupt2-7.1 {
425cb354603Sdan    catchsql " $::presql SELECT b FROM t1 ORDER BY b DESC "
4261bc71590Sdanielk1977  } {1 {database disk image is malformed}}
4271bc71590Sdanielk1977}
4281bc71590Sdanielk1977
4291bc71590Sdanielk1977corruption_test -sqlprep $sqlprep -corrupt {
4301bc71590Sdanielk1977  # Set the page-flags of one of the leaf pages of the table B-Tree to
4311bc71590Sdanielk1977  # 0x0A (interpreted by SQLite as "leaf page of an index B-Tree").
4321bc71590Sdanielk1977  #
4331bc71590Sdanielk1977  set fd [open corrupt.db r+]
4341bc71590Sdanielk1977  fconfigure $fd -translation binary -encoding binary
4351bc71590Sdanielk1977  seek $fd [expr 1024*1 + 8]
4361bc71590Sdanielk1977  set zRightChild [read $fd 4]
4371bc71590Sdanielk1977  binary scan $zRightChild I iRightChild
4381bc71590Sdanielk1977  seek $fd [expr 1024*($iRightChild-1)]
4391bc71590Sdanielk1977  puts -nonewline $fd "\x0A"
4401bc71590Sdanielk1977  close $fd
4411bc71590Sdanielk1977} -test {
4421bc71590Sdanielk1977  do_test corrupt2-8.1 {
443cb354603Sdan    catchsql " $::presql SELECT * FROM t1 WHERE rowid=1000 "
44435af9ba0Sdanielk1977  } {1 {database disk image is malformed}}
4453aa4b67fSdanielk1977}
4463aa4b67fSdanielk1977
4479ffe5d72Sdanielk1977corruption_test -sqlprep {
4489ffe5d72Sdanielk1977  CREATE TABLE t1(a, b, c); CREATE TABLE t8(a, b, c); CREATE TABLE tE(a, b, c);
4499ffe5d72Sdanielk1977  CREATE TABLE t2(a, b, c); CREATE TABLE t9(a, b, c); CREATE TABLE tF(a, b, c);
4509ffe5d72Sdanielk1977  CREATE TABLE t3(a, b, c); CREATE TABLE tA(a, b, c); CREATE TABLE tG(a, b, c);
4519ffe5d72Sdanielk1977  CREATE TABLE t4(a, b, c); CREATE TABLE tB(a, b, c); CREATE TABLE tH(a, b, c);
4529ffe5d72Sdanielk1977  CREATE TABLE t5(a, b, c); CREATE TABLE tC(a, b, c); CREATE TABLE tI(a, b, c);
4539ffe5d72Sdanielk1977  CREATE TABLE t6(a, b, c); CREATE TABLE tD(a, b, c); CREATE TABLE tJ(a, b, c);
4549ffe5d72Sdanielk1977  CREATE TABLE x1(a, b, c); CREATE TABLE x8(a, b, c); CREATE TABLE xE(a, b, c);
4559ffe5d72Sdanielk1977  CREATE TABLE x2(a, b, c); CREATE TABLE x9(a, b, c); CREATE TABLE xF(a, b, c);
4569ffe5d72Sdanielk1977  CREATE TABLE x3(a, b, c); CREATE TABLE xA(a, b, c); CREATE TABLE xG(a, b, c);
4579ffe5d72Sdanielk1977  CREATE TABLE x4(a, b, c); CREATE TABLE xB(a, b, c); CREATE TABLE xH(a, b, c);
4589ffe5d72Sdanielk1977  CREATE TABLE x5(a, b, c); CREATE TABLE xC(a, b, c); CREATE TABLE xI(a, b, c);
4599ffe5d72Sdanielk1977  CREATE TABLE x6(a, b, c); CREATE TABLE xD(a, b, c); CREATE TABLE xJ(a, b, c);
4609ffe5d72Sdanielk1977} -corrupt {
4619ffe5d72Sdanielk1977  set fd [open corrupt.db r+]
4629ffe5d72Sdanielk1977  fconfigure $fd -translation binary -encoding binary
4639ffe5d72Sdanielk1977  seek $fd 108
4649ffe5d72Sdanielk1977  set zRightChild [read $fd 4]
4659ffe5d72Sdanielk1977  binary scan $zRightChild I iRightChild
4669ffe5d72Sdanielk1977  seek $fd [expr 1024*($iRightChild-1)+3]
4679ffe5d72Sdanielk1977  puts -nonewline $fd "\x00\x00"
4689ffe5d72Sdanielk1977  close $fd
4699ffe5d72Sdanielk1977} -test {
4701bc71590Sdanielk1977  do_test corrupt2-9.1 {
471cb354603Sdan    catchsql " $::presql SELECT sql FROM sqlite_master "
4729ffe5d72Sdanielk1977  } {1 {database disk image is malformed}}
4739ffe5d72Sdanielk1977}
4749ffe5d72Sdanielk1977
475ff9b2e75Sdanielk1977corruption_test -sqlprep {
476ff9b2e75Sdanielk1977  CREATE TABLE t1(a, b, c);
477ff9b2e75Sdanielk1977  CREATE TABLE t2(a, b, c);
478ff9b2e75Sdanielk1977  PRAGMA writable_schema = 1;
479ff9b2e75Sdanielk1977  UPDATE sqlite_master SET rootpage = NULL WHERE name = 't2';
480ff9b2e75Sdanielk1977} -test {
481ff9b2e75Sdanielk1977  do_test corrupt2-10.1 {
482cb354603Sdan    catchsql " $::presql SELECT * FROM t2 "
483ff9b2e75Sdanielk1977  } {1 {malformed database schema (t2)}}
484ff9b2e75Sdanielk1977  do_test corrupt2-10.2 {
485ff9b2e75Sdanielk1977    sqlite3_errcode db
486ff9b2e75Sdanielk1977  } {SQLITE_CORRUPT}
487ff9b2e75Sdanielk1977}
488ff9b2e75Sdanielk1977
489fa542f1fSdanielk1977corruption_test -sqlprep {
490fa542f1fSdanielk1977  PRAGMA auto_vacuum = incremental;
491fa542f1fSdanielk1977  CREATE TABLE t1(a INTEGER PRIMARY KEY, b);
492fa542f1fSdanielk1977  CREATE TABLE t2(a INTEGER PRIMARY KEY, b);
493fa542f1fSdanielk1977  INSERT INTO t1 VALUES(1, randstr(100,100));
494fa542f1fSdanielk1977  INSERT INTO t1 SELECT NULL, randstr(100,100) FROM t1;
495fa542f1fSdanielk1977  INSERT INTO t1 SELECT NULL, randstr(100,100) FROM t1;
496fa542f1fSdanielk1977  INSERT INTO t1 SELECT NULL, randstr(100,100) FROM t1;
497fa542f1fSdanielk1977  INSERT INTO t1 SELECT NULL, randstr(100,100) FROM t1;
498fa542f1fSdanielk1977  INSERT INTO t1 SELECT NULL, randstr(100,100) FROM t1;
499fa542f1fSdanielk1977  INSERT INTO t2 SELECT * FROM t1;
500fa542f1fSdanielk1977  DELETE FROM t1;
501fa542f1fSdanielk1977} -corrupt {
502fa542f1fSdanielk1977  set offset [expr [file size corrupt.db] - 1024]
503fa542f1fSdanielk1977  hexio_write corrupt.db $offset FF
504fa542f1fSdanielk1977  hexio_write corrupt.db 24   12345678
505fa542f1fSdanielk1977} -test {
506fa542f1fSdanielk1977  do_test corrupt2-11.1 {
507cb354603Sdan    catchsql " $::presql PRAGMA incremental_vacuum "
508fa542f1fSdanielk1977  } {1 {database disk image is malformed}}
509fa542f1fSdanielk1977}
510fa542f1fSdanielk1977corruption_test -sqlprep {
511fa542f1fSdanielk1977  PRAGMA auto_vacuum = incremental;
512fa542f1fSdanielk1977  CREATE TABLE t1(a INTEGER PRIMARY KEY, b);
513fa542f1fSdanielk1977  CREATE TABLE t2(a INTEGER PRIMARY KEY, b);
514fa542f1fSdanielk1977  INSERT INTO t1 VALUES(1, randstr(100,100));
515fa542f1fSdanielk1977  INSERT INTO t1 SELECT NULL, randstr(100,100) FROM t1;
516fa542f1fSdanielk1977  INSERT INTO t1 SELECT NULL, randstr(100,100) FROM t1;
517fa542f1fSdanielk1977  INSERT INTO t1 SELECT NULL, randstr(100,100) FROM t1;
518fa542f1fSdanielk1977  INSERT INTO t1 SELECT NULL, randstr(100,100) FROM t1;
519fa542f1fSdanielk1977  INSERT INTO t1 SELECT NULL, randstr(100,100) FROM t1;
520fa542f1fSdanielk1977  INSERT INTO t2 SELECT * FROM t1;
521fa542f1fSdanielk1977  DELETE FROM t1;
522fa542f1fSdanielk1977} -corrupt {
523fa542f1fSdanielk1977  set pgno [expr [file size corrupt.db] / 1024]
524fa542f1fSdanielk1977  hexio_write corrupt.db [expr 1024+5*($pgno-3)] 03
525fa542f1fSdanielk1977  hexio_write corrupt.db 24   12345678
526fa542f1fSdanielk1977} -test {
527fa542f1fSdanielk1977  do_test corrupt2-12.1 {
528cb354603Sdan    catchsql " $::presql PRAGMA incremental_vacuum "
529fa542f1fSdanielk1977  } {1 {database disk image is malformed}}
530fa542f1fSdanielk1977}
531fa542f1fSdanielk1977
532ef165cedSdanielk1977ifcapable autovacuum {
533ef165cedSdanielk1977  # It is not possible for the last page in a database file to be the
534ef165cedSdanielk1977  # pending-byte page (AKA the locking page). This test verifies that if
535ef165cedSdanielk1977  # an attempt is made to commit a transaction to such an auto-vacuum
536ef165cedSdanielk1977  # database SQLITE_CORRUPT is returned.
537ef165cedSdanielk1977  #
538ef165cedSdanielk1977  corruption_test -tclprep {
539ef165cedSdanielk1977    db eval {
540ef165cedSdanielk1977      PRAGMA auto_vacuum = full;
541ef165cedSdanielk1977      PRAGMA page_size = 1024;
542ef165cedSdanielk1977      CREATE TABLE t1(a INTEGER PRIMARY KEY, b);
543ef165cedSdanielk1977      INSERT INTO t1 VALUES(NULL, randstr(50,50));
544ef165cedSdanielk1977    }
545ef165cedSdanielk1977    for {set ii 0} {$ii < 10} {incr ii} {
546cb354603Sdan      db eval " $::presql INSERT INTO t1 SELECT NULL, randstr(50,50) FROM t1 "
547ef165cedSdanielk1977    }
548ef165cedSdanielk1977  } -corrupt {
549ef165cedSdanielk1977    do_test corrupt2-13.1 {
550ef165cedSdanielk1977      file size corrupt.db
551ef165cedSdanielk1977    } $::sqlite_pending_byte
552ef165cedSdanielk1977    hexio_write corrupt.db [expr $::sqlite_pending_byte+1023] 00
553dd3cd977Sdrh    hexio_write corrupt.db 28 00000000
554ef165cedSdanielk1977  } -test {
555ef165cedSdanielk1977    do_test corrupt2-13.2 {
556ef165cedSdanielk1977      file size corrupt.db
557ef165cedSdanielk1977    } [expr $::sqlite_pending_byte + 1024]
558ef165cedSdanielk1977    do_test corrupt2-13.3 {
559ef165cedSdanielk1977      catchsql { DELETE FROM t1 WHERE rowid < 30; }
560ef165cedSdanielk1977    } {1 {database disk image is malformed}}
561ef165cedSdanielk1977  }
562ef165cedSdanielk1977}
563ef165cedSdanielk1977
564ad41f5edSdan#-------------------------------------------------------------------------
565ad41f5edSdan# Test that PRAGMA integrity_check detects cases where the freelist-count
566ad41f5edSdan# header field is smaller than the actual number of pages on the freelist.
567ad41f5edSdan#
568ad41f5edSdan
569ad41f5edSdanreset_db
570ad41f5edSdando_execsql_test 14.0 {
571ad41f5edSdan  PRAGMA auto_vacuum = 0;
572ad41f5edSdan  CREATE TABLE t1(x);
573ad41f5edSdan  INSERT INTO t1 VALUES(randomblob(3500));
574ad41f5edSdan  DELETE FROM t1;
575ad41f5edSdan}
576ad41f5edSdan
577ad41f5edSdando_execsql_test 14.1 {
578ad41f5edSdan  PRAGMA integrity_check;
579ad41f5edSdan  PRAGMA freelist_count;
580ad41f5edSdan} {ok 3}
581ad41f5edSdan
582ad41f5edSdan# There are now 3 free pages. Modify the header-field so that it
583ad41f5edSdan# (incorrectly) says that just 2 are free.
584ad41f5edSdando_test 14.2 {
585ad41f5edSdan  db close
586ad41f5edSdan  hexio_write test.db 36 [hexio_render_int32 2]
587ad41f5edSdan  sqlite3 db test.db
588ad41f5edSdan  execsql { PRAGMA freelist_count }
589ad41f5edSdan} {2}
590ad41f5edSdan
591ad41f5edSdando_execsql_test 14.3 {
592ad41f5edSdan  PRAGMA integrity_check;
593ad41f5edSdan} {{*** in database main ***
59491d5866eSdrhMain freelist: size is 3 but should be 2}}
595ad41f5edSdan
596ad41f5edSdan# Use 2 of the free pages on the free-list.
597ad41f5edSdan#
598ad41f5edSdando_execsql_test 14.4 {
599ad41f5edSdan  INSERT INTO t1 VALUES(randomblob(2500));
600ad41f5edSdan  PRAGMA freelist_count;
601ad41f5edSdan} {0}
602ad41f5edSdan
603ad41f5edSdando_execsql_test 14.5 {
604ad41f5edSdan  PRAGMA integrity_check;
605ad41f5edSdan} {{*** in database main ***
60691d5866eSdrhMain freelist: size is 1 but should be 0}}
607ad41f5edSdan
608ad41f5edSdan
609ad41f5edSdanfinish_test
610ad41f5edSdan
6112812956bSdanielk1977finish_test
612