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;
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..." */
};
".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",
** 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
/*
#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;
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;
*/
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;
}
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;
}
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;
#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);
}
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 ){
}
#endif /* !(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_ENABLE_DBPAGE_VTAB) */
-
/*
** If an input line begins with "." then invoke this routine to
** process that line.
}
}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)
}
}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]);
}
}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;
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;
/* 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
#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;
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;
}
}
*/
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
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);
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]);
}
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);
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);
** 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
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);
}
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 */
}
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 ){
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;
** 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
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 */
}
}
- 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);
** 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);
}
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;