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