xref: /sqlite-3.40.0/test/shell5.test (revision 4f3557e4)
1# 2010 August 4
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# The focus of this file is testing the CLI shell tool.
13# These tests are specific to the .import command.
14#
15# $Id: shell5.test,v 1.7 2009/07/17 16:54:48 shaneh Exp $
16#
17
18# Test plan:
19#
20#   shell5-1.*: Basic tests specific to the ".import" command.
21#
22set testdir [file dirname $argv0]
23source $testdir/tester.tcl
24set CLI [test_find_cli]
25db close
26forcedelete test.db test.db-journal test.db-wal
27
28#----------------------------------------------------------------------------
29# Test cases shell5-1.*: Basic handling of the .import and .separator commands.
30#
31
32# .import FILE TABLE     Import data from FILE into TABLE
33do_test shell5-1.1.1 {
34  catchcmd "test.db" ".import"
35} {/1 .ERROR: missing FILE argument.*/}
36do_test shell5-1.1.2 {
37  catchcmd "test.db" ".import FOO"
38} {/1 .ERROR: missing TABLE argument.*/}
39do_test shell5-1.1.3 {
40  # too many arguments
41  catchcmd "test.db" ".import FOO BAR BAD"
42} {/1 .ERROR: extra argument.*/}
43
44# .separator STRING      Change separator used by output mode and .import
45do_test shell5-1.2.1 {
46  catchcmd "test.db" ".separator"
47} {1 {Usage: .separator COL ?ROW?}}
48do_test shell5-1.2.2 {
49  catchcmd "test.db" ".separator ONE"
50} {0 {}}
51do_test shell5-1.2.3 {
52  catchcmd "test.db" ".separator ONE TWO"
53} {0 {}}
54do_test shell5-1.2.4 {
55  # too many arguments
56  catchcmd "test.db" ".separator ONE TWO THREE"
57} {1 {Usage: .separator COL ?ROW?}}
58
59# column separator should default to "|"
60do_test shell5-1.3.1.1 {
61  set res [catchcmd "test.db" ".show"]
62  list [regexp {colseparator: \"\|\"} $res]
63} {1}
64
65# row separator should default to "\n"
66do_test shell5-1.3.1.2 {
67  set res [catchcmd "test.db" ".show"]
68  list [regexp {rowseparator: \"\\n\"} $res]
69} {1}
70
71# set separator to different value.
72# check that .show reports new value
73do_test shell5-1.3.2 {
74  set res [catchcmd "test.db" {.separator ,
75.show}]
76  list [regexp {separator: \",\"} $res]
77} {1}
78
79# import file doesn't exist
80do_test shell5-1.4.1 {
81  forcedelete FOO
82  set res [catchcmd "test.db" {CREATE TABLE t1(a, b);
83.import FOO t1}]
84} {1 {Error: cannot open "FOO"}}
85
86# empty import file
87do_test shell5-1.4.2 {
88  forcedelete shell5.csv
89  set in [open shell5.csv w]
90  close $in
91  set res [catchcmd "test.db" {.import shell5.csv t1
92SELECT COUNT(*) FROM t1;}]
93} {0 0}
94
95# import file with 1 row, 1 column (expecting 2 cols)
96do_test shell5-1.4.3 {
97  set in [open shell5.csv w]
98  puts $in "1"
99  close $in
100  set res [catchcmd "test.db" {.import shell5.csv t1}]
101} {1 {shell5.csv:1: expected 2 columns but found 1 - filling the rest with NULL}}
102
103# import file with 1 row, 3 columns (expecting 2 cols)
104do_test shell5-1.4.4 {
105  set in [open shell5.csv w]
106  puts $in "1|2|3"
107  close $in
108  set res [catchcmd "test.db" {.import shell5.csv t1}]
109} {1 {shell5.csv:1: expected 2 columns but found 3 - extras ignored}}
110
111# import file with 1 row, 2 columns
112do_test shell5-1.4.5 {
113  set in [open shell5.csv w]
114  puts $in "1|2"
115  close $in
116  set res [catchcmd "test.db" {DELETE FROM t1;
117.import shell5.csv t1
118SELECT COUNT(*) FROM t1;}]
119} {0 1}
120
121# import file with 2 rows, 2 columns
122# note we end up with 3 rows because of the 1 row
123# imported above.
124do_test shell5-1.4.6 {
125  set in [open shell5.csv w]
126  puts $in "2|3"
127  puts $in "3|4"
128  close $in
129  set res [catchcmd "test.db" {.import shell5.csv t1
130SELECT COUNT(*) FROM t1;}]
131} {0 3}
132
133# import file with 1 row, 2 columns, using a comma
134do_test shell5-1.4.7 {
135  set in [open shell5.csv w]
136  puts $in "4,5"
137  close $in
138  set res [catchcmd "test.db" {.separator ,
139.import shell5.csv t1
140SELECT COUNT(*) FROM t1;}]
141} {0 4}
142
143# import file with 1 row, 2 columns, text data
144do_test shell5-1.4.8.1 {
145  set in [open shell5.csv w]
146  puts $in "5|Now is the time for all good men to come to the aid of their country."
147  close $in
148  set res [catchcmd "test.db" {.import shell5.csv t1
149SELECT COUNT(*) FROM t1;}]
150} {0 5}
151
152do_test shell5-1.4.8.2 {
153  catchcmd "test.db" {SELECT b FROM t1 WHERE a='5';}
154} {0 {Now is the time for all good men to come to the aid of their country.}}
155
156# import file with 1 row, 2 columns, quoted text data
157# note that currently sqlite doesn't support quoted fields, and
158# imports the entire field, quotes and all.
159do_test shell5-1.4.9.1 {
160  set in [open shell5.csv w]
161  puts $in "6|'Now is the time for all good men to come to the aid of their country.'"
162  close $in
163  set res [catchcmd "test.db" {.import shell5.csv t1
164SELECT COUNT(*) FROM t1;}]
165} {0 6}
166
167do_test shell5-1.4.9.2 {
168  catchcmd "test.db" {SELECT b FROM t1 WHERE a='6';}
169} {0 {'Now is the time for all good men to come to the aid of their country.'}}
170
171# import file with 1 row, 2 columns, quoted text data
172do_test shell5-1.4.10.1 {
173  set in [open shell5.csv w]
174  puts $in "7|\"Now is the time for all good men to come to the aid of their country.\""
175  close $in
176  set res [catchcmd "test.db" {.import shell5.csv t1
177SELECT COUNT(*) FROM t1;}]
178} {0 7}
179
180do_test shell5-1.4.10.2 {
181  catchcmd "test.db" {SELECT b FROM t1 WHERE a='7';}
182} {0 {Now is the time for all good men to come to the aid of their country.}}
183
184# import file with 2 rows, 2 columns and an initial BOM
185#
186do_test shell5-1.4.11 {
187  set in [open shell5.csv wb]
188  puts -nonewline $in "\xef\xbb\xbf"
189  puts $in "2|3"
190  puts $in "4|5"
191  close $in
192  set res [catchcmd "test.db" {CREATE TABLE t2(x INT, y INT);
193.import shell5.csv t2
194.mode quote
195.header on
196SELECT * FROM t2;}]
197 string map {\n | \n\r |} $res
198} {0 {'x','y'|2,3|4,5}}
199
200# import file with 2 rows, 2 columns or text with an initial BOM
201#
202do_test shell5-1.4.12 {
203  set in [open shell5.csv wb]
204  puts $in "\xef\xbb\xbf\"two\"|3"
205  puts $in "4|5"
206  close $in
207  set res [catchcmd "test.db" {DELETE FROM t2;
208.import shell5.csv t2
209.mode quote
210.header on
211SELECT * FROM t2;}]
212 string map {\n | \n\r |} $res
213} {0 {'x','y'|'two',3|4,5}}
214
215# check importing very long field
216do_test shell5-1.5.1 {
217  set str [string repeat X 999]
218  set in [open shell5.csv w]
219  puts $in "8|$str"
220  close $in
221  set res [catchcmd "test.db" {.import shell5.csv t1
222SELECT length(b) FROM t1 WHERE a='8';}]
223} {0 999}
224
225# try importing into a table with a large number of columns.
226# This is limited by SQLITE_MAX_VARIABLE_NUMBER, which defaults to 999.
227set cols 999
228do_test shell5-1.6.1 {
229  set data {}
230  for {set i 1} {$i<$cols} {incr i} {
231    append data "c$i|"
232  }
233  append data "c$cols\n";
234  for {set i 1} {$i<$cols} {incr i} {
235    append data "$i|"
236  }
237  append data "$cols"
238  set in [open shell5.csv w]
239  puts $in $data
240  close $in
241  set res [catchcmd "test.db" {DROP TABLE IF EXISTS t2;
242.import shell5.csv t2
243SELECT COUNT(*) FROM t2;}]
244} {0 1}
245
246# try importing a large number of rows
247set rows 9999
248do_test shell5-1.7.1 {
249  set in [open shell5.csv w]
250  puts $in a
251  for {set i 1} {$i<=$rows} {incr i} {
252    puts $in $i
253  }
254  close $in
255  set res [catchcmd "test.db" {.mode csv
256.import shell5.csv t3
257SELECT COUNT(*) FROM t3;}]
258} [list 0 $rows]
259
260# Inport from a pipe.  (Unix only, as it requires "awk")
261if {$tcl_platform(platform)=="unix"} {
262  do_test shell5-1.8 {
263    forcedelete test.db
264    catchcmd test.db {.mode csv
265.import "|awk 'END{print \"x,y\";for(i=1;i<=5;i++){print i \",this is \" i}}'" t1
266SELECT * FROM t1;}
267  } {0 {1,"this is 1"
2682,"this is 2"
2693,"this is 3"
2704,"this is 4"
2715,"this is 5"}}
272}
273
274# Import columns containing quoted strings
275do_test shell5-1.9 {
276  set out [open shell5.csv w]
277  fconfigure $out -translation lf
278  puts $out {1,"",11}
279  puts $out {2,"x",22}
280  puts $out {3,"""",33}
281  puts $out {4,"hello",44}
282  puts $out "5,55,\"\"\r"
283  puts $out {6,66,"x"}
284  puts $out {7,77,""""}
285  puts $out {8,88,"hello"}
286  puts $out {"",9,99}
287  puts $out {"x",10,110}
288  puts $out {"""",11,121}
289  puts $out {"hello",12,132}
290  close $out
291  forcedelete test.db
292  catchcmd test.db {.mode csv
293    CREATE TABLE t1(a,b,c);
294.import shell5.csv t1
295  }
296  sqlite3 db test.db
297  db eval {SELECT *, '|' FROM t1 ORDER BY rowid}
298} {1 {} 11 | 2 x 22 | 3 {"} 33 | 4 hello 44 | 5 55 {} | 6 66 x | 7 77 {"} | 8 88 hello | {} 9 99 | x 10 110 | {"} 11 121 | hello 12 132 |}
299db close
300
301# Import columns containing quoted strings
302do_test shell5-1.10 {
303  set out [open shell5.csv w]
304  fconfigure $out -translation lf
305  puts $out {column1,column2,column3,column4}
306  puts $out "field1,field2,\"x3 \"\"\r\ndata\"\" 3\",field4"
307  puts $out "x1,x2,\"x3 \"\"\ndata\"\" 3\",x4"
308  close $out
309  forcedelete test.db
310  catchcmd test.db {.mode csv
311    CREATE TABLE t1(a,b,c,d);
312.import shell5.csv t1
313  }
314  sqlite3 db test.db
315  db eval {SELECT hex(c) FROM t1 ORDER BY rowid}
316} {636F6C756D6E33 783320220D0A64617461222033 783320220A64617461222033}
317
318# Blank last column with \r\n line endings.
319do_test shell5-1.11 {
320  set out [open shell5.csv w]
321  fconfigure $out -translation binary
322  puts $out "column1,column2,column3\r"
323  puts $out "a,b, \r"
324  puts $out "x,y,\r"
325  puts $out "p,q,r\r"
326  close $out
327  catch {db close}
328  forcedelete test.db
329  catchcmd test.db {.mode csv
330.import shell5.csv t1
331  }
332  sqlite3 db test.db
333  db eval {SELECT *, '|' FROM t1}
334} {a b { } | x y {} | p q r |}
335db close
336
337#----------------------------------------------------------------------------
338#
339reset_db
340sqlite3 db test.db
341do_test shell5-2.1 {
342  set fd [open shell5.csv w]
343  puts $fd ",hello"
344  close $fd
345  catchcmd test.db [string trim {
346.mode csv
347CREATE TABLE t1(a, b);
348.import shell5.csv t1
349  }]
350  db eval { SELECT * FROM t1 }
351} {{} hello}
352
353do_test shell5-2.2 {
354  set fd [open shell5.csv w]
355  puts $fd {"",hello}
356  close $fd
357  catchcmd test.db [string trim {
358.mode csv
359CREATE TABLE t2(a, b);
360.import shell5.csv t2
361  }]
362  db eval { SELECT * FROM t2 }
363} {{} hello}
364
365do_test shell5-2.3 {
366  set fd [open shell5.csv w]
367  puts $fd {"x""y",hello}
368  close $fd
369  catchcmd test.db [string trim {
370.mode csv
371CREATE TABLE t3(a, b);
372.import shell5.csv t3
373  }]
374  db eval { SELECT * FROM t3 }
375} {x\"y hello}
376
377do_test shell5-2.4 {
378  set fd [open shell5.csv w]
379  puts $fd {"xy""",hello}
380  close $fd
381  catchcmd test.db [string trim {
382.mode csv
383CREATE TABLE t4(a, b);
384.import shell5.csv t4
385  }]
386  db eval { SELECT * FROM t4 }
387} {xy\" hello}
388
389do_test shell5-2.5 {
390  set fd [open shell5.csv w]
391  puts $fd {"one","2"}
392  puts $fd {}
393  close $fd
394  catchcmd test.db [string trim {
395.mode csv
396CREATE TABLE t4(a, b);
397.import shell5.csv t4
398  }]
399  db eval { SELECT * FROM t4 }
400} {xy\" hello one 2 {} {}}
401
402#----------------------------------------------------------------------------
403# Tests for the shell "ascii" import/export mode.
404#
405do_test shell5-3.1 {
406  set fd [open shell5.csv w]
407  fconfigure $fd -encoding binary -translation binary
408  puts -nonewline $fd "\"test 1\"\x1F,test 2\r\n\x1E"
409  puts -nonewline $fd "test 3\x1Ftest 4\n"
410  close $fd
411  catchcmd test.db {
412.mode ascii
413CREATE TABLE t5(a, b);
414.import shell5.csv t5
415  }
416  db eval { SELECT * FROM t5 }
417} "\{\"test 1\"} \{,test 2\r\n\} \{test 3\} \{test 4\n\}"
418
419do_test shell5-3.2 {
420  set x [catchcmd test.db {
421.mode ascii
422SELECT * FROM t5;
423  }]
424  # Handle platform end-of-line differences
425  regsub -all {[\n\r]?\n} $x <EOL> x
426  set x
427} "0 \{\"test 1\"\x1F,test 2<EOL>\x1Etest 3\x1Ftest 4<EOL>\x1E\}"
428
429do_test shell5-4.1 {
430  forcedelete shell5.csv
431  set fd [open shell5.csv w]
432  puts $fd "1,2,3"
433  puts $fd "4,5"
434  puts $fd "6,7,8"
435  close $fd
436  catchcmd test.db [string trim {
437.mode csv
438CREATE TABLE t6(a, b, c);
439.import shell5.csv t6
440  }]
441  db eval { SELECT * FROM t6 ORDER BY a }
442} {1 2 3 4 5 {} 6 7 8}
443
444do_test shell5-4.2 {
445  forcedelete shell5.csv
446  set fd [open shell5.csv w]
447  puts $fd "1,2,3"
448  puts $fd "4,5"
449  puts $fd "6,7,8,9"
450  close $fd
451  catchcmd test.db [string trim {
452.mode csv
453CREATE TABLE t7(a, b, c);
454.import shell5.csv t7
455  }]
456  db eval { SELECT * FROM t7 ORDER BY a }
457} {1 2 3 4 5 {} 6 7 8}
458
459do_test shell5-4.3 {
460  forcedelete shell5.csv
461  set fd [open shell5.csv w]
462  puts $fd ",,"
463  puts $fd "1,2,3"
464  close $fd
465  catchcmd test.db [string trim {
466.mode csv
467CREATE TABLE t8(a, b, c);
468.import -skip 1 shell5.csv t8
469.nullvalue #
470  }]
471  db eval { SELECT * FROM t8 }
472} {1 2 3}
473
474finish_test
475