]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Enhance the CLI with the ".connection" command that can switch between
authordrh <>
Fri, 23 Jul 2021 18:43:58 +0000 (18:43 +0000)
committerdrh <>
Fri, 23 Jul 2021 18:43:58 +0000 (18:43 +0000)
up to five different database connections.  Used for manual testing of multiple
database connections in the same process.

FossilOrigin-Name: 54eaf076c05887157179459ab39c2556953f6fef9c1b14f17a8aa74087da3023

manifest
manifest.uuid
src/shell.c.in

index 5883a6aee2a94f1e4df4fb7e50d53bc0adac96d3..99fe0fda6a573e424a6c90346e45658d303842b9 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Set\sthe\scheckSchema\sflag\sif\sa\sCREATE\sTABLE\sparse\sfails\sbecause\sthe\stable\nalready\sexists,\sto\sensure\sthat\sthe\stable\swas\snot\spreviously\sdeleted\sby\nsome\sother\sconnection.
-D 2021-07-22T21:11:06.648
+C Enhance\sthe\sCLI\swith\sthe\s".connection"\scommand\sthat\scan\sswitch\sbetween\nup\sto\sfive\sdifferent\sdatabase\sconnections.\s\sUsed\sfor\smanual\stesting\sof\smultiple\ndatabase\sconnections\sin\sthe\ssame\sprocess.
+D 2021-07-23T18:43:58.652
 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
 F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
@@ -545,7 +545,7 @@ F src/random.c 097dc8b31b8fba5a9aca1697aeb9fd82078ec91be734c16bffda620ced7ab83c
 F src/resolve.c 047a822844cea769f6fdd8418a335dd4bcd8b75ab5e264f2506a0804f869b562
 F src/rowset.c ba9515a922af32abe1f7d39406b9d35730ed65efab9443dc5702693b60854c92
 F src/select.c 3896009f30352985b28511a0c4d7dbb77ba418e91db80ab7745a7f6dcbe1031f
-F src/shell.c.in 856de2945bb7fdfdeebe7136cf1b59d24618845aa5e5f3937fda7ff37c623b51
+F src/shell.c.in dc889872292ce143ab43080e7eb48b95f9b13beabf85ae729388a6b9ee08bab7
 F src/sqlite.h.in 43fcf0fe2af04081f420a906fc020bde1243851ba44b0aa567a27f94bf8c3145
 F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
 F src/sqlite3ext.h e97f4e9b509408fea4c4e9bef5a41608dfac343b4d3c7a990dedde1e19af9510
@@ -1920,7 +1920,7 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
 F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
 F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
-P b65f4f763979ee9e0b943c787609ea22d6f7e01d41dfc1e084ec50a085a3550c
-R ed9a1584f4d75ade417ceb251baf5a51
+P 91bcb9621529b58d28e91a2763eb9eef3951400d5eaef105073258f3dd331872
+R 0cfa2cb9dd20ef662e91de1cc8d85fce
 U drh
