]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Change the new escape-character mechanism for CSV import so that there are
authordrh <>
Fri, 30 Jan 2026 13:11:20 +0000 (13:11 +0000)
committerdrh <>
Fri, 30 Jan 2026 13:11:20 +0000 (13:11 +0000)
separate options for an escape character for quoted and unquoted fields of
the CSV.

FossilOrigin-Name: 293ec20e29e46d822a98302f8a0ba52ad6f74a7e93f780536d393730c2edbf61

manifest
manifest.uuid
src/shell.c.in
test/shell5.test

index 3cd3292f26366f0a03497f35fc1463182b28631b..4ffa2758fd8c374311564ee65b9accc441fa3496 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Undo\sthe\sprevious\scheck-in.\s\sIn\sits\splace,\sadd\sthe\s--escape\soption\sto\nthe\s".import"\scommand.
-D 2026-01-30T12:27:13.788
+C Change\sthe\snew\sescape-character\smechanism\sfor\sCSV\simport\sso\sthat\sthere\sare\nseparate\soptions\sfor\san\sescape\scharacter\sfor\squoted\sand\sunquoted\sfields\sof\nthe\sCSV.
+D 2026-01-30T13:11:20.460
 F .fossil-settings/binary-glob 61195414528fb3ea9693577e1980230d78a1f8b0a54c78cf1b9b24d0a409ed6a x
 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
@@ -739,7 +739,7 @@ F src/random.c 606b00941a1d7dd09c381d3279a058d771f406c5213c9932bbd93d5587be4b9c
 F src/resolve.c 47aa7fdc9ec4c19b103ac5e79d7887d30119b5675309facf5eed1118391c868b
 F src/rowset.c 8432130e6c344b3401a8874c3cb49fefe6873fec593294de077afea2dce5ec97
 F src/select.c 4bee1bb231771e7c6e5aef243b1f74c3d330df5a005909d5e2c338fb1510fe55
-F src/shell.c.in 9f45c5d4b25c4621dc47cee7a3388ff1d8e8d1931240d4bfb60332bd3710e679
+F src/shell.c.in 461c163897e1d001e957c69cad624ddac367e24e6da0b2de54592ea4e18fe98a
 F src/sqlite.h.in 8bcbaecfe2cbecf8c5c1381354fcdd7d307443e88b4953fccb222456c1267b61
 F src/sqlite3.rc 015537e6ac1eec6c7050e17b616c2ffe6f70fca241835a84a4f0d5937383c479
 F src/sqlite3ext.h 1b7a0ee438bb5c2896d0609c537e917d8057b3340f6ad004d2de44f03e3d3cca
@@ -1621,7 +1621,7 @@ F test/shell1.test 56f200ba4b36cbe8229d84d848e6a8d69eb91a75b2deaf28c454027433c6e
 F test/shell2.test dc541d2681503e55466a24d35a4cbf8ca5b90b8fcdef37fc4db07373a67d31d3
 F test/shell3.test 603b448e917537cf77be0f265c05c6f63bc677c63a533c8e96aae923b56f4a0e
 F test/shell4.test e25580a792b7b54560c3a76b6968bd8189261f38979fe28e6bc6312c5db280db
-F test/shell5.test d2aee9c19b6aa0e4bde599e194cd01df60c97f5b721d6493cf8c1da0b45cede5
+F test/shell5.test c5e8fb055ca74511eb0e757b568e48853d99306aea4a7e6a47a61dcf492a7d3c
 F test/shell6.test e3b883b61d4916b6906678a35f9d19054861123ad91b856461e0a456273bdbb8
 F test/shell7.test 43fd8e511c533bab5232e95c7b4be93b243451709e89582600d4b6e67693d5c3
 F test/shell8.test 641cf21a99c59404c24e3062923734951c4099a6b6b6520de00cf7a1249ee871
@@ -2193,8 +2193,8 @@ F tool/warnings-clang.sh bbf6a1e685e534c92ec2bfba5b1745f34fb6f0bc2a362850723a9ee
 F tool/warnings.sh d924598cf2f55a4ecbc2aeb055c10bd5f48114793e7ba25f9585435da29e7e98
 F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f
 F tool/winmain.c 00c8fb88e365c9017db14c73d3c78af62194d9644feaf60e220ab0f411f3604c
-P 7ba0594d3c24531bc014dd029093b0503118a29cfdd13af2a2ebdd456855ce94
-R 6f4df6c212e3dffec0985d0418b3eb03
+P f11a05d52a1eb933f394d48c0d4ee0353f07ad725b7378c40afd01632e67f72d
+R a8ba995b68957165836a359cdc9bda8c
 U drh
-Z 9898050fb5f26c38e6d09b053652c74e
+Z ebb3f29c37b6be76dd377e9cc977e6a7
 # Remove this line to create a well-formed Fossil manifest.
