1# 2017 December 9 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 13set testdir [file dirname $argv0] 14source $testdir/tester.tcl 15set testprefix zipfile 16 17ifcapable !vtab { 18 finish_test; return 19} 20if {[catch {load_static_extension db zipfile} error]} { 21 puts "Skipping zipfile tests, hit load error: $error" 22 finish_test; return 23} 24 25proc readfile {f} { 26 set fd [open $f] 27 fconfigure $fd -translation binary -encoding binary 28 set data [read $fd] 29 close $fd 30 set data 31} 32 33if {$::tcl_platform(platform)=="unix" && [catch {exec unzip}]==0} { 34 set ::UNZIP 1 35 load_static_extension db fileio 36 proc do_unzip {file} { 37 forcedelete test_unzip 38 file mkdir test_unzip 39 exec unzip -d test_unzip $file 40 41 set res [db eval { 42 SELECT replace(name,'test_unzip/',''),mode,mtime,data 43 FROM fsdir('test_unzip') 44 WHERE name!='test_unzip' 45 ORDER BY name 46 }] 47 set res 48 } 49} 50 51 52# The argument is a blob (not a hex string) containing a zip archive. 53# This proc removes the extended timestamp fields from the archive 54# and returns the result. 55# 56proc remove_timestamps {blob} { 57 set hex [binary encode hex $blob] 58 set hex [string map {55540500 00000500} $hex] 59 binary decode hex $hex 60} 61 62 63# Argument $file is the name of a zip archive on disk. This function 64# executes test cases to check that the results of each of the following 65# are the same: 66# 67# SELECT * FROM zipfile($file) 68# SELECT * FROM zipfile( readfile($file) ) 69# SELECT * FROM zipfile( 70# (SELECT zipfile(name,mode,mtime,data,method) FROM zipfile($file)) 71# ) 72# 73proc do_zipfile_blob_test {tn file} { 74 75 db func r readfile 76 set q1 {SELECT name,mode,mtime,method,quote(data) FROM zipfile($file)} 77 set q2 {SELECT name,mode,mtime,method,quote(data) FROM zipfile( r($file) )} 78 set q3 {SELECT name,mode,mtime,method,quote(data) FROM zipfile( 79 ( SELECT zipfile(name,mode,mtime,data,method) FROM zipfile($file) ) 80 )} 81 82 83 set r1 [db eval $q1] 84 set r2 [db eval $q2] 85 set r3 [db eval $q3] 86 #puts $r1 87 #puts $r2 88 #puts $r3 89 90 uplevel [list do_test $tn.1 [list set {} $r2] $r1] 91 uplevel [list do_test $tn.2 [list set {} $r3] $r1] 92} 93 94# Argument $file is a zip file on disk. This command runs tests to: 95# 96# 1. Unpack the archive with unix command [unzip] and compare the 97# results to reading the same archive using the zipfile() table 98# valued function. 99# 100# 2. Creates a new archive with the same contents using the zipfile() 101# aggregate function as follows: 102# 103# SELECT writefile('test_unzip.zip', 104# ( SELECT zipfile(name,mode,mtime,data,method) FROM zipfile($file) ) 105# ); 106# 107# Then tests that unpacking the new archive using [unzip] produces 108# the same results as in (1). 109# 110proc do_unzip_test {tn file} { 111 if {[info vars ::UNZIP]==""} { return } 112 db func sss strip_slash 113 114 db eval { 115 SELECT writefile('test_unzip.zip', 116 ( SELECT zipfile(name,mode,mtime,data,method) FROM zipfile($file) ) 117 ); 118 } 119 120 set r1 [db eval { 121 SELECT sss(name),mode,mtime,data FROM zipfile($file) ORDER BY name 122 }] 123 set r2 [do_unzip $file] 124 set r3 [do_unzip test_unzip.zip] 125 126 uplevel [list do_test $tn.1 [list set {} $r2] $r1] 127 uplevel [list do_test $tn.2 [list set {} $r3] $r1] 128} 129proc strip_slash {in} { regsub {/$} $in {} } 130 131proc do_zip_tests {tn file} { 132 uplevel do_zipfile_blob_test $tn.1 $file 133 uplevel do_unzip_test $tn.2 $file 134} 135 136forcedelete test.zip 137do_execsql_test 1.0 { 138 CREATE VIRTUAL TABLE temp.zz USING zipfile('test.zip'); 139 PRAGMA table_info(zz); 140} { 141 0 name {} 1 {} 1 142 1 mode {} 0 {} 0 143 2 mtime {} 0 {} 0 144 3 sz {} 0 {} 0 145 4 rawdata {} 0 {} 0 146 5 data {} 0 {} 0 147 6 method {} 0 {} 0 148} 149 150do_catchsql_test 1.1.0.1 { 151 INSERT INTO zz(name, mode, mtime, sz, rawdata, method) 152 VALUES('f.txt', '-rw-r--r--', 1000000000, 5, 'abcde', 0); 153} {1 {constraint failed}} 154do_catchsql_test 1.1.0.2 { 155 INSERT INTO zz(name, mtime, sz, data, method) 156 VALUES('g.txt', 1000000002, 5, '12345', 0); 157} {1 {constraint failed}} 158do_catchsql_test 1.1.0.3 { 159 INSERT INTO zz(name, mtime, rawdata, method) 160 VALUES('g.txt', 1000000002, '12345', 0); 161} {1 {constraint failed}} 162do_catchsql_test 1.1.0.4 { 163 INSERT INTO zz(name, data, method) 164 VALUES('g.txt', '12345', 7); 165} {1 {constraint failed}} 166 167do_execsql_test 1.1.1 { 168 INSERT INTO zz(name, mode, mtime, data, method) 169 VALUES('f.txt', '-rw-r--r--', 1000000000, 'abcde', 0); 170} 171do_execsql_test 1.1.2 { 172 INSERT INTO zz(name, mode, mtime, data, method) 173 VALUES('g.txt', NULL, 1000000002, '12345', 0); 174} 175 176do_execsql_test 1.2 { 177 SELECT name, mtime, data FROM zipfile('test.zip') 178} { 179 f.txt 1000000000 abcde 180 g.txt 1000000002 12345 181} 182do_zip_tests 1.2a test.zip 183 184do_execsql_test 1.3 { 185 INSERT INTO zz(name, mode, mtime, data) VALUES('h.txt', 186 '-rw-r--r--', 1000000004, 'aaaaaaaaaabbbbbbbbbb' 187 ); 188} 189do_zip_tests 1.3a test.zip 190 191do_execsql_test 1.4 { 192 SELECT name, mtime, data, method FROM zipfile('test.zip'); 193} { 194 f.txt 1000000000 abcde 0 195 g.txt 1000000002 12345 0 196 h.txt 1000000004 aaaaaaaaaabbbbbbbbbb 8 197} 198 199ifcapable json1 { 200 do_execsql_test 1.4.1 { 201 SELECT name, json_extract( zipfile_cds(z) , '$.crc32')!=0 202 FROM zipfile('test.zip'); 203 } { 204 f.txt 1 205 g.txt 1 206 h.txt 1 207 } 208} 209do_catchsql_test 1.4.2 { 210 SELECT zipfile_cds(mode) FROM zipfile('test.zip'); 211} {0 {{} {} {}}} 212 213do_execsql_test 1.5.1 { 214 BEGIN; 215 INSERT INTO zz(name, mode, mtime, data, method) 216 VALUES('i.txt', '-rw-r--r--', 1000000006, 'zxcvb', 0); 217 SELECT name FROM zz; 218 COMMIT; 219} {f.txt g.txt h.txt i.txt} 220do_execsql_test 1.5.2 { 221 SELECT name FROM zz; 222} {f.txt g.txt h.txt i.txt} 223do_execsql_test 1.5.3 { 224 SELECT data FROM zz WHERE name='i.txt'; 225} {zxcvb} 226 227do_execsql_test 1.6.0 { 228 DELETE FROM zz WHERE name='g.txt'; 229 SELECT name FROM zz; 230} {f.txt h.txt i.txt} 231 232do_execsql_test 1.6.1 { 233 SELECT name, mode, mtime, data, method FROM zipfile('test.zip'); 234} { 235 f.txt 33188 1000000000 abcde 0 236 h.txt 33188 1000000004 aaaaaaaaaabbbbbbbbbb 8 237 i.txt 33188 1000000006 zxcvb 0 238} 239do_zip_tests 1.6.1a test.zip 240 241do_execsql_test 1.6.2 { 242 UPDATE zz SET mtime=4 WHERE name='i.txt'; 243 SELECT name, mode, mtime, data, method FROM zipfile('test.zip'); 244} { 245 f.txt 33188 1000000000 abcde 0 246 h.txt 33188 1000000004 aaaaaaaaaabbbbbbbbbb 8 247 i.txt 33188 4 zxcvb 0 248} 249 250do_execsql_test 1.6.3 { 251 UPDATE zz SET mode='-rw-r--r-x' WHERE name='h.txt'; 252 SELECT name, mode, mtime, data, method FROM zipfile('test.zip'); 253} { 254 f.txt 33188 1000000000 abcde 0 255 h.txt 33189 1000000004 aaaaaaaaaabbbbbbbbbb 8 256 i.txt 33188 4 zxcvb 0 257} 258do_zip_tests 1.6.3a test.zip 259 260do_execsql_test 1.6.4 { 261 UPDATE zz SET name = 'blue.txt' WHERE name='f.txt'; 262 SELECT name, mode, mtime, data, method FROM zipfile('test.zip'); 263} { 264 blue.txt 33188 1000000000 abcde 0 265 h.txt 33189 1000000004 aaaaaaaaaabbbbbbbbbb 8 266 i.txt 33188 4 zxcvb 0 267} 268do_zip_tests 1.6.4a test.zip 269 270do_execsql_test 1.6.5 { 271 UPDATE zz SET data = 'edcba' WHERE name='blue.txt'; 272 SELECT name, mode, mtime, data, method FROM zipfile('test.zip'); 273} { 274 blue.txt 33188 1000000000 edcba 0 275 h.txt 33189 1000000004 aaaaaaaaaabbbbbbbbbb 8 276 i.txt 33188 4 zxcvb 0 277} 278 279do_execsql_test 1.6.6 { 280 UPDATE zz SET mode=NULL, data = NULL WHERE name='blue.txt'; 281 SELECT name, mode, mtime, data, method FROM zipfile('test.zip'); 282} { 283 blue.txt/ 16877 1000000000 {} 0 284 h.txt 33189 1000000004 aaaaaaaaaabbbbbbbbbb 8 285 i.txt 33188 4 zxcvb 0 286} 287 288do_catchsql_test 1.6.7 { 289 UPDATE zz SET data=NULL WHERE name='i.txt' 290} {1 {constraint failed}} 291do_execsql_test 1.6.8 { 292 SELECT name, mode, mtime, data, method FROM zipfile('test.zip'); 293} { 294 blue.txt/ 16877 1000000000 {} 0 295 h.txt 33189 1000000004 aaaaaaaaaabbbbbbbbbb 8 296 i.txt 33188 4 zxcvb 0 297} 298 299do_execsql_test 1.6.8 { 300 UPDATE zz SET data = '' WHERE name='i.txt'; 301 SELECT name,mode,mtime,data,method from zipfile('test.zip'); 302} { 303 blue.txt/ 16877 1000000000 {} 0 304 h.txt 33189 1000000004 aaaaaaaaaabbbbbbbbbb 8 305 i.txt 33188 4 {} 0 306} 307 308do_execsql_test 1.6.9 { 309 SELECT a.name, a.data 310 FROM zz AS a, zz AS b 311 WHERE a.name=+b.name AND +a.mode=b.mode 312} { 313 blue.txt/ {} 314 h.txt aaaaaaaaaabbbbbbbbbb 315 i.txt {} 316} 317 318do_execsql_test 1.6.10 { 319 SELECT name, data FROM zz WHERE name LIKE '%txt' 320} { 321 h.txt aaaaaaaaaabbbbbbbbbb 322 i.txt {} 323} 324 325do_execsql_test 1.7 { 326 DELETE FROM zz; 327 SELECT * FROM zz; 328} {} 329 330#------------------------------------------------------------------------- 331db close 332forcedelete test.zip 333reset_db 334load_static_extension db fileio 335load_static_extension db zipfile 336do_execsql_test 2.1 { 337 CREATE VIRTUAL TABLE zzz USING zipfile('test.zip'); 338 INSERT INTO zzz(name, mode) VALUES('dirname', 'drwxr-xr-x'); 339 SELECT name, mode, data FROM zzz; 340} {dirname/ 16877 {}} 341do_execsql_test 2.2 { 342 INSERT INTO zzz(name, data) VALUES('dirname2', NULL); 343 INSERT INTO zzz(name, data) VALUES('dirname2/file1.txt', 'abcdefghijklmnop'); 344 SELECT name, mode, data FROM zzz; 345} { 346 dirname/ 16877 {} 347 dirname2/ 16877 {} 348 dirname2/file1.txt 33188 abcdefghijklmnop 349} 350 351do_catchsql_test 2.3 { 352 UPDATE zzz SET name = 'dirname3' WHERE name = 'dirname/'; 353} {0 {}} 354do_execsql_test 2.4 { 355 SELECT name, mode, data FROM zzz; 356} { 357 dirname3/ 16877 {} 358 dirname2/ 16877 {} 359 dirname2/file1.txt 33188 abcdefghijklmnop 360} 361do_zip_tests 2.4a test.zip 362 363# If on unix, check that the [unzip] utility can unpack our archive. 364# 365if {$::tcl_platform(platform)=="unix"} { 366 do_test 2.5.1 { 367 forcedelete dirname 368 forcedelete dirname2 369 set rc [catch { exec unzip test.zip > /dev/null } msg] 370 list $rc $msg 371 } {0 {}} 372 do_test 2.5.2 { file isdir dirname3 } 1 373 do_test 2.5.3 { file isdir dirname2 } 1 374 do_test 2.5.4 { file isdir dirname2/file1.txt } 0 375 do_test 2.5.5 { 376 set fd [open dirname2/file1.txt] 377 set data [read $fd] 378 close $fd 379 set data 380 } {abcdefghijklmnop} 381} 382 383#------------------------------------------------------------------------- 384reset_db 385forcedelete test.zip 386load_static_extension db zipfile 387 388do_execsql_test 3.0 { 389 CREATE VIRTUAL TABLE temp.x1 USING zipfile('test.zip'); 390 INSERT INTO x1(name, data) VALUES('dir1/', NULL); 391 INSERT INTO x1(name, data) VALUES('file1', '1234'); 392 INSERT INTO x1(name, data) VALUES('dir1/file2', '5678'); 393} 394foreach {tn fname} { 395 1 dir1 396 2 file1 397 3 dir1/file2 398} { 399 do_catchsql_test 3.1.$tn.0 { 400 INSERT INTO x1(name, data) VALUES($fname, NULL); 401 } {1 {constraint failed}} 402 do_catchsql_test 3.1.$tn.1 { 403 INSERT INTO x1(name, data) VALUES($fname || '/', NULL); 404 } {1 {constraint failed}} 405 do_catchsql_test 3.1.$tn.2 { 406 INSERT INTO x1(name, data) VALUES($fname, 'abcd'); 407 } {1 {constraint failed}} 408} 409 410do_catchsql_test 3.2 { 411 SELECT rowid FROM x1 412} {1 {no such column: rowid}} 413 414#------------------------------------------------------------------------- 415# Test some error conditions. 416# 417do_catchsql_test 4.1 { 418 CREATE VIRTUAL TABLE yyy USING zipfile(); 419} {1 {zipfile constructor requires one argument}} 420do_catchsql_test 4.2 { 421 CREATE VIRTUAL TABLE yyy USING zipfile('test.zip', 'test.zip'); 422} {1 {zipfile constructor requires one argument}} 423 424do_catchsql_test 4.3 { 425 SELECT * FROM zipfile() 426} {1 {zipfile() function requires an argument}} 427 428do_catchsql_test 4.4 { 429 SELECT * FROM zipfile('/path/that/does/not/exist') 430} {1 {cannot open file: /path/that/does/not/exist}} 431 432foreach {tn mode} { 433 1 abcd 434 2 brwxrwxrwx 435 3 lrwxrrxrwx 436} { 437 do_catchsql_test 4.5.$tn { 438 WITH m(m) AS ( SELECT $mode) 439 SELECT zipfile('a.txt', m, 1000, 'xyz') FROM m 440 } [list 1 "zipfile: parse error in mode: $mode"] 441} 442 443#-------------------------------------------------------------------------- 444 445db func rt remove_timestamps 446do_execsql_test 5.0 { 447 WITH c(name,mtime,data) AS ( 448 SELECT 'a.txt', 946684800, 'abc' 449 ) 450 SELECT name,mtime,data FROM zipfile( 451 ( SELECT rt( zipfile(name,NULL,mtime,data) ) FROM c ) 452 ) 453} { 454 a.txt 946684800 abc 455} 456 457if {[info vars ::UNZIP]!=""} { 458ifcapable datetime { 459 load_static_extension db fileio 460 forcedelete test1.zip test2.zip 461 do_test 6.0 { 462 execsql { 463 WITH c(name,mtime,data) AS ( 464 SELECT 'a.txt', 946684800, 'abc' UNION ALL 465 SELECT 'b.txt', 1000000000, 'abc' UNION ALL 466 SELECT 'c.txt', 1111111000, 'abc' 467 ) 468 SELECT writefile('test1.zip', rt( zipfile(name, NULL, mtime, data) ) ), 469 writefile('test2.zip', ( zipfile(name, NULL, mtime, data) ) ) 470 FROM c; 471 } 472 forcedelete test_unzip 473 file mkdir test_unzip 474 exec unzip -d test_unzip test1.zip 475 476 db eval { 477 SELECT name, strftime('%s', mtime, 'unixepoch', 'localtime') 478 FROM fsdir('test_unzip') WHERE name!='test_unzip' 479 ORDER BY name 480 } 481 } [list {*}{ 482 test_unzip/a.txt 946684800 483 test_unzip/b.txt 1000000000 484 test_unzip/c.txt 1111111000 485 }] 486 487 do_execsql_test 6.1 { 488 SELECT name, mtime, data FROM zipfile('test1.zip') 489 } { 490 a.txt 946684800 abc 491 b.txt 1000000000 abc 492 c.txt 1111111000 abc 493 } 494 495 do_test 6.2 { 496 forcedelete test_unzip 497 file mkdir test_unzip 498 exec unzip -d test_unzip test2.zip 499 500 db eval { 501 SELECT name, mtime 502 FROM fsdir('test_unzip') WHERE name!='test_unzip' 503 ORDER BY name 504 } 505 } [list {*}{ 506 test_unzip/a.txt 946684800 507 test_unzip/b.txt 1000000000 508 test_unzip/c.txt 1111111000 509 }] 510 511 do_execsql_test 6.3 { 512 SELECT name, mtime, sz, rawdata, data FROM zipfile('test2.zip') 513 } { 514 a.txt 946684800 3 abc abc 515 b.txt 1000000000 3 abc abc 516 c.txt 1111111000 3 abc abc 517 } 518} 519} 520 521#------------------------------------------------------------------------- 522# Force an IO error by truncating the zip archive to zero bytes in size 523# while it is being read. 524forcedelete test.zip 525do_test 7.0 { 526 execsql { 527 WITH c(name,data) AS ( 528 SELECT '1', randomblob(1000000) UNION ALL 529 SELECT '2', randomblob(1000000) UNION ALL 530 SELECT '3', randomblob(1000000) 531 ) 532 SELECT writefile('test.zip', zipfile(name, data) ) FROM c; 533 } 534 535 list [catch { 536 db eval { SELECT name, data FROM zipfile('test.zip') } { 537 if {$name==2} { close [open test.zip w+] } 538 } 539 } msg] $msg 540} {1 {error in fread()}} 541 542forcedelete test.zip 543do_execsql_test 8.0.1 { 544 CREATE VIRTUAL TABLE zz USING zipfile('test.zip'); 545 BEGIN; 546 INSERT INTO zz(name, data) VALUES('a.txt', '1'); 547 INSERT INTO zz(name, data) VALUES('b.txt', '2'); 548 INSERT INTO zz(name, data) VALUES('c.txt', '1'); 549 INSERT INTO zz(name, data) VALUES('d.txt', '2'); 550 SELECT name, data FROM zz; 551} { 552 a.txt 1 b.txt 2 c.txt 1 d.txt 2 553} 554do_test 8.0.2 { 555 db eval { SELECT name, data FROM zz } { 556 if { $data=="2" } { db eval { DELETE FROM zz WHERE name=$name } } 557 } 558 execsql { SELECT name, data FROM zz } 559} {a.txt 1 c.txt 1} 560do_test 8.0.3 { 561 db eval { SELECT name, data FROM zz } { 562 db eval { DELETE FROM zz WHERE name=$name } 563 } 564 execsql { SELECT name, data FROM zz } 565} {} 566execsql COMMIT 567 568do_execsql_test 8.1.1 { 569 CREATE VIRTUAL TABLE nogood USING zipfile('test_unzip'); 570} 571do_catchsql_test 8.1.2 { 572 INSERT INTO nogood(name, data) VALUES('abc', 'def'); 573} {1 {zipfile: failed to open file test_unzip for writing}} 574 575do_execsql_test 8.2.1 { 576 DROP TABLE nogood; 577 BEGIN; 578 CREATE VIRTUAL TABLE nogood USING zipfile('test_unzip'); 579} 580do_catchsql_test 8.2.2 { 581 INSERT INTO nogood(name, data) VALUES('abc', 'def'); 582} {1 {zipfile: failed to open file test_unzip for writing}} 583do_execsql_test 8.2.3 { 584 COMMIT; 585} 586 587forcedelete test.zip 588do_execsql_test 8.3.1 { 589 BEGIN; 590 CREATE VIRTUAL TABLE ok USING zipfile('test.zip'); 591 INSERT INTO ok(name, data) VALUES ('sqlite3', 'elf'); 592 COMMIT; 593} 594 595finish_test 596 597