-Z f439f5ab1be78d6d001ae417ae6fc683
+Z 13547b3fed83ea2295e7d5c9afd10815
index 264591703968f0ad7012fe410206fc3892955117..a66844c71ccc21508a80c28e9f313d3aaf5247bc 100644 (file)
@@ -1 +1 @@
-91bcb9621529b58d28e91a2763eb9eef3951400d5eaef105073258f3dd331872
\ No newline at end of file
+54eaf076c05887157179459ab39c2556953f6fef9c1b14f17a8aa74087da3023
\ No newline at end of file
index 248bc0dc0e284787a4ac7168bf893481e6165248..713050555ccb8506a4e4a9ebf8703ad67ee5dce9 100644 (file)
@@ -983,7 +983,7 @@ static void shellAddSchemaName(
   sqlite3 *db = sqlite3_context_db_handle(pCtx);
   UNUSED_PARAMETER(nVal);
   if( zIn!=0 && strncmp(zIn, "CREATE ", 7)==0 ){
-    for(i=0; i<(int)(sizeof(aPrefix)/sizeof(aPrefix[0])); i++){
+    for(i=0; i<ArraySize(aPrefix); i++){
       int n = strlen30(aPrefix[i]);
       if( strncmp(zIn+7, aPrefix[i], n)==0 && zIn[n+7]==' ' ){
         char *z = 0;
@@ -1140,19 +1140,22 @@ struct ShellState {
   char nullValue[20];    /* The text to print when a NULL comes back from
                          ** the database */
   char outfile[FILENAME_MAX]; /* Filename for *out */
-  const char *zDbFilename;    /* name of the database file */
-  char *zFreeOnClose;         /* Filename to free when closing */
-  const char *zVfs;           /* Name of VFS to use */
   sqlite3_stmt *pStmt;   /* Current statement if any. */
   FILE *pLog;            /* Write log output here */
+  struct AuxDb {         /* Storage space for auxiliary database connections */
+    sqlite3 *db;               /* Connection pointer */
+    const char *zDbFilename;   /* Filename used to open the connection */
+    char *zFreeOnClose;        /* Free this memory allocation on close */
+#if defined(SQLITE_ENABLE_SESSION)
+    int nSession;              /* Number of active sessions */
+    OpenSession aSession[4];   /* Array of sessions.  [0] is in focus. */
+#endif
+  } aAuxDb[5],           /* Array of all database connections */
+    *pAuxDb;             /* Currently active database connection */
   int *aiIndent;         /* Array of indents used in MODE_Explain */
   int nIndent;           /* Size of array aiIndent[] */
   int iIndent;           /* Index of current op in aiIndent[] */
   EQPGraph sGraph;       /* Information for the graphical EXPLAIN QUERY PLAN */
-#if defined(SQLITE_ENABLE_SESSION)
-  int nSession;             /* Number of active sessions */
-  OpenSession aSession[4];  /* Array of sessions.  [0] is in focus. */
-#endif
   ExpertInfo expert;        /* Valid if previous command was ".expert OPT..." */
 };
 
@@ -3904,6 +3907,7 @@ static const char *(azHelp[]) = {
   ".changes on|off          Show number of rows changed by SQL",
   ".check GLOB              Fail if output since .testcase does not match",
   ".clone NEWDB             Clone data into NEWDB from the existing database",
+  ".connection [close] [#]  Open or close an auxiliary database connection",
   ".databases               List names and files of attached databases",
   ".dbconfig ?op? ?val?     List or change sqlite3_db_config() options",
   ".dbinfo ?DB?             Show status information about the database",
@@ -4244,15 +4248,16 @@ static void session_close(OpenSession *pSession){
 ** Close all OpenSession objects and release all associated resources.
 */
 #if defined(SQLITE_ENABLE_SESSION)
-static void session_close_all(ShellState *p){
-  int i;
-  for(i=0; i<p->nSession; i++){
-    session_close(&p->aSession[i]);
+static void session_close_all(ShellState *p, int i){
+  int j;
+  struct AuxDb *pAuxDb = i<0 ? p->pAuxDb : &p->aAuxDb[i];
+  for(j=0; j<pAuxDb->nSession; j++){
+    session_close(&pAuxDb->aSession[j]);
   }
-  p->nSession = 0;
+  pAuxDb->nSession = 0;
 }
 #else
-# define session_close_all(X)
+# define session_close_all(X,Y)
 #endif
 
 /*
@@ -4317,8 +4322,8 @@ int deduceDatabaseType(const char *zName, int dfltZip){
 #ifndef SQLITE_OMIT_DESERIALIZE
 /*
 ** Reconstruct an in-memory database using the output from the "dbtotxt"
-** program.  Read content from the file in p->zDbFilename.  If p->zDbFilename
-** is 0, then read from standard input.
+** program.  Read content from the file in p->aAuxDb[].zDbFilename.
+** If p->aAuxDb[].zDbFilename is 0, then read from standard input.
 */
 static unsigned char *readHexDb(ShellState *p, int *pnData){
   unsigned char *a = 0;
@@ -4329,12 +4334,13 @@ static unsigned char *readHexDb(ShellState *p, int *pnData){
   int j, k;
   int rc;
   FILE *in;
+  const char *zDbFilename = p->pAuxDb->zDbFilename;
   unsigned int x[16];
   char zLine[1000];
-  if( p->zDbFilename ){
-    in = fopen(p->zDbFilename, "r");
+  if( zDbFilename ){
+    in = fopen(zDbFilename, "r");
     if( in==0 ){
-      utf8_printf(stderr, "cannot open \"%s\" for reading\n", p->zDbFilename);
+      utf8_printf(stderr, "cannot open \"%s\" for reading\n", zDbFilename);
       return 0;
     }
     nLine = 0;
@@ -4576,17 +4582,18 @@ static void shellEscapeCrnl(
 */
 static void open_db(ShellState *p, int openFlags){
   if( p->db==0 ){
+    const char *zDbFilename = p->pAuxDb->zDbFilename;
     if( p->openMode==SHELL_OPEN_UNSPEC ){
-      if( p->zDbFilename==0 || p->zDbFilename[0]==0 ){
+      if( zDbFilename==0 || zDbFilename[0]==0 ){
         p->openMode = SHELL_OPEN_NORMAL;
       }else{
-        p->openMode = (u8)deduceDatabaseType(p->zDbFilename, 
+        p->openMode = (u8)deduceDatabaseType(zDbFilename, 
                              (openFlags & OPEN_DB_ZIPFILE)!=0);
       }
     }
     switch( p->openMode ){
       case SHELL_OPEN_APPENDVFS: {
-        sqlite3_open_v2(p->zDbFilename, &p->db, 
+        sqlite3_open_v2(zDbFilename, &p->db, 
            SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE|p->openFlags, "apndvfs");
         break;
       }
@@ -4600,13 +4607,13 @@ static void open_db(ShellState *p, int openFlags){
         break;
       }
       case SHELL_OPEN_READONLY: {
-        sqlite3_open_v2(p->zDbFilename, &p->db,
+        sqlite3_open_v2(zDbFilename, &p->db,
             SQLITE_OPEN_READONLY|p->openFlags, 0);
         break;
       }
       case SHELL_OPEN_UNSPEC:
       case SHELL_OPEN_NORMAL: {
-        sqlite3_open_v2(p->zDbFilename, &p->db,
+        sqlite3_open_v2(zDbFilename, &p->db,
            SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE|p->openFlags, 0);
         break;
       }
@@ -4614,7 +4621,7 @@ static void open_db(ShellState *p, int openFlags){
     globalDb = p->db;
     if( p->db==0 || SQLITE_OK!=sqlite3_errcode(p->db) ){
       utf8_printf(stderr,"Error: unable to open database \"%s\": %s\n",
-          p->zDbFilename, sqlite3_errmsg(p->db));
+          zDbFilename, sqlite3_errmsg(p->db));
       if( openFlags & OPEN_DB_KEEPALIVE ){
         sqlite3_open(":memory:", &p->db);
         return;
@@ -4661,7 +4668,7 @@ static void open_db(ShellState *p, int openFlags){
 #endif
     if( p->openMode==SHELL_OPEN_ZIPFILE ){
       char *zSql = sqlite3_mprintf(
-         "CREATE VIRTUAL TABLE zip USING zipfile(%Q);", p->zDbFilename);
+         "CREATE VIRTUAL TABLE zip USING zipfile(%Q);", zDbFilename);
       sqlite3_exec(p->db, zSql, 0, 0, 0);
       sqlite3_free(zSql);
     }
@@ -4672,7 +4679,7 @@ static void open_db(ShellState *p, int openFlags){
       int nData = 0;
       unsigned char *aData;
       if( p->openMode==SHELL_OPEN_DESERIALIZE ){
-        aData = (unsigned char*)readFile(p->zDbFilename, &nData);
+        aData = (unsigned char*)readFile(zDbFilename, &nData);
       }else{
         aData = readHexDb(p, &nData);
         if( aData==0 ){
@@ -7393,7 +7400,6 @@ static int recoverDatabaseCmd(ShellState *pState, int nArg, char **azArg){
 }
 #endif /* !(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_ENABLE_DBPAGE_VTAB) */
 
-
 /*
 ** If an input line begins with "." then invoke this routine to
 ** process that line.
@@ -7558,6 +7564,13 @@ static int do_meta_command(char *zLine, ShellState *p){
     }
   }else
 
+  /* The undocumented ".breakpoint" command causes a call to the no-op
+  ** routine named test_breakpoint().
+  */
+  if( c=='b' && n>=3 && strncmp(azArg[0], "breakpoint", n)==0 ){
+    test_breakpoint();
+  }else
+
   if( c=='c' && strcmp(azArg[0],"cd")==0 ){
     if( nArg==2 ){
 #if defined(_WIN32) || defined(WIN32)
@@ -7577,13 +7590,6 @@ static int do_meta_command(char *zLine, ShellState *p){
     }
   }else
 
-  /* The undocumented ".breakpoint" command causes a call to the no-op
-  ** routine named test_breakpoint().
-  */
-  if( c=='b' && n>=3 && strncmp(azArg[0], "breakpoint", n)==0 ){
-    test_breakpoint();
-  }else
-
   if( c=='c' && n>=3 && strncmp(azArg[0], "changes", n)==0 ){
     if( nArg==2 ){
       setOrClearFlag(p, SHFLG_CountChanges, azArg[1]);
@@ -7627,6 +7633,52 @@ static int do_meta_command(char *zLine, ShellState *p){
     }
   }else
 
+  if( c=='c' && strncmp(azArg[0], "connection", n)==0 ){
+    if( nArg==1 ){
+      /* List available connections */
+      int i;
+      for(i=0; i<ArraySize(p->aAuxDb); i++){
+        const char *zFile = p->aAuxDb[i].zDbFilename;
+        if( p->aAuxDb[i].db==0 && p->pAuxDb!=&p->aAuxDb[i] ){
+          zFile = "(not open)";
+        }else if( zFile==0 ){
+          zFile = "(memory)";
+        }else if( zFile[0]==0 ){
+          zFile = "(temporary-file)";
+        }
+        if( p->pAuxDb == &p->aAuxDb[i] ){
+          utf8_printf(stdout, "ACTIVE %d: %s\n", i, zFile);
+        }else if( p->aAuxDb[i].db!=0 ){
+          utf8_printf(stdout, "       %d: %s\n", i, zFile);
+        }
+      }
+    }else if( nArg==2 && IsDigit(azArg[1][0]) && azArg[1][1]==0 ){
+      int i = azArg[1][0] - '0';
+      if( p->pAuxDb != &p->aAuxDb[i] && i>=0 && i<ArraySize(p->aAuxDb) ){
+        p->pAuxDb->db = p->db;
+        p->pAuxDb = &p->aAuxDb[i];
+        globalDb = p->db = p->pAuxDb->db;
+        p->pAuxDb->db = 0;
+      }
+    }else if( nArg==3 && strcmp(azArg[1], "close")==0
+           && IsDigit(azArg[2][0]) && azArg[2][1]==0 ){
+      int i = azArg[2][0] - '0';
+      if( i<0 || i>=ArraySize(p->aAuxDb) ){
+        /* No-op */
+      }else if( p->pAuxDb == &p->aAuxDb[i] ){
+        raw_printf(stderr, "cannot close the active database connection\n");
+        rc = 1;
+      }else if( p->aAuxDb[i].db ){
+        session_close_all(p, i);
+        close_db(p->aAuxDb[i].db);
+        p->aAuxDb[i].db = 0;
+      }
+    }else{
+      raw_printf(stderr, "Usage: .connection [close] [CONNECTION-NUMBER]\n");
+      rc = 1;
+    }
+  }else
+
   if( c=='d' && n>1 && strncmp(azArg[0], "databases", n)==0 ){
     char **azName = 0;
     int nName = 0;
@@ -8716,12 +8768,12 @@ static int do_meta_command(char *zLine, ShellState *p){
     int iName = 1;           /* Index in azArg[] of the filename */
     int newFlag = 0;         /* True to delete file before opening */
     /* Close the existing database */
-    session_close_all(p);
+    session_close_all(p, -1);
     close_db(p->db);
     p->db = 0;
-    p->zDbFilename = 0;
-    sqlite3_free(p->zFreeOnClose);
-    p->zFreeOnClose = 0;
+    p->pAuxDb->zDbFilename = 0;
+    sqlite3_free(p->pAuxDb->zFreeOnClose);
+    p->pAuxDb->zFreeOnClose = 0;
     p->openMode = SHELL_OPEN_UNSPEC;
     p->openFlags = 0;
     p->szMax = 0;
@@ -8765,18 +8817,18 @@ static int do_meta_command(char *zLine, ShellState *p){
     /* If a filename is specified, try to open it first */
     if( zNewFilename || p->openMode==SHELL_OPEN_HEXDB ){
       if( newFlag ) shellDeleteFile(zNewFilename);
-      p->zDbFilename = zNewFilename;
+      p->pAuxDb->zDbFilename = zNewFilename;
       open_db(p, OPEN_DB_KEEPALIVE);
       if( p->db==0 ){
         utf8_printf(stderr, "Error: cannot open '%s'\n", zNewFilename);
         sqlite3_free(zNewFilename);
       }else{
-        p->zFreeOnClose = zNewFilename;
+        p->pAuxDb->zFreeOnClose = zNewFilename;
       }
     }
     if( p->db==0 ){
       /* As a fall-back open a TEMP database */
-      p->zDbFilename = 0;
+      p->pAuxDb->zDbFilename = 0;
       open_db(p, 0);
     }
   }else
@@ -9305,7 +9357,8 @@ static int do_meta_command(char *zLine, ShellState *p){
 
 #if defined(SQLITE_ENABLE_SESSION)
   if( c=='s' && strncmp(azArg[0],"session",n)==0 && n>=3 ){
-    OpenSession *pSession = &p->aSession[0];
+    struct AuxDb *pAuxDb = p->pAuxDb;
+    OpenSession *pSession = &pAuxDb->aSession[0];
     char **azCmd = &azArg[1];
     int iSes = 0;
     int nCmd = nArg - 1;
@@ -9313,15 +9366,15 @@ static int do_meta_command(char *zLine, ShellState *p){
     if( nArg<=1 ) goto session_syntax_error;
     open_db(p, 0);
     if( nArg>=3 ){
-      for(iSes=0; iSes<p->nSession; iSes++){
-        if( strcmp(p->aSession[iSes].zName, azArg[1])==0 ) break;
+      for(iSes=0; iSes<pAuxDb->nSession; iSes++){
+        if( strcmp(pAuxDb->aSession[iSes].zName, azArg[1])==0 ) break;
       }
-      if( iSes<p->nSession ){
-        pSession = &p->aSession[iSes];
+      if( iSes<pAuxDb->nSession ){
+        pSession = &pAuxDb->aSession[iSes];
         azCmd++;
         nCmd--;
       }else{
-        pSession = &p->aSession[0];
+        pSession = &pAuxDb->aSession[0];
         iSes = 0;
       }
     }
@@ -9383,9 +9436,9 @@ static int do_meta_command(char *zLine, ShellState *p){
     */
     if( strcmp(azCmd[0], "close")==0 ){
       if( nCmd!=1 ) goto session_syntax_error;
-      if( p->nSession ){
+      if( pAuxDb->nSession ){
         session_close(pSession);
-        p->aSession[iSes] = p->aSession[--p->nSession];
+        pAuxDb->aSession[iSes] = pAuxDb->aSession[--pAuxDb->nSession];
       }
     }else
 
@@ -9396,7 +9449,7 @@ static int do_meta_command(char *zLine, ShellState *p){
       int ii;
       if( nCmd>2 ) goto session_syntax_error;
       ii = nCmd==1 ? -1 : booleanValue(azCmd[1]);
-      if( p->nSession ){
+      if( pAuxDb->nSession ){
         ii = sqlite3session_enable(pSession->p, ii);
         utf8_printf(p->out, "session %s enable flag = %d\n",
                     pSession->zName, ii);
@@ -9409,7 +9462,7 @@ static int do_meta_command(char *zLine, ShellState *p){
     if( strcmp(azCmd[0], "filter")==0 ){
       int ii, nByte;
       if( nCmd<2 ) goto session_syntax_error;
-      if( p->nSession ){
+      if( pAuxDb->nSession ){
         for(ii=0; ii<pSession->nFilter; ii++){
           sqlite3_free(pSession->azFilter[ii]);
         }
@@ -9434,7 +9487,7 @@ static int do_meta_command(char *zLine, ShellState *p){
       int ii;
       if( nCmd>2 ) goto session_syntax_error;
       ii = nCmd==1 ? -1 : booleanValue(azCmd[1]);
-      if( p->nSession ){
+      if( pAuxDb->nSession ){
         ii = sqlite3session_indirect(pSession->p, ii);
         utf8_printf(p->out, "session %s indirect flag = %d\n",
                     pSession->zName, ii);
@@ -9447,7 +9500,7 @@ static int do_meta_command(char *zLine, ShellState *p){
     if( strcmp(azCmd[0], "isempty")==0 ){
       int ii;
       if( nCmd!=1 ) goto session_syntax_error;
-      if( p->nSession ){
+      if( pAuxDb->nSession ){
         ii = sqlite3session_isempty(pSession->p);
         utf8_printf(p->out, "session %s isempty flag = %d\n",
                     pSession->zName, ii);
@@ -9458,8 +9511,8 @@ static int do_meta_command(char *zLine, ShellState *p){
     ** List all currently open sessions
     */
     if( strcmp(azCmd[0],"list")==0 ){
-      for(i=0; i<p->nSession; i++){
-        utf8_printf(p->out, "%d %s\n", i, p->aSession[i].zName);
+      for(i=0; i<pAuxDb->nSession; i++){
+        utf8_printf(p->out, "%d %s\n", i, pAuxDb->aSession[i].zName);
       }
     }else
 
@@ -9472,17 +9525,17 @@ static int do_meta_command(char *zLine, ShellState *p){
       if( nCmd!=3 ) goto session_syntax_error;
       zName = azCmd[2];
       if( zName[0]==0 ) goto session_syntax_error;
-      for(i=0; i<p->nSession; i++){
-        if( strcmp(p->aSession[i].zName,zName)==0 ){
+      for(i=0; i<pAuxDb->nSession; i++){
+        if( strcmp(pAuxDb->aSession[i].zName,zName)==0 ){
           utf8_printf(stderr, "Session \"%s\" already exists\n", zName);
           goto meta_command_exit;
         }
       }
-      if( p->nSession>=ArraySize(p->aSession) ){
-        raw_printf(stderr, "Maximum of %d sessions\n", ArraySize(p->aSession));
+      if( pAuxDb->nSession>=ArraySize(pAuxDb->aSession) ){
+        raw_printf(stderr, "Maximum of %d sessions\n", ArraySize(pAuxDb->aSession));
         goto meta_command_exit;
       }
-      pSession = &p->aSession[p->nSession];
+      pSession = &pAuxDb->aSession[pAuxDb->nSession];
       rc = sqlite3session_create(p->db, azCmd[1], &pSession->p);
       if( rc ){
         raw_printf(stderr, "Cannot open session: error code=%d\n", rc);
@@ -9491,7 +9544,7 @@ static int do_meta_command(char *zLine, ShellState *p){
       }
       pSession->nFilter = 0;
       sqlite3session_table_filter(pSession->p, session_filter, pSession);
-      p->nSession++;
+      pAuxDb->nSession++;
       pSession->zName = sqlite3_mprintf("%s", zName);
     }else
     /* If no command name matches, show a syntax error */
@@ -9820,7 +9873,7 @@ static int do_meta_command(char *zLine, ShellState *p){
     }
     raw_printf(p->out, "\n");
     utf8_printf(p->out, "%12.12s: %s\n", "filename",
-                p->zDbFilename ? p->zDbFilename : "");
+                p->pAuxDb->zDbFilename ? p->pAuxDb->zDbFilename : "");
   }else
 
   if( c=='s' && strncmp(azArg[0], "stats", n)==0 ){
@@ -10841,6 +10894,7 @@ static void main_init(ShellState *data) {
   memset(data, 0, sizeof(*data));
   data->normalMode = data->cMode = data->mode = MODE_List;
   data->autoExplain = 1;
+  data->pAuxDb = &data->aAuxDb[0];
   memcpy(data->colSeparator,SEP_Column, 2);
   memcpy(data->rowSeparator,SEP_Row, 2);
   data->showHeader = 0;
@@ -11004,7 +11058,7 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){
     ** this compile-time option to embed this shell program in larger
     ** applications. */
     extern void SQLITE_SHELL_DBNAME_PROC(const char**);
-    SQLITE_SHELL_DBNAME_PROC(&data.zDbFilename);
+    SQLITE_SHELL_DBNAME_PROC(&data.pAuxDb->zDbFilename);
     warnInmemoryDb = 0;
   }
 #endif
@@ -11019,8 +11073,8 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){
     char *z;
     z = argv[i];
     if( z[0]!='-' ){
-      if( data.zDbFilename==0 ){
-        data.zDbFilename = z;
+      if( data.aAuxDb->zDbFilename==0 ){
+        data.aAuxDb->zDbFilename = z;
       }else{
         /* Excesss arguments are interpreted as SQL (or dot-commands) and
         ** mean that nothing is read from stdin */
@@ -11160,9 +11214,9 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){
     }
   }
 
-  if( data.zDbFilename==0 ){
+  if( data.pAuxDb->zDbFilename==0 ){
 #ifndef SQLITE_OMIT_MEMORYDB
-    data.zDbFilename = ":memory:";
+    data.pAuxDb->zDbFilename = ":memory:";
     warnInmemoryDb = argc==1;
 #else
     utf8_printf(stderr,"%s: Error: no database filename specified\n", Argv0);
@@ -11177,7 +11231,7 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){
   ** files from being created if a user mistypes the database name argument
   ** to the sqlite command-line tool.
   */
-  if( access(data.zDbFilename, 0)==0 ){
+  if( access(data.pAuxDb->zDbFilename, 0)==0 ){
     open_db(&data, 0);
   }
 
@@ -11431,10 +11485,16 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){
   free(azCmd);
   set_table_name(&data, 0);
   if( data.db ){
-    session_close_all(&data);
+    session_close_all(&data, -1);
     close_db(data.db);
   }
-  sqlite3_free(data.zFreeOnClose);
+  for(i=0; i<ArraySize(data.aAuxDb); i++){
+    sqlite3_free(data.aAuxDb[i].zFreeOnClose);
+    if( data.aAuxDb[i].db ){
+      session_close_all(&data, i);
+      close_db(data.aAuxDb[i].db);
+    }
+  }
   find_home_dir(1);
   output_reset(&data);
   data.doXdgOpen = 0;