index 8f02d1ad8d6fa60f1d851afd0fc5f51980d8d9aa..9910be4c8d64f0e40b5774733d75dcdb7fa95972 100644 (file)
@@ -1 +1 @@
-f11a05d52a1eb933f394d48c0d4ee0353f07ad725b7378c40afd01632e67f72d
+293ec20e29e46d822a98302f8a0ba52ad6f74a7e93f780536d393730c2edbf61
index 846bf58531f46cf4064ddacd8879345fc0967864..d9b32e48598b95f18505c6cebe7924e1adf37a5e 100644 (file)
@@ -4920,7 +4920,8 @@ struct ImportCtx {
   int cTerm;          /* Character that terminated the most recent field */
   int cColSep;        /* The column separator character.  (Usually ",") */
   int cRowSep;        /* The row separator character.  (Usually "\n") */
-  int cEscape;        /* Escape character.  0 for none */
+  int cQEscape;       /* Escape character with "...".  0 for none */
+  int cUQEscape;      /* Escape character not with "...".  0 for none */
 };
 
 /* Clean up resourced used by an ImportCtx */
@@ -4979,7 +4980,6 @@ static char *SQLITE_CDECL csv_read_one_field(ImportCtx *p){
   int c;
   int cSep = (u8)p->cColSep;
   int rSep = (u8)p->cRowSep;
-  int cEsc = (u8)p->cEscape;
   p->n = 0;
   c = import_getc(p);
   if( c==EOF || seenInterrupt ){
@@ -4990,6 +4990,7 @@ static char *SQLITE_CDECL csv_read_one_field(ImportCtx *p){
     int pc, ppc;
     int startLine = p->nLine;
     int cQuote = c;
+    int cEsc = (u8)p->cQEscape;
     pc = ppc = 0;
     while( 1 ){
       c = import_getc(p);
@@ -4997,8 +4998,7 @@ static char *SQLITE_CDECL csv_read_one_field(ImportCtx *p){
       if( c==cEsc && cEsc!=0 ){
         c = import_getc(p);
         import_append_char(p, c);
-        ppc = pc;
-        pc = c;
+        ppc = pc = 0;
         continue;
       }
       if( c==cQuote ){
@@ -5033,6 +5033,7 @@ static char *SQLITE_CDECL csv_read_one_field(ImportCtx *p){
   }else{
     /* If this is the first field being parsed and it begins with the
     ** UTF-8 BOM  (0xEF BB BF) then skip the BOM */
+    int cEsc = p->cUQEscape;
     if( (c&0xff)==0xef && p->bNotFirst==0 ){
       import_append_char(p, c);
       c = import_getc(p);
@@ -7411,7 +7412,8 @@ static int pickStr(const char *zArg, char **pzErr, ...){
 **                   delimiters are specified using --colsep and/or --rowsep
 **   --colsep CHAR   Use CHAR as the column separator.
 **   --csv           Input is standard RFC-4180 CSV.
-**   --escape CHAR   Use CHAR as an escape character in CSV imports.
+**   --esc CHAR      Use CHAR as an escape character in unquoted CSV inputs.
+**   --qesc CHAR     Use CHAR as an escape character in quoted CSV inputs.
 **   --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
@@ -7470,8 +7472,10 @@ static int dotCmdImport(ShellState *p){
       if( sCtx.cColSep==0 ) sCtx.cColSep = ',';
       if( sCtx.cRowSep==0 ) sCtx.cRowSep = '\n';
       xRead = csv_read_one_field;
-    }else if( cli_strcmp(z,"-escape")==0 ){
-      sCtx.cEscape = azArg[++i][0];
+    }else if( cli_strcmp(z,"-esc")==0 ){
+      sCtx.cUQEscape = azArg[++i][0];
+    }else if( cli_strcmp(z,"-qesc")==0 ){
+      sCtx.cQEscape = azArg[++i][0];
     }else if( cli_strcmp(z,"-colsep")==0 ){
       if( i==nArg-1 ){
         dotCmdError(p, i, "missing argument", 0);
index 3ba8df463df2bc6ec6f392cb6c0d494f98f32843..b21b24b924e85d45631d0d85a7c001bda5d75d6b 100644 (file)
@@ -344,16 +344,16 @@ do_test shell5-1.10.1 {
   set out [open shell5.csv w]
   fconfigure $out -translation lf
   puts $out {column1,column2,column3,column4}
-  puts $out "x1,x2,\"x3 \\\"data\\\" 3\",x4"
+  puts $out "x1,x2%\"x3,\"x3\\\"data\\\"3\",x4"
   close $out
   forcedelete test.db
   catchcmd test.db {
     CREATE TABLE t1(a,b,c,d);
-.import --csv --escape \\ shell5.csv t1
+.import --csv --qesc \\ --esc % shell5.csv t1
   }
   sqlite3 db test.db
-  db eval {SELECT c FROM t1 ORDER BY rowid}
-} {column3 {x3 "data" 3}}
+  db eval {SELECT b, c FROM t1 ORDER BY rowid}
+} {column2 column3 x2\"x3 x3\"data\"3}
 
 # Blank last column with \r\n line endings.
 do_test shell5-1.11 {