xref: /sqlite-3.40.0/test/avfs.test (revision 5cad178b)
1# 2021-03-06
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#
12# This file implements tests for the appendvfs extension.
13#
14# Tests performed:
15# avfs-1.0. Test that an appendvfs DB can be added to an empty (ZLF) file.
16# avfs-1.1. Test that the DB can be read with correct content upon reopen.
17# avfs-1.2. Test that an appendvfs DB can be added to a simple text file.
18# avfs-1.3. Test that the DB can be read with correct content upon reopen.
19# avfs-1.4. Test that appended DB is aligned to default page boundary.
20# avfs-2.1. Test that the simple text file retains its initial text.
21# avfs-3.1. Test that the appendvfs can grow and shrink, remaining intact.
22# avfs-3.2. Test that appendvfs is intact after grow/shrink/close/reopen.
23# avfs-3.3. Test that appendvfs can grow by many pages and be written.
24# avfs-3.4. Test that grown appendvfs can be reopened and appear intact.
25# avfs-3.5. Test that much grown appendvfs can shrink and reopen intact.
26# avfs-4.1. Test shell's ability to append to a non-appendvfs file.
27# avfs-4.2. Test shell's ability to append to empty or nonexistent file.
28# avfs-4.3. Test shell's ability to reopen and alter an appendvfs file.
29# avfs-5.1. Test appendvfs refusal to open too-tiny DB appended onto ZLF.
30# avfs-5.2. Test appendvfs refusal to open too-tiny DB appended on other.
31# ...
32# (more to come)
33
34set testdir [file dirname $argv0]
35source $testdir/tester.tcl
36set ::testprefix avfs
37
38# Do not attempt this test if SQLITE_OMIT_VIRTUALTABLE is defined.
39#
40ifcapable !vtab {
41  finish_test
42  return
43}
44
45set CLI [test_find_cli]
46db close
47# forcedelete test.db
48
49load_static_extension db appendvfs
50
51set ::fa avfs.adb
52set ::fza avfs.sdb
53forcedelete $::fa $::fza
54set ::result {}
55
56proc shellDoesAr {} {
57  set shdo "sh_app1.sql"
58  forcedelete $shdo
59  set fd [open $shdo w]
60  puts $fd ".help\n.q"
61  close $fd
62  set res [catchcmd "-batch -cmd \".read $shdo\""]
63  return [regexp {^.archive} [lindex $res 1]]
64}
65
66set ::vf "&vfs=apndvfs"
67
68# Return file offset of appendvfs portion of a file, or {} if none such.
69proc fosAvfs {fname} {
70  if {[file size $fname] < 25} {
71    return {}
72  }
73  if {[catch {set fd [open $fname rb]}]} {
74    return {}
75  }
76  seek $fd -25 end
77  set am [read $fd 17]
78  set ao [read $fd 8]
79  close $fd
80  if {$am ne "Start-Of-SQLite3-"} {
81    return {}
82  }
83  binary scan $ao "W" rvo
84  return $rvo
85}
86
87do_test 1.0 {
88  set results {}
89  set out [open $::fza wb]
90  close $out
91  sqlite3 adb "file:$::fza?mode=rwc$::vf" -uri 1
92  adb eval {
93    PRAGMA page_size=1024;
94    PRAGMA cache_size=10;
95    CREATE TABLE t1(a TEXT);
96    INSERT INTO t1 VALUES ('dog'),('cat');
97    SELECT group_concat(a) as pets FROM (SELECT a FROM t1 ORDER BY a);
98  } { lappend results $pets }
99  adb close
100  lappend results [fosAvfs $fza]
101  set ::result [join $results " | "]
102} {cat,dog | 0}
103
104do_test 1.1 {
105  set results {}
106  sqlite3 adb "file:$::fza?mode=rw$::vf" -uri 1
107  adb eval {
108    SELECT group_concat(a) as pets FROM (SELECT a FROM t1 ORDER BY a DESC);
109  } { lappend results $pets }
110  adb close
111  set ::result [join $results " | "]
112} {dog,cat}
113
114do_test 1.2 {
115  set results {}
116  set out [open $::fa wb]
117  set ::tlo { "Just some text," "and more text," "ending at 3 lines." }
118  puts $out [join $::tlo "\n"]
119  close $out
120  set adbSz [file size $::fa]
121  sqlite3 adb "file:$::fa?mode=rwc$::vf" -uri 1
122  adb eval {
123    PRAGMA auto_vacuum = 0;
124    PRAGMA page_size=512;
125    PRAGMA cache_size=0;
126    CREATE TABLE t1(a TEXT);
127    INSERT INTO t1 VALUES ('dog'),('cat'),('pig');
128    SELECT group_concat(a) as pets FROM (SELECT a FROM t1 ORDER BY a);
129  } { lappend results $pets }
130  adb close
131  set adaSz [file size $::fa]
132  lappend results "Bytes before/after $adbSz/$adaSz"
133  set ::result [join $results " | "]
134} {cat,dog,pig | Bytes before/after 50/5145}
135
136do_test 1.3 {
137  set results {}
138  sqlite3 adb "file:$::fa?mode=rw$::vf" -uri 1
139  adb eval {
140    SELECT group_concat(a) as pets FROM (SELECT a FROM t1 ORDER BY a DESC);
141  } { lappend results $pets }
142  adb close
143  set ::result [join $results " | "]
144} {pig,dog,cat}
145
146do_test 1.4 {
147  set ::result [fosAvfs $fa]
148} {4096}
149
150do_test 2.1 {
151  set in [open $::fa r]
152  set tli {}
153  for {set i [llength $::tlo]} {$i > 0} {incr i -1} {
154    lappend tli [gets $in]
155  }
156  close $in
157  if { [join $tli ":"] ne [join $::tlo ":"] } {
158    set ::result "Appendee changed."
159  } else {
160    set ::result "Appendee intact."
161  }
162} {Appendee intact.}
163
164# Set of repeatable random integers for a couple tests.
165set ::nrint 50000
166proc rint {v} {
167  return [::tcl::mathfunc::int [expr $v * 100000]]
168}
169array set ::randints [list 0 [rint [::tcl::mathfunc::srand 0]]]
170for {set i 1} {$i < $::nrint} {incr i} {
171  set ::randints($i) [rint [::tcl::mathfunc::rand]]
172}
173
174do_test 3.1 {
175  set results {}
176  sqlite3 adb "file:$::fa?mode=rw$::vf" -uri 1
177  adb eval {
178    DROP TABLE t1;
179    PRAGMA cache_size=10;
180    CREATE TABLE ri (i INTEGER);
181    BEGIN;
182  }
183  for {set i 0} {$i < $::nrint} {incr i} {
184    set r $::randints($i)
185    set s $::randints([incr i])
186    set t $::randints([incr i])
187    set u $::randints([incr i])
188    set v $::randints([incr i])
189    adb eval {
190      INSERT INTO ri VALUES ($r),($s),($t),($u),($v)
191    }
192  }
193  adb eval {
194    COMMIT;
195    SELECT integrity_check as ic FROM pragma_integrity_check();
196  } { lappend results $ic }
197  set adbSz [file size $::fa]
198  set qr {}
199  adb eval {
200    SELECT count(*) as ic FROM ri;
201    DELETE FROM ri WHERE (i % 50) <> 25;
202    SELECT integrity_check as ic FROM pragma_integrity_check();
203    VACUUM;
204    SELECT integrity_check as ic FROM pragma_integrity_check();
205    SELECT count(*) as ic FROM ri;
206  } { lappend qr $ic }
207  adb close
208  set adaSz [file size $::fa]
209  set adba [expr ($adbSz + 0.1)/$adaSz]
210  # lappend results $adba
211  set results [concat $results [lrange $qr 0 2]]
212  lappend results [expr {$adba > 10.0}]
213  set ::result [join $results " | "]
214} "ok | $::nrint | ok | ok | 1"
215
216do_test 3.2 {
217  set results {}
218  sqlite3 adb "file:$::fa?mode=rw$::vf" -uri 1
219  adb eval {
220    SELECT integrity_check as ic FROM pragma_integrity_check();
221  } { lappend results $ic }
222  adb close
223  set ::result [join $results " | "]
224} {ok}
225
226# avfs-3.3. Test that appendvfs can grow by many pages and be written.
227do_test 3.3 {
228  set results {}
229  sqlite3 adb "file:$::fa?mode=rw$::vf" -uri 1
230  set npages 300
231  adb eval { BEGIN }
232  while {$npages > 0} {
233    adb eval { INSERT INTO ri VALUES (randomblob(1500)) }
234    incr npages -1
235  }
236  adb eval { COMMIT }
237  adb eval {
238    SELECT integrity_check as ic FROM pragma_integrity_check();
239  } { lappend results $ic }
240  adb close
241  set adaSzr [expr [file size $::fa] / 300.0 / 1500 ]
242  set okSzr [expr $adaSzr > 1.0 && $adaSzr < 1.3 ]
243  lappend results $okSzr
244  set ::result [join $results " | "]
245} {ok | 1}
246
247# avfs-3.4. Test that grown appendvfs can be reopened and appear intact.
248do_test 3.4 {
249  set results {}
250  sqlite3 adb "file:$::fa?mode=rw$::vf" -uri 1
251  adb eval {
252    SELECT integrity_check as ic FROM pragma_integrity_check();
253  } { lappend results $ic }
254  adb close
255  set ::result $ic
256} {ok}
257
258# avfs-3.5. Test that much grown appendvfs can shrink and reopen intact.
259do_test 3.5 {
260  set results {}
261  set adbsz [file size $::fa]
262  sqlite3 adb "file:$::fa?mode=rw$::vf" -uri 1
263  adb eval {
264    DELETE FROM ri WHERE rowid % 8 <> 0;
265    SELECT integrity_check as ic FROM pragma_integrity_check();
266    VACUUM;
267    SELECT integrity_check as ic FROM pragma_integrity_check();
268  } { lappend results $ic }
269  adb close
270  set adasz [file size $::fa]
271  lappend results [expr {$adbsz/$adasz > 5}]
272  sqlite3 adb "file:$::fa?mode=rw$::vf" -uri 1
273  adb eval {
274    SELECT integrity_check as ic FROM pragma_integrity_check();
275  } { lappend results $ic }
276  adb close
277  set ::result [join $results " | "]
278} {ok | ok | 1 | ok}
279
280set ::cliDoesAr [shellDoesAr]
281
282do_test 4.1 {
283  set shdo "sh_app1.sql"
284  set shod "sh_app1.adb"
285  forcedelete $shdo $shod
286  set ofd [open $shdo w]
287  if {$::cliDoesAr} {
288    puts $ofd ".ar -c"
289  } else {
290    puts $ofd "pragma page_size=512;"
291    puts $ofd "create table sqlar (a);"
292  }
293  puts $ofd ".tables"
294  puts $ofd ".q"
295  close $ofd
296  set ofd [open $shod wb]
297  puts $ofd "Some text."
298  close $ofd
299  set res [catchcmd "-append -batch -init $shdo $shod" ""]
300  lappend res [fosAvfs $shod]
301  forcedelete $shdo $shod
302  set ::result [join $res " | "]
303} {0 | sqlar | 4096}
304
305do_test 4.2 {
306  set shdo "sh_app1.sql"
307  set shod "sh_app1.adb"
308  forcedelete $shdo $shod
309  set ofd [open $shdo w]
310  if {$::cliDoesAr} {
311    puts $ofd ".ar -c"
312  } else {
313    puts $ofd "pragma page_size=512;"
314    puts $ofd "create table sqlar (a);"
315  }
316  puts $ofd ".tables"
317  puts $ofd ".q"
318  close $ofd
319  set ofd [open $shod wb]
320  close $ofd
321  set res [catchcmd "-append -batch -init $shdo $shod" ""]
322  lappend res [fosAvfs $shod]
323  forcedelete $shdo ; # Leave $shod for next test.
324  set ::result [join $res " | "]
325} {0 | sqlar | 0}
326
327do_test 4.3 {
328  set shdo "sh_app1.sql"
329  set shod "sh_app1.adb" ; # Same as test 4.2, reusing ADB.
330  forcedelete $shdo
331  set ofd [open $shdo w]
332  if {$::cliDoesAr} {
333    puts $ofd ".ar -u $shdo"
334    puts $ofd "select count(*) from sqlar where name = '$shdo';"
335  } else {
336    puts $ofd "insert into sqlar values (1);"
337    puts $ofd "select count(*) from sqlar;"
338  }
339  puts $ofd ".q"
340  close $ofd
341  set res [catchcmd "-append -batch -init $shdo $shod" ""]
342  sqlite3 adb "file:$shod?mode=rw$::vf" -uri 1
343  adb eval {
344    SELECT count(*) as n FROM sqlar
345  } { lappend res $n }
346  adb close
347  forcedelete $shdo $shod;
348  set ::result [join $res " | "]
349} {0 | 1 | 1}
350
351do_test 5.1 {
352  set fake "faketiny.sdb"
353  forcedelete $fake
354  set ofd [open $fake wb]
355  puts -nonewline $ofd "SQLite format 3"
356  puts -nonewline $ofd [binary format "c" 0]
357  puts -nonewline $ofd "Start-Of-SQLite3-"
358  puts -nonewline $ofd [binary format "W" 0]
359  close $ofd
360  if {[catch {sqlite3 adb "file:$fake?mode=rw$::vf" -uri 1}]} {
361    set res "Open failed."
362  } else {
363    adb close
364    set res "Opened when should not."
365  }
366  forcedelete $fake
367  set ::result $res
368} {Open failed.}
369
370do_test 5.2 {
371  set fake "faketiny.sdb"
372  forcedelete $fake
373  set ofd [open $fake wb]
374  set fakeAppendee "Dog ate my homework.\n"
375  puts -nonewline $ofd $fakeAppendee
376  puts -nonewline $ofd "SQLite format 3"
377  puts -nonewline $ofd [binary format "c" 0]
378  puts -nonewline $ofd "Start-Of-SQLite3-"
379  puts -nonewline $ofd [binary format "W" [string length $fakeAppendee]]
380  close $ofd
381  if {[catch {sqlite3 adb "file:$fake?mode=rw$::vf" -uri 1}]} {
382    set res "Open failed."
383  } else {
384    adb close
385    set res "Opened when should not."
386  }
387  forcedelete $fake
388  set ::result $res
389} {Open failed.}
390
391forcedelete $::fa $::fza
392
393unset -nocomplain ::fa ::fza ::tlo ::result ::randints ::nrint ::cliDoesAr
394
395finish_test
396