xref: /sqlite-3.40.0/test/zipfile.test (revision 41a6f2cb)
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 {rawdata must be NULL}}
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 {sz must be NULL}}
158do_catchsql_test 1.1.0.3 {
159  INSERT INTO zz(name, mtime, rawdata, method)
160  VALUES('g.txt', 1000000002, '12345', 0);
161} {1 {rawdata must be NULL}}
162do_catchsql_test 1.1.0.4 {
163  INSERT INTO zz(name, data, method)
164  VALUES('g.txt', '12345', 7);
165} {1 {unknown compression method: 7}}
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 {zipfile: mode does not match data}}
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  } [list 1 "duplicate name: \"$fname/\""]
402  do_catchsql_test 3.1.$tn.1 {
403    INSERT INTO x1(name, data) VALUES($fname || '/', NULL);
404  } [list 1 "duplicate name: \"$fname/\""]
405  do_catchsql_test 3.1.$tn.2 {
406    INSERT INTO x1(name, data) VALUES($fname, 'abcd');
407  } [list 1 "duplicate name: \"$fname\""]
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
443do_catchsql_test 4.6 {
444  WITH c(name,data) AS ( SELECT 'a.txt', 'abc')
445  SELECT zipfile(name) FROM c
446} {1 {wrong number of arguments to function zipfile()}}
447
448do_catchsql_test 4.7 {
449  WITH c(name,data) AS (
450    SELECT 'a.txt', 'abc' UNION ALL
451    SELECT NULL, 'def'
452  )
453  SELECT zipfile(name,data) FROM c
454} {1 {first argument to zipfile() must be non-NULL}}
455
456do_catchsql_test 4.7 {
457  WITH c(name,data,method) AS (
458    SELECT 'a.txt', 'abc', 0
459    UNION SELECT 'b.txt', 'def', 8
460    UNION SELECT 'c.txt', 'ghi', 16
461  )
462  SELECT zipfile(name,NULL,NULL,data,method) FROM c
463} {1 {illegal method value: 16}}
464
465do_catchsql_test 4.8 {
466  WITH c(name,data) AS (
467    SELECT 'a.txt', 'abc'
468    UNION SELECT 'b.txt', 'def'
469    UNION SELECT 'c.txt/', 'ghi'
470  )
471  SELECT zipfile(name,NULL,NULL,data) FROM c
472} {1 {non-directory name must not end with /}}
473
474#--------------------------------------------------------------------------
475
476db func rt remove_timestamps
477do_execsql_test 5.0 {
478  WITH c(name,mtime,data) AS (
479    SELECT 'a.txt', 946684800, 'abc'
480  )
481  SELECT name,mtime,data FROM zipfile(
482    ( SELECT rt( zipfile(name,NULL,mtime,data,NULL) ) FROM c )
483  )
484} {
485  a.txt 946684800 abc
486}
487
488if {[info vars ::UNZIP]!=""} {
489ifcapable datetime {
490  load_static_extension db fileio
491  forcedelete test1.zip test2.zip
492  do_test 6.0 {
493    execsql {
494      WITH c(name,mtime,data) AS (
495        SELECT 'a.txt', 946684800, 'abc' UNION ALL
496        SELECT 'b.txt', 1000000000, 'abc' UNION ALL
497        SELECT 'c.txt', 1111111000, 'abc'
498      )
499      SELECT writefile('test1.zip', rt( zipfile(name, NULL, mtime, data) ) ),
500             writefile('test2.zip',   ( zipfile(name, NULL, mtime, data) ) )
501      FROM c;
502    }
503    forcedelete test_unzip
504    file mkdir test_unzip
505    exec unzip -d test_unzip test1.zip
506
507    db eval {
508      SELECT name, strftime('%s', mtime, 'unixepoch', 'localtime')
509      FROM fsdir('test_unzip') WHERE name!='test_unzip'
510      ORDER BY name
511    }
512  } [list {*}{
513    test_unzip/a.txt 946684800
514    test_unzip/b.txt 1000000000
515    test_unzip/c.txt 1111111000
516  }]
517
518  do_execsql_test 6.1 {
519    SELECT name, mtime, data FROM zipfile('test1.zip')
520  } {
521    a.txt 946684800   abc
522    b.txt 1000000000  abc
523    c.txt 1111111000  abc
524  }
525
526  do_test 6.2 {
527    forcedelete test_unzip
528    file mkdir test_unzip
529    exec unzip -d test_unzip test2.zip
530
531    db eval {
532      SELECT name, mtime
533      FROM fsdir('test_unzip') WHERE name!='test_unzip'
534      ORDER BY name
535    }
536  } [list {*}{
537    test_unzip/a.txt 946684800
538    test_unzip/b.txt 1000000000
539    test_unzip/c.txt 1111111000
540  }]
541
542  do_execsql_test 6.3 {
543    SELECT name, mtime, sz, rawdata, data FROM zipfile('test2.zip')
544  } {
545    a.txt 946684800   3 abc abc
546    b.txt 1000000000  3 abc abc
547    c.txt 1111111000  3 abc abc
548  }
549}
550}
551
552#-------------------------------------------------------------------------
553# Force an IO error by truncating the zip archive to zero bytes in size
554# while it is being read.
555forcedelete test.zip
556do_test 7.0 {
557  execsql {
558    WITH c(name,data) AS (
559        SELECT '1', randomblob(1000000) UNION ALL
560        SELECT '2', randomblob(1000000) UNION ALL
561        SELECT '3', randomblob(1000000)
562    )
563    SELECT writefile('test.zip', zipfile(name, data) ) FROM c;
564  }
565
566  list [catch {
567    db eval { SELECT name, data FROM zipfile('test.zip') } {
568      if {$name==2} { close [open test.zip w+] }
569    }
570  } msg] $msg
571} {1 {error in fread()}}
572
573forcedelete test.zip
574do_execsql_test 8.0.1 {
575  CREATE VIRTUAL TABLE zz USING zipfile('test.zip');
576  BEGIN;
577    INSERT INTO zz(name, data) VALUES('a.txt', '1');
578    INSERT INTO zz(name, data) VALUES('b.txt', '2');
579    INSERT INTO zz(name, data) VALUES('c.txt', '1');
580    INSERT INTO zz(name, data) VALUES('d.txt', '2');
581    SELECT name, data FROM zz;
582} {
583  a.txt 1 b.txt 2 c.txt 1 d.txt 2
584}
585do_test 8.0.2 {
586  db eval { SELECT name, data FROM zz } {
587    if { $data=="2" } { db eval { DELETE FROM zz WHERE name=$name } }
588  }
589  execsql { SELECT name, data FROM zz }
590} {a.txt 1 c.txt 1}
591do_test 8.0.3 {
592  db eval { SELECT name, data FROM zz } {
593    db eval { DELETE FROM zz WHERE name=$name }
594  }
595  execsql { SELECT name, data FROM zz }
596} {}
597execsql COMMIT
598
599do_execsql_test 8.1.1 {
600  CREATE VIRTUAL TABLE nogood USING zipfile('test_unzip');
601}
602do_catchsql_test 8.1.2 {
603  INSERT INTO nogood(name, data) VALUES('abc', 'def');
604} {1 {zipfile: failed to open file test_unzip for writing}}
605
606do_execsql_test 8.2.1 {
607  DROP TABLE nogood;
608  BEGIN;
609    CREATE VIRTUAL TABLE nogood USING zipfile('test_unzip');
610}
611do_catchsql_test 8.2.2 {
612    INSERT INTO nogood(name, data) VALUES('abc', 'def');
613} {1 {zipfile: failed to open file test_unzip for writing}}
614do_execsql_test 8.2.3 {
615  COMMIT;
616}
617
618forcedelete test.zip
619do_execsql_test 8.3.1 {
620  BEGIN;
621    CREATE VIRTUAL TABLE ok USING zipfile('test.zip');
622    INSERT INTO ok(name, data) VALUES ('sqlite3', 'elf');
623  COMMIT;
624}
625
626#-------------------------------------------------------------------------
627# Test that the zipfile aggregate correctly adds and removes "/" from
628# the ends of directory file names.
629do_execsql_test 9.0 {
630  WITH src(nm) AS (
631    VALUES('dir1') UNION ALL
632    VALUES('dir2/') UNION ALL
633    VALUES('dir3//') UNION ALL
634    VALUES('dir4///') UNION ALL
635    VALUES('/')
636  )
637  SELECT name FROM zipfile((SELECT zipfile(nm, NULL) FROM src))
638} {dir1/ dir2/ dir3/ dir4/ /}
639finish_test
640