xref: /sqlite-3.40.0/test/backcompat.test (revision 9ab7fe87)
1# 2010 August 19
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.  The
12# focus of this file is testing that the current version of SQLite
13# is capable of reading and writing databases created by previous
14# versions, and vice-versa.
15#
16# To use this test, old versions of the testfixture process should be
17# copied into the working directory alongside the new version. The old
18# versions should be named "testfixtureXXX" (or testfixtureXXX.exe on
19# windows), where XXX can be any string.
20#
21# This test file uses the tcl code for controlling a second testfixture
22# process located in lock_common.tcl. See the commments in lock_common.tcl
23# for documentation of the available commands.
24#
25
26set testdir [file dirname $argv0]
27source $testdir/tester.tcl
28source $testdir/lock_common.tcl
29source $testdir/malloc_common.tcl
30db close
31
32# Search for binaries to test against. Any executable files that match
33# our naming convention are assumed to be testfixture binaries to test
34# against.
35#
36set binaries [list]
37set pattern "[file tail [info nameofexec]]*"
38foreach file [glob $pattern] {
39  if {[file executable $file]} {lappend binaries $file}
40}
41if {[llength $binaries]==0} {
42  puts "WARNING: No binaries to test against. No tests have been run."
43  finish_test
44  return
45}
46proc get_version {binary} {
47  set chan [launch_testfixture $binary]
48  set v [testfixture $chan { sqlite3 -version }]
49  close $chan
50  set v
51}
52foreach bin $binaries {
53  puts "Testing against $bin - version [get_version $bin]"
54}
55
56proc do_backcompat_test {rv bin1 bin2 script} {
57
58  file delete -force test.db
59
60  if {$bin1 != ""} { set ::bc_chan1 [launch_testfixture $bin1] }
61  set ::bc_chan2 [launch_testfixture $bin2]
62
63  if { $rv } {
64    proc code2 {tcl} { uplevel #0 $tcl }
65    if {$bin1 != ""} { proc code2 {tcl} { testfixture $::bc_chan1 $tcl } }
66    proc code1 {tcl} { testfixture $::bc_chan2 $tcl }
67  } else {
68    proc code1 {tcl} { uplevel #0 $tcl }
69    if {$bin1 != ""} { proc code1 {tcl} { testfixture $::bc_chan1 $tcl } }
70    proc code2 {tcl} { testfixture $::bc_chan2 $tcl }
71  }
72
73  proc sql1 sql { code1 [list db eval $sql] }
74  proc sql2 sql { code2 [list db eval $sql] }
75
76  code1 { sqlite3 db test.db }
77  code2 { sqlite3 db test.db }
78
79  uplevel $script
80
81  catch { code1 { db close } }
82  catch { code2 { db close } }
83  catch { close $::bc_chan2 }
84  catch { close $::bc_chan1 }
85}
86
87array set ::incompatible [list]
88proc do_allbackcompat_test {script} {
89
90  foreach bin $::binaries {
91    set nErr [set_test_counter errors]
92    foreach dir {0 1} {
93
94      set bintag [string map {testfixture {}} $bin]
95      if {$bintag == ""} {set bintag self}
96      set ::bcname ".$bintag.$dir."
97
98      rename do_test _do_test
99      proc do_test {nm sql res} {
100        set nm [regsub {\.} $nm $::bcname]
101        uplevel [list _do_test $nm $sql $res]
102      }
103
104      do_backcompat_test $dir {} $bin $script
105
106      rename do_test {}
107      rename _do_test do_test
108    }
109    if { $nErr < [set_test_counter errors] } {
110      set ::incompatible([get_version $bin]) 1
111    }
112  }
113}
114
115proc read_file {zFile} {
116  set zData {}
117  if {[file exists $zFile]} {
118    set fd [open $zFile]
119    fconfigure $fd -translation binary -encoding binary
120
121    if {[file size $zFile]<=$::sqlite_pending_byte || $zFile != "test.db"} {
122      set zData [read $fd]
123    } else {
124      set zData [read $fd $::sqlite_pending_byte]
125      append zData [string repeat x 512]
126      seek $fd [expr $::sqlite_pending_byte+512] start
127      append zData [read $fd]
128    }
129
130    close $fd
131  }
132  return $zData
133}
134proc write_file {zFile zData} {
135  set fd [open $zFile w]
136  fconfigure $fd -translation binary -encoding binary
137  puts -nonewline $fd $zData
138  close $fd
139}
140proc read_file_system {} {
141  set ret [list]
142  foreach f {test.db test.db-journal test.db-wal} { lappend ret [read_file $f] }
143  set ret
144}
145proc write_file_system {data} {
146  foreach f {test.db test.db-journal test.db-wal} d $data {
147    if {[string length $d] == 0} {
148      file delete -force $f
149    } else {
150      write_file $f $d
151    }
152  }
153}
154
155#-------------------------------------------------------------------------
156# Actual tests begin here.
157#
158# This first block of tests checks to see that the same database and
159# journal files can be used by old and new versions. WAL and wal-index
160# files are tested separately below.
161#
162do_allbackcompat_test {
163
164  # Test that database files are backwards compatible.
165  #
166  do_test backcompat-1.1.1 { sql1 {
167    CREATE TABLE t1(a PRIMARY KEY, b UNIQUE);
168    INSERT INTO t1 VALUES('abc', 'def');
169  } } {}
170  do_test backcompat-1.1.2 { sql2 { SELECT * FROM t1; } } {abc def}
171  do_test backcompat-1.1.3 { sql2 { INSERT INTO t1 VALUES('ghi', 'jkl'); } } {}
172  do_test backcompat-1.1.4 { sql1 { SELECT * FROM t1; } } {abc def ghi jkl}
173  do_test backcompat-1.1.5 { sql1 { PRAGMA integrity_check } } {ok}
174  do_test backcompat-1.1.6 { sql2 { PRAGMA integrity_check } } {ok}
175
176  # Test that one version can roll back a hot-journal file left in the
177  # file-system by the other version.
178  #
179  # Each test case is named "backcompat-1.X...", where X is either 0 or
180  # 1. If it is 0, then the current version creates a journal file that
181  # the old versions try to read. Otherwise, if X is 1, then the old version
182  # creates the journal file and we try to read it with the current version.
183  #
184  do_test backcompat-1.2.1 { sql1 {
185    PRAGMA cache_size = 10;
186    BEGIN;
187      INSERT INTO t1 VALUES(randomblob(400), randomblob(400));
188      INSERT INTO t1 SELECT randomblob(400), randomblob(400) FROM t1;
189      INSERT INTO t1 SELECT randomblob(400), randomblob(400) FROM t1;
190      INSERT INTO t1 SELECT randomblob(400), randomblob(400) FROM t1;
191      INSERT INTO t1 SELECT randomblob(400), randomblob(400) FROM t1;
192    COMMIT;
193  } } {}
194  set cksum1 [sql1 {SELECT md5sum(a), md5sum(b) FROM t1}]
195  set cksum2 [sql2 {SELECT md5sum(a), md5sum(b) FROM t1}]
196  do_test backcompat-1.2.2 [list string compare $cksum1 $cksum2] 0
197
198  do_test backcompat-1.2.3 { sql1 {
199    BEGIN;
200      UPDATE t1 SET a = randomblob(500);
201  } } {}
202  set data [read_file_system]
203
204  set f "test.db-journal[incr x]"
205  file copy -force test.db-journal $f
206
207  do_test backcompat-1.2.4 { sql1 { COMMIT } } {}
208
209  set same [expr {[sql2 {SELECT md5sum(a), md5sum(b) FROM t1}] == $cksum2}]
210  do_test backcompat-1.2.5 [list set {} $same] 0
211
212  code1 { db close }
213  code2 { db close }
214  write_file_system $data
215  code1 { sqlite3 db test.db }
216  code2 { sqlite3 db test.db }
217
218  set same [expr {[sql2 {SELECT md5sum(a), md5sum(b) FROM t1}] == $cksum2}]
219  do_test backcompat-1.2.6 [list set {} $same] 1
220
221  do_test backcompat-1.2.7 { sql1 { PRAGMA integrity_check } } {ok}
222  do_test backcompat-1.2.8 { sql2 { PRAGMA integrity_check } } {ok}
223}
224foreach k [lsort [array names ::incompatible]] {
225  puts "ERROR: Detected journal incompatibility with version $k"
226}
227unset ::incompatible
228
229
230#-------------------------------------------------------------------------
231# Test that WAL and wal-index files may be shared between different
232# SQLite versions.
233#
234do_allbackcompat_test {
235  if {[code1 {sqlite3 -version}] >= "3.7.0"
236   && [code2 {sqlite3 -version}] >= "3.7.0"
237  } {
238
239    do_test backcompat-2.1.1 { sql1 {
240      PRAGMA journal_mode = WAL;
241      CREATE TABLE t1(a PRIMARY KEY, b UNIQUE);
242      INSERT INTO t1 VALUES('I', 1);
243      INSERT INTO t1 VALUES('II', 2);
244      INSERT INTO t1 VALUES('III', 3);
245      SELECT * FROM t1;
246    } } {wal I 1 II 2 III 3}
247    do_test backcompat-2.1.2 { sql2 {
248      SELECT * FROM t1;
249    } } {I 1 II 2 III 3}
250
251    set data [read_file_system]
252    code1 {db close}
253    code2 {db close}
254    write_file_system $data
255    code1 {sqlite3 db test.db}
256    code2 {sqlite3 db test.db}
257
258    # The WAL file now in the file-system was created by the [code1]
259    # process. Check that the [code2] process can recover the log.
260    #
261    do_test backcompat-2.1.3 { sql2 {
262      SELECT * FROM t1;
263    } } {I 1 II 2 III 3}
264    do_test backcompat-2.1.4 { sql1 {
265      SELECT * FROM t1;
266    } } {I 1 II 2 III 3}
267  }
268}
269
270finish_test
271