From e62296100f7f32a53b3c6cea88ef4618057b503f Mon Sep 17 00:00:00 2001 From: drh Date: Mon, 18 Aug 2014 15:08:26 +0000 Subject: [PATCH 1/3] Begin adding commands to the command-line interface for interacting with the sessions extension. This is the first check-in of a work-in-progress. FossilOrigin-Name: c2fcf0b9f4bdc48dfc6530bda4f531b94a833207 --- main.mk | 5 ++ manifest | 17 +++--- manifest.uuid | 2 +- src/shell.c | 151 ++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 167 insertions(+), 8 deletions(-) diff --git a/main.mk b/main.mk index 73c9847408..24e599ca5b 100644 --- a/main.mk +++ b/main.mk @@ -72,6 +72,8 @@ LIBOBJ+= vdbe.o parse.o \ vdbeapi.o vdbeaux.o vdbeblob.o vdbemem.o vdbesort.o \ vdbetrace.o wal.o walker.o where.o utf.o vtab.o +LIBOBJ += sqlite3session.o + # All of the source code files. @@ -563,6 +565,9 @@ fts3_write.o: $(TOP)/ext/fts3/fts3_write.c $(HDR) $(EXTHDR) rtree.o: $(TOP)/ext/rtree/rtree.c $(HDR) $(EXTHDR) $(TCCX) -DSQLITE_CORE -c $(TOP)/ext/rtree/rtree.c +sqlite3session.o: $(TOP)/ext/session/sqlite3session.c $(HDR) $(EXTHDR) + $(TCCX) -DSQLITE_CORE -c $(TOP)/ext/session/sqlite3session.c + # Rules for building test programs and for running tests # diff --git a/manifest b/manifest index 84f92a5c48..3a3abec595 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Merge\sthe\slatest\strunk\schanges,\sand\sin\sparticular\sthe\srefactoring\sof\sthe\nobject\snames\sin\sthe\scommand-line\sshell. -D 2014-08-18T13:48:41.625 +C Begin\sadding\scommands\sto\sthe\scommand-line\sinterface\sfor\sinteracting\swith\nthe\ssessions\sextension.\s\sThis\sis\sthe\sfirst\scheck-in\sof\sa\swork-in-progress. +D 2014-08-18T15:08:26.073 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.in 639859a6f81bd15921ccd56ddbd6dfd335278377 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 @@ -161,7 +161,7 @@ F ext/session/test_session.c 920ccb6d6e1df263cd9099563328094c230b2925 F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8 F magic.txt 8273bf49ba3b0c8559cb2774495390c31fd61c60 -F main.mk 152593de5d19eeab0fdaacfdedd5eafdfdd83d5a +F main.mk eb6742eba7ebb8dc18401220e7ce21788d41aaef F mkopcodec.awk c2ff431854d702cdd2d779c9c0d1f58fa16fa4ea F mkopcodeh.awk c6b3fa301db6ef7ac916b14c60868aeaec1337b5 F mkso.sh fd21c06b063bb16a5d25deea1752c2da6ac3ed83 @@ -238,7 +238,7 @@ F src/random.c d10c1f85b6709ca97278428fd5db5bbb9c74eece F src/resolve.c 0ea356d32a5e884add23d1b9b4e8736681dd5697 F src/rowset.c a9c9aae3234b44a6d7c6f5a3cadf90dce1e627be F src/select.c ea48e891406ccdf748f3eb02893e056d134a0fea -F src/shell.c 41df1103617331e023301d943977f8c06bd1486f +F src/shell.c 6378fc281ab6c2c5ee46cf13f2657e8a187c3ce7 F src/sqlite.h.in 021a1f5c50e83060675d994a6014fd409e611d9e F src/sqlite3.rc 992c9f5fb8285ae285d6be28240a7e8d3a7f2bad F src/sqlite3ext.h 886f5a34de171002ad46fae8c36a7d8051c190fc @@ -1202,7 +1202,10 @@ F tool/vdbe_profile.tcl 67746953071a9f8f2f668b73fe899074e2c6d8c1 F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4 F tool/warnings.sh 0abfd78ceb09b7f7c27c688c8e3fe93268a13b32 F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f -P 82fdb1975f5b29a751089a8582713372999ae56e 11a70e1ae7f05d06e4e09c9d20db0444b8881584 -R 9eb07c2c47c345635567e1ec3d8ef22d +P 419d286a2fc465f6e0f9662909d0cc52a18eefa4 +R 23a3bfb6cd8e3a34baca1f6d98151d23 +T *branch * sessions_from_cli +T *sym-sessions_from_cli * +T -sym-sessions * U drh -Z 44d5fceffc6c6ae323a3b362c9fccbd4 +Z dbfbe7f80e47f8918e41f67c20d63911 diff --git a/manifest.uuid b/manifest.uuid index 3f8761cf9d..c0786ba14f 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -419d286a2fc465f6e0f9662909d0cc52a18eefa4 \ No newline at end of file +c2fcf0b9f4bdc48dfc6530bda4f531b94a833207 \ No newline at end of file diff --git a/src/shell.c b/src/shell.c index 87b335fbb1..66f073a9a8 100644 --- a/src/shell.c +++ b/src/shell.c @@ -432,6 +432,19 @@ static char *one_input_line(FILE *in, char *zPrior, int isContinuation){ return zResult; } +#if defined(SQLITE_ENABLE_SESSION) +/* +** State information for a single open session +*/ +typedef struct OpenSession OpenSession; +struct OpenSession { + char *zName; /* Symbolic name for this session */ + int nFilter; /* Number of xFilter rejection GLOB patterns */ + char **azFilter; /* Array of xFilter rejection GLOB patterns */ + sqlite3_session *p; /* The open session */ +}; +#endif + /* ** Shell output mode information from before ".explain on", ** saved so that it can be restored by ".explain off" @@ -479,6 +492,10 @@ struct ShellState { int *aiIndent; /* Array of indents used in MODE_Explain */ int nIndent; /* Size of array aiIndent[] */ int iIndent; /* Index of current op in aiIndent[] */ +#if defined(SQLITE_ENABLE_SESSION) + int nSession; /* Number of active sessions */ + OpenSession aSession[4]; /* Array of sessions. [0] is in focus. */ +#endif }; /* @@ -1636,6 +1653,9 @@ static char zHelp[] = " LIKE pattern TABLE.\n" ".separator STRING ?NL? Change separator used by output mode and .import\n" " NL is the end-of-line mark for CSV\n" +#if defined(SQLITE_ENABLE_SESSION) + ".session CMD ... Create or control sessions\n" +#endif ".shell CMD ARGS... Run CMD ARGS... in a system shell\n" ".show Show the current values for various settings\n" ".stats on|off Turn stats on or off\n" @@ -1651,6 +1671,30 @@ static char zHelp[] = " Negative values right-justify\n" ; +#if defined(SQLITE_ENABLE_SESSION) +/* +** Print help information for the ".sessions" command +*/ +void session_help(ShellState *p){ + fprintf(p->out, + ".session ?NAME? SUBCOMMAND ?ARGS...?\n" + "If ?NAME? is omitted, the first defined session is used.\n" + "Subcommands:\n" + " attach TABLE Attach TABLE\n" + " changeset FILE Write a changeset into FILE\n" + " close Close one session\n" + " enable ?BOOLEAN? Set or query the enable bit\n" + " filter GLOB... Reject tables matching GLOBs\n" + " indirect ?BOOLEAN? Mark or query the indirect status\n" + " isempty Query whether the session is empty\n" + " list List currently open session names\n" + " open DB NAME Open a new session on DB\n" + " patchset FILE Write a patchset into FILE\n" + ); +} +#endif + + /* Forward reference */ static int process_input(ShellState *p, FILE *in); /* @@ -1714,6 +1758,36 @@ static void writefileFunc( sqlite3_result_int64(context, rc); } +#if defined(SQLITE_ENABLE_SESSION) +/* +** Close a single OpenSession object and release all of its associated +** resources. +*/ +static void session_close(OpenSession *pSession){ + int i; + sqlite3session_delete(pSession->p); + sqlite3_free(pSession->zName); + for(i=0; inFilter; i++){ + sqlite3_free(pSession->azFilter[i]); + } + sqlite3_free(pSession->azFilter); + memset(pSession, 0, sizeof(OpenSession)); +} +#endif + +/* +** Close all OpenSession objects and release all assocaited resources. +*/ +static void session_close_all(ShellState *p){ +#if defined(SQLITE_ENABLE_SESSION) + int i; + for(i=0; inSession; i++){ + session_close(&p->aSession[i]); + } + p->nSession = 0; +#endif +} + /* ** Make sure the database is open. If it is not, then open it. If ** the database fails to open, print an error message and exit. @@ -2865,6 +2939,7 @@ static int do_meta_command(char *zLine, ShellState *p){ } open_db(p, 1); if( p->db!=0 ){ + session_close_all(p); sqlite3_close(savedDb); sqlite3_free(p->zFreeOnClose); p->zFreeOnClose = zNewFilename; @@ -3086,6 +3161,81 @@ static int do_meta_command(char *zLine, ShellState *p){ } }else +#if defined(SQLITE_ENABLE_SESSION) + if( c=='s' && strncmp(azArg[0],"session",n)==0 && n>=3 ){ + OpenSession *pSession = &p->aSession[0]; + char **azCmd = &azArg[1]; + int iSes = 0; + int nCmd = nArg - 1; + int i; + if( nArg<=1 ) goto session_syntax_error; + if( nArg>=3 ){ + for(iSes=0; iSesnSession; iSes++){ + if( strcmp(p->aSession[iSes].zName, azArg[1])==0 ) break; + } + if( iSesnSession ){ + pSession = &p->aSession[iSes]; + azCmd++; + nCmd--; + }else{ + pSession = &p->aSession[0]; + iSes = 0; + } + } + + /* .session close + ** Close the identified session + */ + if( strcmp(azCmd[0], "close")==0 ){ + if( p->nSession ){ + session_close(pSession); + p->aSession[iSes] = p->aSession[--p->nSession]; + } + }else + + /* .session list + ** List all currently open sessions + */ + if( strcmp(azCmd[0],"list")==0 ){ + for(i=0; inSession; i++){ + fprintf(p->out, "%d %s\n", i, p->aSession[i].zName); + } + }else + + /* .session open DB NAME + ** Open a new session called NAME on the attached database DB. + ** DB is normally "main". + */ + if( strcmp(azCmd[0],"open")==0 ){ + char *zName; + if( nCmd!=3 ) goto session_syntax_error; + zName = azCmd[2]; + if( zName[0]==0 ) goto session_syntax_error; + for(i=0; inSession; i++){ + if( strcmp(p->aSession[i].zName,zName)==0 ){ + fprintf(stderr, "Session \"%s\" already exists\n", zName); + goto meta_command_exit; + } + } + if( p->nSession>=ArraySize(p->aSession) ){ + fprintf(stderr, "Maximum of %d sessions\n", ArraySize(p->aSession)); + goto meta_command_exit; + } + pSession = &p->aSession[p->nSession]; + rc = sqlite3session_create(p->db, azCmd[1], &pSession->p); + if( rc ){ + fprintf(stderr, "Cannot open session: error code=%d\n", rc); + goto meta_command_exit; + } + p->nSession++; + pSession->zName = sqlite3_mprintf("%s", zName); + }else + /* If no command name matches, show a syntax error */ + session_syntax_error: + session_help(p); + }else +#endif + #ifdef SQLITE_DEBUG /* Undocumented commands for internal testing. Subject to change ** without notice. */ @@ -4134,6 +4284,7 @@ int main(int argc, char **argv){ } set_table_name(&data, 0); if( data.db ){ + session_close_all(&data); sqlite3_close(data.db); } sqlite3_free(data.zFreeOnClose); From 3a67b0453e359e3f9158f51682eb07125ec1d4db Mon Sep 17 00:00:00 2001 From: drh Date: Mon, 18 Aug 2014 17:56:31 +0000 Subject: [PATCH 2/3] Add the "changeset" command-line utility for getting an ASCII dump of change sets. FossilOrigin-Name: 55bb3544a6b474c04853270067a35ca4b0079f52 --- ext/session/changeset.c | 314 ++++++++++++++++++++++++++++++++++++++++ main.mk | 4 + manifest | 18 +-- manifest.uuid | 2 +- src/shell.c | 49 +++++++ 5 files changed, 376 insertions(+), 11 deletions(-) create mode 100644 ext/session/changeset.c diff --git a/ext/session/changeset.c b/ext/session/changeset.c new file mode 100644 index 0000000000..62817a64e5 --- /dev/null +++ b/ext/session/changeset.c @@ -0,0 +1,314 @@ +/* +** 2014-08-18 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +************************************************************************* +** This file contains code to implement the "changeset" command line +** utility for displaying and transforming changesets generated by +** the Sessions extension. +*/ +#include "sqlite3.h" +#include +#include +#include +#include +#include + + +/* +** Show a usage message on stderr then quit. +*/ +static void usage(const char *argv0){ + fprintf(stderr, "Usage: %s FILENAME COMMAND ...\n", argv0); + fprintf(stderr, + "COMMANDs:\n" + " apply DB Apply the changeset to database file DB\n" + " concat FILE2 OUT Concatenate FILENAME and FILE2 into OUT\n" + " dump Show the complete content of the changeset\n" + " invert OUT Write an inverted changeset into file OUT\n" + " sql Give a pseudo-SQL rendering of the changeset\n" + ); + exit(1); +} + +/* +** Read the content of a disk file into an in-memory buffer +*/ +static void readFile(const char *zFilename, int *pSz, void **ppBuf){ + FILE *f; + int sz; + void *pBuf; + f = fopen(zFilename, "rb"); + if( f==0 ){ + fprintf(stderr, "cannot open \"%s\" for reading\n", zFilename); + exit(1); + } + fseek(f, 0, SEEK_END); + sz = (int)ftell(f); + rewind(f); + pBuf = sqlite3_malloc( sz ? sz : 1 ); + if( pBuf==0 ){ + fprintf(stderr, "cannot allocate %d to hold content of \"%s\"\n", + sz, zFilename); + exit(1); + } + if( sz>0 ){ + if( fread(pBuf, sz, 1, f)!=1 ){ + fprintf(stderr, "cannot read all %d bytes of \"%s\"\n", sz, zFilename); + exit(1); + } + fclose(f); + } + *pSz = sz; + *ppBuf = pBuf; +} + +/* Array for converting from half-bytes (nybbles) into ASCII hex +** digits. */ +static const char hexdigits[] = { + '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' +}; + +/* +** Render an sqlite3_value as an SQL string. +*/ +static void renderValue(sqlite3_value *pVal){ + switch( sqlite3_value_type(pVal) ){ + case SQLITE_FLOAT: { + double r1; + char zBuf[50]; + r1 = sqlite3_value_double(pVal); + sqlite3_snprintf(sizeof(zBuf), zBuf, "%!.15g", r1); + printf("%s", zBuf); + break; + } + case SQLITE_INTEGER: { + printf("%lld", sqlite3_value_int64(pVal)); + break; + } + case SQLITE_BLOB: { + char const *zBlob = sqlite3_value_blob(pVal); + int nBlob = sqlite3_value_bytes(pVal); + int i; + printf("x'"); + for(i=0; i>4)&0x0F]); + putchar(hexdigits[(zBlob[i])&0x0F]); + } + putchar('\''); + break; + } + case SQLITE_TEXT: { + const unsigned char *zArg = sqlite3_value_text(pVal); + putchar('\''); + while( zArg[0] ){ + putchar(zArg[0]); + if( zArg[0]=='\'' ) putchar(zArg[0]); + zArg++; + } + putchar('\''); + break; + } + default: { + assert( sqlite3_value_type(pVal)==SQLITE_NULL ); + printf("NULL"); + break; + } + } +} + +int main(int argc, char **argv){ + int sz, rc; + void *pBuf = 0; + if( argc<3 ) usage(argv[0]); + readFile(argv[1], &sz, &pBuf); + + /* changeset FILENAME apply DB + ** Apply the changeset in FILENAME to the database file DB + */ + if( strcmp(argv[2],"apply")==0 ){ + fprintf(stderr, "not yet implemented\n"); + }else + + /* changeset FILENAME concat FILE2 OUT + ** Add changeset FILE2 onto the end of the changeset in FILENAME + ** and write the result into OUT. + */ + if( strcmp(argv[2],"concat")==0 ){ + fprintf(stderr, "not yet implemented\n"); + }else + + /* changeset FILENAME dump + ** Show the complete content of the changeset in FILENAME + */ + if( strcmp(argv[2],"dump")==0 ){ + int cnt = 0; + int i; + sqlite3_changeset_iter *pIter; + rc = sqlite3changeset_start(&pIter, sz, pBuf); + if( rc!=SQLITE_OK ){ + fprintf(stderr, "sqlite3changeset_start() returns %d\n", rc); + exit(1); + } + while( sqlite3changeset_next(pIter)==SQLITE_ROW ){ + int op, bIndirect, nCol; + const char *zTab; + unsigned char *abPK; + sqlite3changeset_op(pIter, &zTab, &nCol, &op, &bIndirect); + cnt++; + printf("%d: %s table=[%s] indirect=%d nColumn=%d\n", + cnt, op==SQLITE_INSERT ? "INSERT" : + op==SQLITE_UPDATE ? "UPDATE" : "DELETE", + zTab, bIndirect, nCol); + sqlite3changeset_pk(pIter, &abPK, 0); + for(i=0; i=3 ){ for(iSes=0; iSesnSession; iSes++){ if( strcmp(p->aSession[iSes].zName, azArg[1])==0 ) break; @@ -3183,6 +3184,53 @@ static int do_meta_command(char *zLine, ShellState *p){ } } + /* .session attach TABLE + ** Invoke the sqlite3session_attach() interface to attach a particular + ** table so that it is never filtered. + */ + if( strcmp(azCmd[0],"attach")==0 ){ + if( nCmd!=2 ) goto session_syntax_error; + if( pSession->p==0 ){ + session_not_open: + fprintf(stderr, "ERROR: No sessions are open\n"); + }else{ + rc = sqlite3session_attach(pSession->p, azCmd[1]); + if( rc ){ + fprintf(stderr, "ERROR: sqlite3session_attach() returns %d\n", rc); + rc = 0; + } + } + }else + + /* .session changeset FILE + ** .session patchset FILE + ** Write a changeset or patchset into a file. The file is overwritten. + */ + if( strcmp(azCmd[0],"changeset")==0 || strcmp(azCmd[0],"patchset")==0 ){ + FILE *out = 0; + if( nCmd!=2 ) goto session_syntax_error; + if( pSession->p==0 ) goto session_not_open; + out = fopen(azCmd[1], "wb"); + if( out==0 ){ + fprintf(stderr, "ERROR: cannot open \"%s\" for writing\n", azCmd[1]); + }else{ + int szChng; + void *pChng; + if( azCmd[0][0]=='c' ){ + sqlite3session_changeset(pSession->p, &szChng, &pChng); + }else{ + sqlite3session_patchset(pSession->p, &szChng, &pChng); + } + if( pChng + && fwrite(pChng, szChng, 1, out)!=1 ){ + fprintf(stderr, "ERROR: Failed to write entire %d-byte output\n", + szChng); + } + sqlite3_free(pChng); + fclose(out); + } + }else + /* .session close ** Close the identified session */ @@ -3225,6 +3273,7 @@ static int do_meta_command(char *zLine, ShellState *p){ rc = sqlite3session_create(p->db, azCmd[1], &pSession->p); if( rc ){ fprintf(stderr, "Cannot open session: error code=%d\n", rc); + rc = 0; goto meta_command_exit; } p->nSession++; From 03168cacd5c139471e651cf825501a953e0fcd4c Mon Sep 17 00:00:00 2001 From: drh Date: Mon, 18 Aug 2014 20:01:31 +0000 Subject: [PATCH 3/3] A reasonably complete implementation of the "changeset" command-line tool and the ".sessions" command in the command-line shell. FossilOrigin-Name: 7b12f1f9c012f33d376242920583807b014b3287 --- ext/session/changeset.c | 108 ++++++++++++++++++++++++++++++++++++++-- manifest | 14 +++--- manifest.uuid | 2 +- src/shell.c | 80 +++++++++++++++++++++++++++++ 4 files changed, 191 insertions(+), 13 deletions(-) diff --git a/ext/session/changeset.c b/ext/session/changeset.c index 62817a64e5..1ff5cde87d 100644 --- a/ext/session/changeset.c +++ b/ext/session/changeset.c @@ -124,6 +124,58 @@ static void renderValue(sqlite3_value *pVal){ } } +/* +** Number of conflicts seen +*/ +static int nConflict = 0; + +/* +** The conflict callback +*/ +static int conflictCallback( + void *pCtx, + int eConflict, + sqlite3_changeset_iter *pIter +){ + int op, bIndirect, nCol, i; + const char *zTab; + unsigned char *abPK; + const char *zType = ""; + const char *zOp = ""; + const char *zSep = " "; + + nConflict++; + sqlite3changeset_op(pIter, &zTab, &nCol, &op, &bIndirect); + sqlite3changeset_pk(pIter, &abPK, 0); + switch( eConflict ){ + case SQLITE_CHANGESET_DATA: zType = "DATA"; break; + case SQLITE_CHANGESET_NOTFOUND: zType = "NOTFOUND"; break; + case SQLITE_CHANGESET_CONFLICT: zType = "PRIMARY KEY"; break; + case SQLITE_CHANGESET_FOREIGN_KEY: zType = "FOREIGN KEY"; break; + case SQLITE_CHANGESET_CONSTRAINT: zType = "CONSTRAINT"; break; + } + switch( op ){ + case SQLITE_UPDATE: zOp = "UPDATE of"; break; + case SQLITE_INSERT: zOp = "INSERT into"; break; + case SQLITE_DELETE: zOp = "DELETE from"; break; + } + printf("%s conflict on %s table %s with primary key", zType, zOp, zTab); + for(i=0; inFilter; i++){ + if( sqlite3_strglob(pSession->azFilter[i], zTab)==0 ) return 0; + } + return 1; +} +#endif + /* ** Make sure the database is open. If it is not, then open it. If ** the database fails to open, print an error message and exit. @@ -3235,12 +3250,75 @@ static int do_meta_command(char *zLine, ShellState *p){ ** Close the identified session */ if( strcmp(azCmd[0], "close")==0 ){ + if( nCmd!=1 ) goto session_syntax_error; if( p->nSession ){ session_close(pSession); p->aSession[iSes] = p->aSession[--p->nSession]; } }else + /* .session enable ?BOOLEAN? + ** Query or set the enable flag + */ + if( strcmp(azCmd[0], "enable")==0 ){ + int ii; + if( nCmd>2 ) goto session_syntax_error; + ii = nCmd==1 ? -1 : booleanValue(azCmd[1]); + if( p->nSession ){ + ii = sqlite3session_enable(pSession->p, ii); + fprintf(p->out, "session %s enable flag = %d\n", pSession->zName, ii); + } + }else + + /* .session filter GLOB .... + ** Set a list of GLOB patterns of table names to be excluded. + */ + if( strcmp(azCmd[0], "filter")==0 ){ + int ii, nByte; + if( nCmd<2 ) goto session_syntax_error; + if( p->nSession ){ + for(ii=0; iinFilter; ii++){ + sqlite3_free(pSession->azFilter[ii]); + } + sqlite3_free(pSession->azFilter); + nByte = sizeof(pSession->azFilter[0])*(nCmd-1); + pSession->azFilter = sqlite3_malloc( nByte ); + if( pSession->azFilter==0 ){ + fprintf(stderr, "Error: out or memory\n"); + exit(1); + } + for(ii=1; iiazFilter[ii-1] = sqlite3_mprintf("%s", azCmd[ii]); + } + pSession->nFilter = ii-1; + } + }else + + /* .session indirect ?BOOLEAN? + ** Query or set the indirect flag + */ + if( strcmp(azCmd[0], "indirect")==0 ){ + int ii; + if( nCmd>2 ) goto session_syntax_error; + ii = nCmd==1 ? -1 : booleanValue(azCmd[1]); + if( p->nSession ){ + ii = sqlite3session_indirect(pSession->p, ii); + fprintf(p->out, "session %s indirect flag = %d\n", pSession->zName,ii); + } + }else + + /* .session isempty + ** Determine if the session is empty + */ + if( strcmp(azCmd[0], "isempty")==0 ){ + int ii; + if( nCmd!=1 ) goto session_syntax_error; + if( p->nSession ){ + ii = sqlite3session_isempty(pSession->p); + fprintf(p->out, "session %s isempty flag = %d\n", pSession->zName, ii); + } + }else + /* .session list ** List all currently open sessions */ @@ -3276,6 +3354,8 @@ static int do_meta_command(char *zLine, ShellState *p){ rc = 0; goto meta_command_exit; } + pSession->nFilter = 0; + sqlite3session_table_filter(pSession->p, session_filter, pSession); p->nSession++; pSession->zName = sqlite3_mprintf("%s", zName); }else