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