From: drh <> Date: Sun, 28 Dec 2025 14:01:52 +0000 (+0000) Subject: Improvements to the .import command of the CLI. X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=38c80149408b6e8810491f4182b429d821ccd3c4;p=thirdparty%2Fsqlite.git Improvements to the .import command of the CLI. FossilOrigin-Name: 436ed7937bcd3b5781539d883ff2957b81d74abccb75f65a2ffb7446a5944522 --- diff --git a/manifest b/manifest index fe92ac26c2..2748076d94 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Change\sa\ssingle\sinteger\svariable\sto\s64-bit\sin\sthe\stokenizer\sto\savoid\nany\spossibility\sof\san\sinteger\soverflow. -D 2025-12-27T19:57:59.356 +C Improvements\sto\sthe\s.import\scommand\sof\sthe\sCLI. +D 2025-12-28T14:01:52.978 F .fossil-settings/binary-glob 61195414528fb3ea9693577e1980230d78a1f8b0a54c78cf1b9b24d0a409ed6a x F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea @@ -737,7 +737,7 @@ F src/random.c 606b00941a1d7dd09c381d3279a058d771f406c5213c9932bbd93d5587be4b9c F src/resolve.c 47aa7fdc9ec4c19b103ac5e79d7887d30119b5675309facf5eed1118391c868b F src/rowset.c 8432130e6c344b3401a8874c3cb49fefe6873fec593294de077afea2dce5ec97 F src/select.c 85852256d860f3ba5be4a9edc1238e68dbea082a0167f31b7345c821ae45775d -F src/shell.c.in 8aa6b2bc490271bcbb39b650b120d28976c2de0c0ae80f356a64b2971ba85023 +F src/shell.c.in b30c49e8b58c51ee12034b14960a9097eec7fc2053231a31fe6271aa7e6c319f F src/sqlite.h.in b6599377f02ef9d545a8da48959213928b63291ad83ff65e5f3a72bf4fec595d F src/sqlite3.rc 015537e6ac1eec6c7050e17b616c2ffe6f70fca241835a84a4f0d5937383c479 F src/sqlite3ext.h 5d5330f5f8461f5ce74960436ddcfa53ecd09c2b8b23901e22ae38aec3243998 @@ -1614,7 +1614,7 @@ F test/sharedB.test 1a84863d7a2204e0d42f2e1606577c5e92e4473fa37ea0f5bdf829e4bf8e F test/shared_err.test 32634e404a3317eeb94abc7a099c556a346fdb8fb3858dbe222a4cbb8926a939 F test/sharedlock.test 5ede3c37439067c43b0198f580fd374ebf15d304 F test/shell1.test da4901c88f3a1db2acd39381ed260a0fe3c706bbc170b695fcbe6cb3eb6f7ab0 -F test/shell2.test f563798603950345a98f2a630d4ea746fc0063919a3dac917475d4e2166acbd7 +F test/shell2.test dc541d2681503e55466a24d35a4cbf8ca5b90b8fcdef37fc4db07373a67d31d3 F test/shell3.test 603b448e917537cf77be0f265c05c6f63bc677c63a533c8e96aae923b56f4a0e F test/shell4.test e25580a792b7b54560c3a76b6968bd8189261f38979fe28e6bc6312c5db280db F test/shell5.test 145a9474bab6d80b6ee452b83135507b25275d53b0936aab76520f007ac4d9dc @@ -2188,8 +2188,8 @@ F tool/warnings-clang.sh bbf6a1e685e534c92ec2bfba5b1745f34fb6f0bc2a362850723a9ee F tool/warnings.sh d924598cf2f55a4ecbc2aeb055c10bd5f48114793e7ba25f9585435da29e7e98 F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f F tool/winmain.c f40bccf0236f8bcc34b299781b7d34cb269ace23afe5c1b8a9d966e2fa1ce9e5 -P f41851587328f8b434e66987765324b7bf5746ecdf3a9f2869000276e0f79401 -R 0318fcb6944164fd4ebe079bcdee2867 +P 69cf692a24d714305b2e4c8c4c40f70dc6510c26b8db7e5249f32a53e44c7e5c +R 3af252d77a85ba7bba900e37cdf35e34 U drh -Z cb66f3935c2321391ca778a118ec4b1c +Z 298454a73cfd919f12c3b1fa1a6bf676 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 71e418aa81..bf21efe34e 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -69cf692a24d714305b2e4c8c4c40f70dc6510c26b8db7e5249f32a53e44c7e5c +436ed7937bcd3b5781539d883ff2957b81d74abccb75f65a2ffb7446a5944522 diff --git a/src/shell.c.in b/src/shell.c.in index ce6026330a..9b74c0166b 100644 --- a/src/shell.c.in +++ b/src/shell.c.in @@ -4930,8 +4930,8 @@ static void import_append_char(ImportCtx *p, int c){ ** + Input comes from p->in. ** + Store results in p->z of length p->n. Space to hold p->z comes ** from sqlite3_malloc64(). -** + Use p->cSep as the column separator. The default is ",". -** + Use p->rSep as the row separator. The default is "\n". +** + Use p->cColSep as the column separator. The default is ",". +** + Use p->cRowSep as the row separator. The default is "\n". ** + Keep track of the line number in p->nLine. ** + Store the character that terminates the field in p->cTerm. Store ** EOF on end-of-file. @@ -5020,8 +5020,8 @@ static char *SQLITE_CDECL csv_read_one_field(ImportCtx *p){ ** + Input comes from p->in. ** + Store results in p->z of length p->n. Space to hold p->z comes ** from sqlite3_malloc64(). -** + Use p->cSep as the column separator. The default is "\x1F". -** + Use p->rSep as the row separator. The default is "\x1E". +** + Use p->cColSep as the column separator. The default is "\x1F". +** + Use p->cRowSep as the row separator. The default is "\x1E". ** + Keep track of the row number in p->nLine. ** + Store the character that terminates the field in p->cTerm. Store ** EOF on end-of-file. @@ -7346,13 +7346,23 @@ static int pickStr(const char *zArg, char **pzErr, ...){ ** names. If FILE begins with "|" then it is a command that is run ** and the output from the command is used as the input data. ** -** FILE is assumed to be in a CSV format, unless the current mode -** is "ascii" or "tabs" or unless one of the options below specify -** an alternative. +** The content of FILE is interpreted using RFC-4180 ("CSV") quoting +** rules unless the current mode is "ascii" or "tabs" or unless one +** the --ascii option is used. +** +** The column and row separators must be single ASCII characters. If +** multiple characters or a Unicode character are specified for the +** separators, then only the first byte of the separator is used. Except, +** if the row separator is \n and the mode is not --ascii, then \r\n is +** understood as a row separator too. ** ** Options: -** --ascii Use \037 and \036 as column and row separators on input +** --ascii Do not use RFC-4180 quoting. Use \037 and \036 +** as column and row separators on input, unless other +** delimiters are specified using --colsep and/or --rowsep +** --colsep CHAR Use CHAR as the column separator. ** --csv Input is standard RFC-4180 CSV. +** --rowsep CHAR Use CHAR as the row separator. ** --schema S When creating TABLE, put it in schema S ** --skip N Ignore the first N rows of input ** -v Verbose mode @@ -7368,13 +7378,11 @@ static int dotCmdImport(ShellState *p){ i64 nByte; /* Number of bytes in an SQL string */ int i, j; /* Loop counters */ int needCommit; /* True to COMMIT or ROLLBACK at end */ - int nSep; /* Number of bytes in spec.zColumnSep */ char *zSql = 0; /* An SQL statement */ ImportCtx sCtx; /* Reader context */ char *(SQLITE_CDECL *xRead)(ImportCtx*); /* Func to read one value */ int eVerbose = 0; /* Larger for more console output */ i64 nSkip = 0; /* Initial lines to skip */ - int useOutputMode = 1; /* Use output mode to determine separators */ char *zCreate = 0; /* CREATE TABLE statement text */ int rc; /* Result code */ @@ -7404,15 +7412,27 @@ static int dotCmdImport(ShellState *p){ }else if( cli_strcmp(z,"-skip")==0 && imode.spec.zColumnSep==0 ){ - modeSetStr(&p->mode.spec.zColumnSep, ","); - nSep = 1; - }else if( (nSep = strlen30(p->mode.spec.zColumnSep))==0 ){ - eputz("Error: non-null column separator required for import\n"); - return 1; - } - if( nSep>1 ){ - eputz("Error: multi-character column separators not allowed" - " for import\n"); - return 1; - } - if( p->mode.spec.zRowSep==0 ){ - modeSetStr(&p->mode.spec.zRowSep, "\n"); - nSep = 1; - }else if( (nSep = strlen30(p->mode.spec.zRowSep))==0 ){ - eputz("Error: non-null row separator required for import\n"); - return 1; + if( sCtx.cColSep==0 ){ + if( p->mode.spec.zColumnSep && p->mode.spec.zColumnSep[0]!=0 ){ + sCtx.cColSep = p->mode.spec.zColumnSep[0]; + }else{ + sCtx.cColSep = ','; } - if( nSep==2 && p->mode.eMode==MODE_Csv - && cli_strcmp(p->mode.spec.zRowSep,SEP_CrLf)==0 - ){ - /* When importing CSV (only), if the row separator is set to the - ** default output row separator, change it to the default input - ** row separator. This avoids having to maintain different input - ** and output row separators. */ - modeSetStr(&p->mode.spec.zRowSep, SEP_Row); - nSep = strlen30(p->mode.spec.zRowSep); - } - if( nSep>1 ){ - eputz("Error: multi-character row separators not allowed" - " for import\n"); - return 1; + } + if( (sCtx.cColSep & 0x80)!=0 ){ + eputz("Error: .import column separator must be ASCII\n"); + return 1; + } + if( sCtx.cRowSep==0 ){ + if( p->mode.spec.zRowSep && p->mode.spec.zRowSep[0]!=0 ){ + sCtx.cRowSep = p->mode.spec.zRowSep[0]; + }else{ + sCtx.cRowSep = '\n'; } - sCtx.cColSep = (u8)p->mode.spec.zColumnSep[0]; - sCtx.cRowSep = (u8)p->mode.spec.zRowSep[0]; + } + if( sCtx.cRowSep=='\r' && xRead!=ascii_read_one_field ){ + sCtx.cRowSep = '\n'; + } + if( (sCtx.cRowSep & 0x80)!=0 ){ + eputz("Error: .import row separator must be ASCII\n"); + return 1; } sCtx.zFile = zFile; sCtx.nLine = 1; @@ -7484,7 +7489,7 @@ static int dotCmdImport(ShellState *p){ cli_printf(stderr,"Error: cannot open \"%s\"\n", zFile); return 1; } - if( eVerbose>=2 || (eVerbose>=1 && useOutputMode) ){ + if( eVerbose>=1 ){ char zSep[2]; zSep[1] = 0; zSep[0] = sCtx.cColSep; diff --git a/test/shell2.test b/test/shell2.test index 3f60041769..7141c4d497 100644 --- a/test/shell2.test +++ b/test/shell2.test @@ -330,11 +330,11 @@ do_test shell2-1.4.11 { close $df set res [catchcmd :memory: [string trim { CREATE TABLE t(line text); -.mode ascii -.separator "\377" "\n" +.mode ascii -colsep "\377" -rowsep "\n" .import dummy.csv t SELECT count(*) FROM t;}]] -} {0 1} +} {1 {0 +Error: .import column separator must be ASCII}} } ;# ifcapable vtab # Bug from forum post 7cbe081746dd3803