]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
If the filename argument to the .import command is of the form
authordrh <>
Sun, 28 Dec 2025 14:32:28 +0000 (14:32 +0000)
committerdrh <>
Sun, 28 Dec 2025 14:32:28 +0000 (14:32 +0000)
"&lt;&lt;endmark" then the content is read from the script (or stdin) until
the first line that begins with "endmark".

FossilOrigin-Name: 6e8ab51fbe0ee918bce6ab960c9fb078764aa9721ef41d39818127eb98045d46

manifest
manifest.uuid
src/shell.c.in

index 2748076d94b735cca70545515dc930ac23f46bbe..dc2d3989062c84c4051d4abb26d831c252c6c300 100644 (file)
--- 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"&lt;&lt;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.
index bf21efe34e8bed6702507b8c4bf051dda522c313..cc58ec3a276f426bb560951e801ec1a7d8a9983d 100644 (file)
@@ -1 +1 @@
-436ed7937bcd3b5781539d883ff2957b81d74abccb75f65a2ffb7446a5944522
+6e8ab51fbe0ee918bce6ab960c9fb078764aa9721ef41d39818127eb98045d46
index 9b74c0166bd3a9610d3ab68d12e28bbcac637ad1..ea4329bfdf06d0b6c5d1d73705e84fc8ca91373b 100644 (file)
@@ -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 = "<pipe>";
     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;
   }