From 1d1cd36428636de62803782f53cc75e4ce1484f0 Mon Sep 17 00:00:00 2001 From: drh <> Date: Fri, 30 Jan 2026 12:27:13 +0000 Subject: [PATCH] Undo the previous check-in. In its place, add the --escape option to the ".import" command. FossilOrigin-Name: f11a05d52a1eb933f394d48c0d4ee0353f07ad725b7378c40afd01632e67f72d --- manifest | 14 +++++++------- manifest.uuid | 2 +- src/shell.c.in | 26 +++++++++++++++----------- test/shell5.test | 6 ++---- 4 files changed, 25 insertions(+), 23 deletions(-) diff --git a/manifest b/manifest index b16027a331..3cd3292f26 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Enhance\sthe\sCSV\simport\scapability\sin\sthe\sCLI\sso\sthat\sit\sunderstands\nbackslash-escaped\sdouble-quotes. -D 2026-01-30T12:15:25.973 +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 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 09adbe4663ae8ac344f6cceb4cebbdc474099337c82b106419a1955638c32268 +F src/shell.c.in 9f45c5d4b25c4621dc47cee7a3388ff1d8e8d1931240d4bfb60332bd3710e679 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 06df656f0a7f200d5e32717e1791e7cc11147c387e76ba6edce8b7addfa1cf77 +F test/shell5.test d2aee9c19b6aa0e4bde599e194cd01df60c97f5b721d6493cf8c1da0b45cede5 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 407724c4e80efdf93d885e95b5209a100a3f470fe0298138be57201f65f9817e -R f26e958d4efb79f8ef1e08564237ec1b +P 7ba0594d3c24531bc014dd029093b0503118a29cfdd13af2a2ebdd456855ce94 +R 6f4df6c212e3dffec0985d0418b3eb03 U drh -Z e9a4e3f3e0a6390e31312a9d8cbd8430 +Z 9898050fb5f26c38e6d09b053652c74e # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 490c8b5910..8f02d1ad8d 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -7ba0594d3c24531bc014dd029093b0503118a29cfdd13af2a2ebdd456855ce94 +f11a05d52a1eb933f394d48c0d4ee0353f07ad725b7378c40afd01632e67f72d diff --git a/src/shell.c.in b/src/shell.c.in index 6943fcd244..846bf58531 100644 --- a/src/shell.c.in +++ b/src/shell.c.in @@ -4920,6 +4920,7 @@ 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 */ }; /* Clean up resourced used by an ImportCtx */ @@ -4978,6 +4979,7 @@ 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 ){ @@ -4992,6 +4994,13 @@ static char *SQLITE_CDECL csv_read_one_field(ImportCtx *p){ while( 1 ){ c = import_getc(p); if( c==rSep ) p->nLine++; + if( c==cEsc && cEsc!=0 ){ + c = import_getc(p); + import_append_char(p, c); + ppc = pc; + pc = c; + continue; + } if( c==cQuote ){ if( pc==cQuote ){ pc = 0; @@ -5008,17 +5017,8 @@ static char *SQLITE_CDECL csv_read_one_field(ImportCtx *p){ break; } if( pc==cQuote && c!='\r' ){ - if( ppc=='\\' ){ - /* Deal backslash-escaped double-quote within a quoted string. - ** This is not RFC 4180 compliant, but one sees this in the - ** wild, so we might as well deal with it rather than raise - ** an error. */ - p->z[p->n-2] = '"'; - p->n--; - }else{ - cli_printf(stderr,"%s:%d: unescaped %c character\n", - p->zFile, p->nLine, cQuote); - } + cli_printf(stderr,"%s:%d: unescaped %c character\n", + p->zFile, p->nLine, cQuote); } if( c==EOF ){ cli_printf(stderr,"%s:%d: unterminated %c-quoted field\n", @@ -5047,6 +5047,7 @@ static char *SQLITE_CDECL csv_read_one_field(ImportCtx *p){ } } while( c!=EOF && c!=cSep && c!=rSep ){ + if( c==cEsc && cEsc!=0 ) c = import_getc(p); import_append_char(p, c); c = import_getc(p); } @@ -7410,6 +7411,7 @@ 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. ** --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 @@ -7468,6 +7470,8 @@ 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,"-colsep")==0 ){ if( i==nArg-1 ){ dotCmdError(p, i, "missing argument", 0); diff --git a/test/shell5.test b/test/shell5.test index e9082fd8ad..3ba8df463d 100644 --- a/test/shell5.test +++ b/test/shell5.test @@ -338,9 +338,7 @@ do_test shell5-1.10 { db eval {SELECT hex(c) FROM t1 ORDER BY rowid} } {636F6C756D6E33 783320220D0A64617461222033 783320220A64617461222033} -# Import columns containing quoted strings that contain -# a backslash-escaped quote. This is not complaint with -# RFC 4180, but people do it all the same. +# The --escape option # do_test shell5-1.10.1 { set out [open shell5.csv w] @@ -351,7 +349,7 @@ do_test shell5-1.10.1 { forcedelete test.db catchcmd test.db { CREATE TABLE t1(a,b,c,d); -.import -csv shell5.csv t1 +.import --csv --escape \\ shell5.csv t1 } sqlite3 db test.db db eval {SELECT c FROM t1 ORDER BY rowid} -- 2.47.3