From: drh <> Date: Sun, 28 Dec 2025 14:32:28 +0000 (+0000) Subject: If the filename argument to the .import command is of the form X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=c00c0a839256c15adf82eba1709a2e487057288c;p=thirdparty%2Fsqlite.git If the filename argument to the .import command is of the form "<<endmark" then the content is read from the script (or stdin) until the first line that begins with "endmark". FossilOrigin-Name: 6e8ab51fbe0ee918bce6ab960c9fb078764aa9721ef41d39818127eb98045d46 --- diff --git a/manifest b/manifest index 2748076d94..dc2d398906 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Improvements\sto\sthe\s.import\scommand\sof\sthe\sCLI. -D 2025-12-28T14:01:52.978 +C If\sthe\sfilename\sargument\sto\sthe\s.import\scommand\sis\sof\sthe\sform\n"<<endmark"\sthen\sthe\scontent\sis\sread\sfrom\sthe\sscript\s(or\sstdin)\suntil\nthe\sfirst\sline\sthat\sbegins\swith\s"endmark". +D 2025-12-28T14:32:28.710 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 b30c49e8b58c51ee12034b14960a9097eec7fc2053231a31fe6271aa7e6c319f +F src/shell.c.in 35396d39cf51e4bfcfee81c4d65b02f91b95d8ee8cec5e98bf1d9213cca40949 F src/sqlite.h.in b6599377f02ef9d545a8da48959213928b63291ad83ff65e5f3a72bf4fec595d F src/sqlite3.rc 015537e6ac1eec6c7050e17b616c2ffe6f70fca241835a84a4f0d5937383c479 F src/sqlite3ext.h 5d5330f5f8461f5ce74960436ddcfa53ecd09c2b8b23901e22ae38aec3243998 @@ -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 69cf692a24d714305b2e4c8c4c40f70dc6510c26b8db7e5249f32a53e44c7e5c -R 3af252d77a85ba7bba900e37cdf35e34 +P 436ed7937bcd3b5781539d883ff2957b81d74abccb75f65a2ffb7446a5944522 +R c6951f2ba3a0d1f38f6ef6bfc8ae18c1 U drh -Z 298454a73cfd919f12c3b1fa1a6bf676 +Z 8253cdf56e85c54f922d095ac05cd420 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index bf21efe34e..cc58ec3a27 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -436ed7937bcd3b5781539d883ff2957b81d74abccb75f65a2ffb7446a5944522 +6e8ab51fbe0ee918bce6ab960c9fb078764aa9721ef41d39818127eb98045d46 diff --git a/src/shell.c.in b/src/shell.c.in index 9b74c0166b..ea4329bfdf 100644 --- a/src/shell.c.in +++ b/src/shell.c.in @@ -4892,7 +4892,9 @@ struct ImportCtx { const char *zFile; /* Name of the input file */ FILE *in; /* Read the CSV text from this input stream */ int (SQLITE_CDECL *xCloser)(FILE*); /* Func to close in */ + char *zIn; /* Input text */ char *z; /* Accumulated text for a field */ + i64 nUsed; /* Bytes of zIn[] used so far */ i64 n; /* Number of bytes in z */ i64 nAlloc; /* Space allocated for z[] */ int nLine; /* Current line number */ @@ -4912,9 +4914,28 @@ static void import_cleanup(ImportCtx *p){ } sqlite3_free(p->z); p->z = 0; + if( p->zIn ){ + sqlite3_free(p->zIn); + p->zIn = 0; + } +} + +/* Read a single character of the .import input text. Return EOF +** at end-of-file. +*/ +static int import_getc(ImportCtx *p){ + if( p->in ){ + return fgetc(p->in); + }else if( p->zIn && p->zIn[p->nUsed]!=0 ){ + return p->zIn[p->nUsed++]; + }else{ + return EOF; + } } -/* Append a single byte to z[] */ +/* Append a single byte to the field value begin constructed +** in the p->z[] buffer +*/ static void import_append_char(ImportCtx *p, int c){ if( p->n+1>=p->nAlloc ){ p->nAlloc += p->nAlloc + 100; @@ -4942,7 +4963,7 @@ static char *SQLITE_CDECL csv_read_one_field(ImportCtx *p){ int cSep = (u8)p->cColSep; int rSep = (u8)p->cRowSep; p->n = 0; - c = fgetc(p->in); + c = import_getc(p); if( c==EOF || seenInterrupt ){ p->cTerm = EOF; return 0; @@ -4953,7 +4974,7 @@ static char *SQLITE_CDECL csv_read_one_field(ImportCtx *p){ int cQuote = c; pc = ppc = 0; while( 1 ){ - c = fgetc(p->in); + c = import_getc(p); if( c==rSep ) p->nLine++; if( c==cQuote ){ if( pc==cQuote ){ @@ -4989,10 +5010,10 @@ static char *SQLITE_CDECL csv_read_one_field(ImportCtx *p){ ** UTF-8 BOM (0xEF BB BF) then skip the BOM */ if( (c&0xff)==0xef && p->bNotFirst==0 ){ import_append_char(p, c); - c = fgetc(p->in); + c = import_getc(p); if( (c&0xff)==0xbb ){ import_append_char(p, c); - c = fgetc(p->in); + c = import_getc(p); if( (c&0xff)==0xbf ){ p->bNotFirst = 1; p->n = 0; @@ -5002,7 +5023,7 @@ static char *SQLITE_CDECL csv_read_one_field(ImportCtx *p){ } while( c!=EOF && c!=cSep && c!=rSep ){ import_append_char(p, c); - c = fgetc(p->in); + c = import_getc(p); } if( c==rSep ){ p->nLine++; @@ -5032,14 +5053,14 @@ static char *SQLITE_CDECL ascii_read_one_field(ImportCtx *p){ int cSep = (u8)p->cColSep; int rSep = (u8)p->cRowSep; p->n = 0; - c = fgetc(p->in); + c = import_getc(p); if( c==EOF || seenInterrupt ){ p->cTerm = EOF; return 0; } while( c!=EOF && c!=cSep && c!=rSep ){ import_append_char(p, c); - c = fgetc(p->in); + c = import_getc(p); } if( c==rSep ){ p->nLine++; @@ -7344,7 +7365,9 @@ static int pickStr(const char *zArg, char **pzErr, ...){ ** Import CSV or similar text from FILE into TABLE. If TABLE does ** not exist, it is created using the first row of FILE as the column ** 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. +** and the output from the command is used as the input data. If +** FILE begins with "<<" followed by a label, then content is read from +** the script until the first line that matches the label. ** ** The content of FILE is interpreted using RFC-4180 ("CSV") quoting ** rules unless the current mode is "ascii" or "tabs" or unless one @@ -7481,11 +7504,32 @@ static int dotCmdImport(ShellState *p){ sCtx.zFile = ""; sCtx.xCloser = pclose; #endif + }else if( sCtx.zFile[0]=='<' && sCtx.zFile[1]=='<' && sCtx.zFile[2]!=0 ){ + /* Input text comes from subsequent lines of script until the zFile + ** delimiter */ + int nEndMark = strlen30(sCtx.zFile); + sqlite3_str *pPattern = sqlite3_str_new(p->db); + int ckEnd = 1; + char zLine[2000]; + while( sqlite3_fgets(zLine,sizeof(zLine),p->in) ){ + if( ckEnd && cli_strncmp(&sCtx.zFile[2],zLine,nEndMark-2)==0 ) break; + if( strchr(zLine,'\n') ){ + p->lineno++; + ckEnd = 1; + }else{ + ckEnd = 0; + } + sqlite3_str_appendall(pPattern, zLine); + } + sCtx.zIn = sqlite3_str_finish(pPattern); + if( sCtx.zIn==0 ){ + sCtx.zIn = sqlite3_mprintf(""); + } }else{ sCtx.in = sqlite3_fopen(sCtx.zFile, "rb"); sCtx.xCloser = fclose; } - if( sCtx.in==0 ){ + if( sCtx.in==0 && sCtx.zIn==0 ){ cli_printf(stderr,"Error: cannot open \"%s\"\n", zFile); return 1; }