TCL extension polished and more smoothly integrated. A couple of bugs related to takedown fixed. Help for added features. Code cleaned up and commented.
FossilOrigin-Name: 4ee7df118033af368854992763d96f860f8bd218b28b9ad27dd7ad4291cabfd7
This commit is contained in:
+194
-39
@@ -16,29 +16,81 @@
|
||||
-o tclshext.so -ltcl8.6
|
||||
** Later TCL versions can be used if desired.
|
||||
**
|
||||
** This extension adds two features to the host shell:
|
||||
** 1. The .tcl dot command is added.
|
||||
** 2. TCL scripting support is added.
|
||||
**
|
||||
** The .tcl command can be run with 0 or more arguments.
|
||||
** With no arguments, it does a REPL loop until the end of input is seen.
|
||||
** The end of input is either an EOF condition or a lone '.' on a line.
|
||||
** With more arguments, files they name are interpreted as TCL script.
|
||||
** In either case, the TCL command return code is tranlated to a DotCmdRC.
|
||||
** This extension adds these features to the host shell:
|
||||
** 1. TCL scripting support is added.
|
||||
** 2. TCL commands added: udb shdb now_interactive get_tcl_group ..
|
||||
** 3. The .tcl and .eval dot commands are added.
|
||||
**
|
||||
** TCL scripting support is added with a ShellExtensionAPI hookScripting()
|
||||
** call in the manner documented for it and the ScriptHooks struct. This
|
||||
** support lasts until the extension destructor is called. Until then,
|
||||
** shell input groups beginning with ".." are treated as TCL input, one
|
||||
** complete TCL command at a time.
|
||||
** shell input groups beginning with ".." are treated as TCL input, in
|
||||
** various ways, specifically: (1) When a bare ".." is entered, a TCL
|
||||
** REPL loop is run until the end of input is seen; (2) When "..X ..."
|
||||
** is entered, (where "X" is a dot-command name), the X dot command will
|
||||
** be run in its normal fashion, but its arguments will be collected
|
||||
** according to TCL parsing rules and will be expanded as is usual for
|
||||
** TCL commands; and (3) when ".. T ..." is entered, (where "T" is a TCL
|
||||
** command name), that TCL command and its arguments will be collected
|
||||
** and expanded according to TCL parsing rules, and the command will be
|
||||
** run in the TCL execution environment (in the root namespace), but the
|
||||
** shell execution environment remains in effect afterward. (Note that
|
||||
** cases 2 and 3 differ in whether space occurs after the leading "..".)
|
||||
**
|
||||
** The phrase "end of input" means either: end-of-file is seen on a file,
|
||||
** pipe or string stream input, or a lone "." on the first and only line
|
||||
** of an input line group is seen. This convention is useful in scripting
|
||||
** when it is expedient to switch execution environments from within the
|
||||
** same input stream. For example:
|
||||
# From shell, enter TCL REPL:
|
||||
..
|
||||
# Initialize some variables and insert into the DB:
|
||||
set var1 [compute_however ...]
|
||||
set var2 [derive_somehow ...]
|
||||
udb eval { INSERT INTO SomeTable(col1, col2) VALUES($var1, var2) }
|
||||
# Leave REPL
|
||||
.
|
||||
# Generate and keep pretty output:
|
||||
.mode box -ww
|
||||
.header on
|
||||
.once prettified.txt
|
||||
SELECT * FROM SomeTable;
|
||||
# Alternatively, the query can be run from the TCL environment:
|
||||
..
|
||||
set tstamp [clock format [clock seconds] -format %Y-%m-%d]
|
||||
.once "backup of prettified.txt made $tstamp"
|
||||
.eval {SELECT col1, col2 FROM SomeTable}
|
||||
# Return to shell environment:
|
||||
.
|
||||
**
|
||||
** For any of these ways of providing TCL input, the same TCL interpreter
|
||||
** is used, with its state maintained from one input to the next. In this
|
||||
** way, .sqliterc or other preparatory shell scripts (or typing) can be
|
||||
** made to provide a useful set of user-defined shell enhancements.
|
||||
** made to provide useful, user-defined shell enhancements or specialized
|
||||
** procedures (aka "TCL commands") for oft-repeated tasks.
|
||||
**
|
||||
** The added TCL commands are:
|
||||
** udb shdb ; # which expose the user DB and shell DB for access via TCL
|
||||
** now_interactive ; # which indicates whether input is interactive
|
||||
** get_tcl_group ; # which gets a single TCL input line group
|
||||
** .. ; # which does nothing, silently and without error
|
||||
** The .. command exists so that a lone ".." on an input line suffices
|
||||
** to ensure the TCL REPL is running. This is symmetric with a lone "."
|
||||
** input to the TCL REPL because it either terminates the loop or, if
|
||||
** entered in the shell environment, quietly does nothing without error.
|
||||
**
|
||||
** The added .tcl dot command may be used to enter a TCL REPL, or with
|
||||
** arguments, it will read files as TCL. (This is somewhat extraneous,
|
||||
** as the same can be done with TCL commands. This is more easily done
|
||||
** from the shell invocation, and the .tcl command's integration into
|
||||
** the .help facility provides a way for users to get help for "..".)
|
||||
**
|
||||
** The added .eval dot command may be used from the TCL environment to
|
||||
** run a SQL query or statement and obtain the shell's nice display or
|
||||
** other disposition of results. While the udb and shdb command allow
|
||||
** convenient access to the DB(s), they are not setup for display.
|
||||
*/
|
||||
|
||||
#include <limits.h>
|
||||
#include "shext_linkage.h"
|
||||
|
||||
static struct ShExtAPI *pShExtApi = 0;
|
||||
@@ -63,6 +115,7 @@ INCLUDE tclsqlite.c
|
||||
#endif
|
||||
|
||||
typedef struct TclCmd TclCmd;
|
||||
typedef struct EvalCmd EvalCmd;
|
||||
|
||||
static void TclCmd_Takedown(TclCmd *ptc);
|
||||
|
||||
@@ -72,19 +125,47 @@ static void TclCmd_Takedown(TclCmd *ptc);
|
||||
DERIVED_METHOD(void, destruct, MetaCommand,TclCmd, 0, ()){
|
||||
TclCmd_Takedown((TclCmd *)pThis);
|
||||
}
|
||||
DERIVED_METHOD(void, destruct, MetaCommand,EvalCmd, 0, ()){
|
||||
(void)(pThis);
|
||||
}
|
||||
|
||||
DERIVED_METHOD(const char *, name, MetaCommand,TclCmd, 0,()){
|
||||
return "tcl";
|
||||
}
|
||||
DERIVED_METHOD(const char *, name, MetaCommand,EvalCmd, 0,()){
|
||||
return "eval";
|
||||
}
|
||||
|
||||
DERIVED_METHOD(const char *, help, MetaCommand,TclCmd, 1,(int more)){
|
||||
switch( more ){
|
||||
case 0: return
|
||||
".tcl ?FILES? Run a TCL REPL or interpret files as TCL\n";
|
||||
".tcl ?FILES? Run a TCL REPL or interpret files as TCL.\n";
|
||||
case 1: return
|
||||
" If FILES are provided, they name files to be read in as TCL.\n"
|
||||
" Otherwise, a read/evaluate/print loop is run until a lone \".\"\n"
|
||||
" is entered on an input line or end-of-stream is encountered.\n";
|
||||
" If FILES are provided, they name files to be read in as TCL.\n"
|
||||
" Otherwise, a read/evaluate/print loop is run until a lone \".\" is\n"
|
||||
" entered as complete TCL input or input end-of-stream is encountered.\n"
|
||||
"\n"
|
||||
" The same REPL can be run with a lone \"..\". Or the \"..\" prefix\n"
|
||||
" may be used thusly, \"..dotcmd ...\" or \".. tclcmd ...\", to run a\n"
|
||||
" single dot command or TCL command, respectively, whereupon it will\n"
|
||||
" be run in its respective execution environment after its arguments\n"
|
||||
" are collected using TCL parsing rules and expanded as for TCL, in\n"
|
||||
" the TCL base namespace. In this way, arguments may be \"computed\".\n"
|
||||
;
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
DERIVED_METHOD(const char *, help, MetaCommand,EvalCmd, 1,(int more)){
|
||||
switch( more ){
|
||||
case 0: return
|
||||
".eval SQL ... Evaluate given SQL statements as shell does.\n";
|
||||
case 1: return
|
||||
" There is little use for this dot command without the TCL extension,\n"
|
||||
" as it merely does what can be done in the shell by direct entry of\n"
|
||||
" the same statements. Its utility is in the TCL environment, where\n"
|
||||
" statements may be computed or derived in a variety of ways, and\n"
|
||||
" one wishes to use the shell's result output rendering capability.\n"
|
||||
;
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
@@ -93,6 +174,10 @@ DERIVED_METHOD(DotCmdRC, argsCheck, MetaCommand,TclCmd, 3,
|
||||
(char **pzErrMsg, int nArgs, char *azArgs[])){
|
||||
return DCR_Ok;
|
||||
}
|
||||
DERIVED_METHOD(DotCmdRC, argsCheck, MetaCommand,EvalCmd, 3,
|
||||
(char **pzErrMsg, int nArgs, char *azArgs[])){
|
||||
return DCR_Ok;
|
||||
}
|
||||
|
||||
static Tcl_Interp *getInterp(TclCmd *ptc);
|
||||
|
||||
@@ -103,7 +188,7 @@ static void copy_complaint(char **pzErr, Tcl_Interp *pi){
|
||||
}
|
||||
}
|
||||
|
||||
/* The .tcl REPL script is one of the 3 following string literals,
|
||||
/* The .tcl/.. REPL script is one of the 3 following string literals,
|
||||
* selected at build time for these different purposes:
|
||||
* 1st: a simple input collection, reading only stdin, which may
|
||||
* be (handily) used as a fallback for debugging purposes.
|
||||
@@ -114,6 +199,7 @@ static void copy_complaint(char **pzErr, Tcl_Interp *pi){
|
||||
* input collection. It has higher shell dependency, and for
|
||||
* that it gains the shell's line editing and history recall,
|
||||
* in addition to working with the shell's input switching.
|
||||
* It also supports recursive REPLs when return is caught.
|
||||
*/
|
||||
#ifdef TCL_REPL_STDIN_ONLY
|
||||
# define TCL_REPL 1
|
||||
@@ -196,18 +282,19 @@ static const char * const zREPL =
|
||||
"namespace delete ::REPL\n"
|
||||
"read stdin 0\n"
|
||||
#elif TCL_REPL==3
|
||||
# define SHELL_REPL_CMDNAME "sqlite_shell_REPL"
|
||||
/* using shell's input collection with line editing (if configured) */
|
||||
"sqlite_shell_REPL"
|
||||
SHELL_REPL_CMDNAME
|
||||
#else
|
||||
"throw {NONE} {not built for REPL}\n"
|
||||
#endif
|
||||
; /* zREPL */
|
||||
|
||||
static const char * const zDefineREPL =
|
||||
"proc sqlite_shell_REPL {} {\n"
|
||||
"proc "SHELL_REPL_CMDNAME" {} {\n"
|
||||
"set interactive [now_interactive]\n"
|
||||
"while {1} {\n"
|
||||
"foreach {group ready} [get_input_line_group] {}\n"
|
||||
"foreach {group ready} [get_tcl_group] {}\n"
|
||||
"set trimmed [string trim $group]\n"
|
||||
"if {$group eq \"\" && !$ready} break\n"
|
||||
"if {$trimmed eq \"\"} continue\n"
|
||||
@@ -231,39 +318,62 @@ static const char * const zDefineREPL =
|
||||
"}\n"
|
||||
;
|
||||
|
||||
/* Enter the preferred REPL */
|
||||
static DotCmdRC runTclREPL(Tcl_Interp *interp, char **pzErrMsg){
|
||||
int rc = Tcl_Eval(interp, zREPL);
|
||||
clearerr(stdin); /* Cure issue where stdin gets stuck after keyboard EOF. */
|
||||
if( rc!=TCL_OK ){
|
||||
copy_complaint(pzErrMsg, interp);
|
||||
return DCR_Error;
|
||||
}
|
||||
return DCR_Ok;
|
||||
}
|
||||
|
||||
DERIVED_METHOD(DotCmdRC, execute, MetaCommand,TclCmd, 4,
|
||||
(ShellExState *psx, char **pzErrMsg, int nArgs, char *azArgs[])){
|
||||
FILE *out = pExtHelpers->currentOutputFile(psx);
|
||||
TclCmd *ptc = (TclCmd *)pThis;
|
||||
DotCmdRC rv = DCR_Ok;
|
||||
int rc = TCL_OK;
|
||||
if( nArgs>1 ){
|
||||
/* Read named files into the interpreter. */
|
||||
int rc = TCL_OK;
|
||||
int aix;
|
||||
for( aix=0; aix<(nArgs-1) && rc==TCL_OK; ++aix ){
|
||||
rc = Tcl_EvalFile(getInterp(ptc), azArgs[aix+1]);
|
||||
}
|
||||
if( rc!=TCL_OK ){
|
||||
copy_complaint(pzErrMsg, getInterp(ptc));
|
||||
return DCR_Error;
|
||||
}
|
||||
return DCR_Ok;
|
||||
}else{
|
||||
/* Enter a REPL */
|
||||
rc = Tcl_Eval(getInterp(ptc), zREPL);
|
||||
clearerr(stdin); /* Cure issue where stdin gets stuck after keyboard EOF. */
|
||||
return runTclREPL(getInterp(ptc), pzErrMsg);
|
||||
}
|
||||
if( rc!=TCL_OK ){
|
||||
copy_complaint(pzErrMsg, getInterp(ptc));
|
||||
rv = DCR_Error;
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
DERIVED_METHOD(DotCmdRC, execute, MetaCommand,EvalCmd, 4,
|
||||
(ShellExState *psx, char **pzErrMsg, int nArgs, char *azArgs[])){
|
||||
FILE *out = pExtHelpers->currentOutputFile(psx);
|
||||
fprintf(out, "The .eval command does nothing, yet.\n");
|
||||
return DCR_Ok;
|
||||
}
|
||||
|
||||
/* Define a MetaCommand v-table initialized to reference above methods. */
|
||||
/* Define MetaCommand v-tables initialized to reference above methods. */
|
||||
MetaCommand_IMPLEMENT_VTABLE(TclCmd, tclcmd_methods);
|
||||
MetaCommand_IMPLEMENT_VTABLE(EvalCmd, evalcmd_methods);
|
||||
|
||||
/* Static instances are used because that suffices and makes the
|
||||
* interpreter easy to reference without going through pointers. */
|
||||
INSTANCE_BEGIN(TclCmd);
|
||||
Tcl_Interp *interp;
|
||||
INSTANCE_END(TclCmd) tclcmd = {
|
||||
&tclcmd_methods
|
||||
, 0 /* interp pointer */
|
||||
};
|
||||
INSTANCE_BEGIN(EvalCmd);
|
||||
/* no instance data */
|
||||
INSTANCE_END(EvalCmd) evalcmd = {
|
||||
&evalcmd_methods
|
||||
};
|
||||
|
||||
static Tcl_Interp *getInterp(TclCmd *ptc){
|
||||
return ptc->interp;
|
||||
@@ -275,7 +385,11 @@ static void TclCmd_Takedown(TclCmd *ptc){
|
||||
Tcl_Finalize();
|
||||
}
|
||||
|
||||
/* Say line is script lead-in iff its first dark is "..". */
|
||||
/* Say line is script lead-in iff its first dark is "..".
|
||||
* In combination with dot commands also being TCL commands and the
|
||||
* special handling in the next three functions, this effects what is
|
||||
* promised in this file's header text and by .tcl's help text.
|
||||
*/
|
||||
static int tclIsScriptLead(void *pvState, const char *zLineLead){
|
||||
char c;
|
||||
(void)(pvState);
|
||||
@@ -283,11 +397,13 @@ static int tclIsScriptLead(void *pvState, const char *zLineLead){
|
||||
return (c=='.' && *zLineLead=='.');
|
||||
}
|
||||
|
||||
/* Say line group is complete if it passes muster as ready-to-go TCL. */
|
||||
static int tclIsComplete(void *pvState, const char *zScript){
|
||||
(void)(pvState);
|
||||
return Tcl_CommandComplete(zScript);
|
||||
}
|
||||
|
||||
/* Run as TCL after some jiggering with the leading dots. */
|
||||
static DotCmdRC tclRunScript(void *pvState, const char *zScript,
|
||||
ShellExState *p, char **pzErrMsg){
|
||||
char c;
|
||||
@@ -295,11 +411,40 @@ static DotCmdRC tclRunScript(void *pvState, const char *zScript,
|
||||
while( (c=*zScript++) && (c==' '||c=='\t') ) {}
|
||||
if( c=='.' && *zScript++=='.' ){
|
||||
int rc, nc = strlen30(zScript);
|
||||
rc = Tcl_EvalEx(ptc->interp, zScript, nc, TCL_EVAL_DIRECT|TCL_EVAL_GLOBAL);
|
||||
if( rc!=TCL_OK ) copy_complaint(pzErrMsg, getInterp(ptc));
|
||||
/* At this point, *zScript should fall into one of these cases: */
|
||||
switch( *zScript ){
|
||||
case '.':
|
||||
/* Three dots, assume user meant to run a dot command. */
|
||||
one_shot_tcl:
|
||||
rc = Tcl_EvalEx(ptc->interp, zScript, /* needs no adjustment */
|
||||
nc, TCL_EVAL_DIRECT|TCL_EVAL_GLOBAL);
|
||||
if( rc!=TCL_OK ) copy_complaint(pzErrMsg, getInterp(ptc));
|
||||
break;
|
||||
case ' ': case '\t':
|
||||
/* Two dots then whitespace, it's a TCL one-shot command. */
|
||||
while( (c = *zScript)!=0 && c==' ' || c=='\t' ) ++zScript, --nc;
|
||||
if ( c!=0 ) goto one_shot_tcl;
|
||||
/* It looks like "..", so run it that way via fall-thru. */
|
||||
case 0:
|
||||
/* Two lone dots, user wants to run TCL REPL. */
|
||||
return runTclREPL(ptc->interp, pzErrMsg);
|
||||
default:
|
||||
/* Two dots then dark not dot, may be a dot command. */
|
||||
if( *zScript>='a' && *zScript<='z' ){
|
||||
--zScript, ++nc;
|
||||
goto one_shot_tcl;
|
||||
}
|
||||
/* It cannot be a dot command; a user tip is apparently needed. */
|
||||
if( pzErrMsg ){
|
||||
*pzErrMsg = sqlite3_mprintf("Nothing valid begins with ..%c\n"
|
||||
"Run .help tcl to see what is valid.\n",
|
||||
*zScript);
|
||||
return DCR_SayUsage;
|
||||
}
|
||||
}
|
||||
return DCR_Ok|(rc!=TCL_OK);
|
||||
}
|
||||
return DCR_Error;
|
||||
return DCR_Error; /* Silent error because it should not happen. */
|
||||
}
|
||||
|
||||
#define GETLINE_MAXLEN 1000
|
||||
@@ -326,7 +471,7 @@ static int getInputLine(void *pvSS, Tcl_Interp *interp,
|
||||
#endif
|
||||
|
||||
#if TCL_REPL==3
|
||||
/* C implementation of TCL proc, get_input_line_group
|
||||
/* C implementation of TCL proc, get_tcl_group
|
||||
* This routine returns a 2 element list consisting of:
|
||||
* the collected input lines, joined with "\n", as a string
|
||||
* and
|
||||
@@ -565,9 +710,9 @@ static int udbEventHandle(void *pv, NoticeKind nk, void *pvSubject,
|
||||
if( nk==NK_ShutdownImminent ){
|
||||
udbCleanup(pudb);
|
||||
}else if( nk==NK_Unsubscribe ){
|
||||
assert(pudb==0 || (pudb->nRef==0 && pudb->ppSdb==0));
|
||||
assert(pudb==0 || pudb->numSdb==0);
|
||||
}else if( nk==NK_DbUserAppeared || nk==NK_DbUserVanishing
|
||||
|| nk==NK_DbAboutToClose ){
|
||||
|| nk==NK_DbAboutToClose || nk==NK_ExtensionUnload ){
|
||||
sqlite3 *dbSubject = (sqlite3*)pvSubject;
|
||||
int ix = udbIndexOfDb(pudb, dbSubject);
|
||||
switch( nk ){
|
||||
@@ -578,6 +723,10 @@ static int udbEventHandle(void *pv, NoticeKind nk, void *pvSubject,
|
||||
case NK_DbUserVanishing:
|
||||
if( ix>=0 ) pudb->ixuSdb = -1;
|
||||
break;
|
||||
case NK_ExtensionUnload:
|
||||
pShExtApi->subscribeEvents(psx, sqlite3_tclshext_init, 0,
|
||||
NK_Unsubscribe, udbEventHandle);
|
||||
/* fall thru */
|
||||
case NK_DbAboutToClose:
|
||||
if( ix>=0 ) udbRemove(pudb, ix);
|
||||
break;
|
||||
@@ -703,6 +852,7 @@ int sqlite3_tclshext_init(
|
||||
if( pShExtLink && pShExtLink->pShellExtensionAPI->numRegistrars>=1 ){
|
||||
ShellExState *psx = pShExtLink->pSXS;
|
||||
MetaCommand *pmc = (MetaCommand *)&tclcmd;
|
||||
MetaCommand *pec = (MetaCommand *)&evalcmd;
|
||||
const char *zShellName, *zShellDir;
|
||||
int rc;
|
||||
|
||||
@@ -729,6 +879,7 @@ int sqlite3_tclshext_init(
|
||||
TclCmd_Takedown(&tclcmd);
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
rc = pShExtApi->registerMetaCommand(psx, sqlite3_tclshext_init, pec);
|
||||
rc = pShExtApi->registerMetaCommand(psx, sqlite3_tclshext_init, pmc);
|
||||
if( rc==SQLITE_OK ){
|
||||
ScriptHooks sh = { pmc, tclIsScriptLead, tclIsComplete, tclRunScript };
|
||||
@@ -746,14 +897,18 @@ int sqlite3_tclshext_init(
|
||||
#endif
|
||||
#if TCL_REPL==3
|
||||
Tcl_CreateObjCommand(tclcmd.interp,
|
||||
"get_input_line_group", getInputLineGroup, psx, 0);
|
||||
"get_tcl_group", getInputLineGroup, psx, 0);
|
||||
Tcl_Eval(tclcmd.interp, zDefineREPL);
|
||||
#endif
|
||||
Tcl_CreateCommand(tclcmd.interp,
|
||||
"now_interactive", nowInteractive, psx, 0);
|
||||
/* Rename unknown so that calls to it can be intercepted. */
|
||||
Tcl_Eval(tclcmd.interp, "rename unknown "UNKNOWN_RENAME);
|
||||
Tcl_CreateCommand(tclcmd.interp,
|
||||
"unknown", unknownDotDelegate, psx, 0);
|
||||
/* Define this proc so that ".." either gets to the TCL REPL loop
|
||||
* or does nothing (if already in it), as a user convenience. */
|
||||
Tcl_Eval(tclcmd.interp, "proc .. {} {}");
|
||||
pShExtLink->eid = sqlite3_tclshext_init;
|
||||
}else{
|
||||
TclCmd_Takedown(&tclcmd);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
C In\sTCL\sshell\sextension,\swrap\sshell\sDB\sfor\suse\sfrom\sTCL.
|
||||
D 2022-03-28T05:57:07.930
|
||||
C TCL\sextension\spolished\sand\smore\ssmoothly\sintegrated.\sA\scouple\sof\sbugs\srelated\sto\stakedown\sfixed.\sHelp\sfor\sadded\sfeatures.\sCode\scleaned\sup\sand\scommented.
|
||||
D 2022-03-28T21:42:16.234
|
||||
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
|
||||
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
|
||||
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
|
||||
@@ -329,7 +329,7 @@ F ext/misc/showauth.c 732578f0fe4ce42d577e1c86dc89dd14a006ab52
|
||||
F ext/misc/spellfix.c 94df9bbfa514a563c1484f684a2df3d128a2f7209a84ca3ca100c68a0163e29f
|
||||
F ext/misc/sqlar.c 0ace5d3c10fe736dc584bf1159a36b8e2e60fab309d310cd8a0eecd9036621b6
|
||||
F ext/misc/stmt.c 35063044a388ead95557e4b84b89c1b93accc2f1c6ddea3f9710e8486a7af94a
|
||||
F ext/misc/tclshext.c.in c78715b514667807c152c4cd1bccc054f848ca43f3c3367047e1a0a78cb1c5ce
|
||||
F ext/misc/tclshext.c.in db6ec20db5c11f8ca808232d569ed9cd6173ae4d5de49ddee891b980d1a181ff
|
||||
F ext/misc/templatevtab.c 8a16a91a5ceaccfcbd6aaaa56d46828806e460dd194965b3f77bf38f14b942c4
|
||||
F ext/misc/totype.c fa4aedeb07f66169005dffa8de3b0a2b621779fd44f85c103228a42afa71853b
|
||||
F ext/misc/uint.c 053fed3bce2e89583afcd4bf804d75d659879bbcedac74d0fa9ed548839a030b
|
||||
@@ -556,8 +556,8 @@ F src/random.c 097dc8b31b8fba5a9aca1697aeb9fd82078ec91be734c16bffda620ced7ab83c
|
||||
F src/resolve.c ea935b87d6fb36c78b70cdc7b28561dc8f33f2ef37048389549c7b5ef9b0ba5e
|
||||
F src/rowset.c ba9515a922af32abe1f7d39406b9d35730ed65efab9443dc5702693b60854c92
|
||||
F src/select.c c366c05e48e836ea04f8ecefb9c1225745dc250c3f01bdb39e9cbb0dc25e3610
|
||||
F src/shell.c.in c95182f95dc2b1278214812e02fb5131a847851c93b95c548a8a825b87f47aa1 x
|
||||
F src/shext_linkage.h 68aca955c86a0ef7d1f007a4e776a7e9c17542cfde66195db1d7333695fcef7d
|
||||
F src/shell.c.in 15c883304f6a757ec9b6c4e2f6720a5d9a2066ed0b88348a36bcf5c1b11c1f7c x
|
||||
F src/shext_linkage.h c70f95dd0738c2cd8452ab5c47c245d7fa3b99ec26cd160c67dfb4489d5dacf9
|
||||
F src/sqlite.h.in 2a35f62185eb5e7ecc64a2f68442b538ce9be74f80f28a00abc24837edcf1c17
|
||||
F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
|
||||
F src/sqlite3ext.h f49e28c25bd941e79794db5415fdf7b202deb3bc072ed6f1ed273d578703684e
|
||||
@@ -1951,8 +1951,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
|
||||
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
|
||||
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
|
||||
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
|
||||
P fbf0eb0d12932513bba4c6a6ef31d9972d704ab38690c806098504a4cd67786d
|
||||
R d234ea22bba902ea03fc077d174e9a9f
|
||||
P 41cc84336bbf7bc64492c24e1bf5be0fccbb8a3db57498070b624af8818d0075
|
||||
R a6fcd98d9f55917ef71d90740fb27226
|
||||
U larrybr
|
||||
Z cd9ddf3319fb6a9468c36fb31583d848
|
||||
Z 385f3d338e553fbadafb70a790af574d
|
||||
# Remove this line to create a well-formed Fossil manifest.
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
41cc84336bbf7bc64492c24e1bf5be0fccbb8a3db57498070b624af8818d0075
|
||||
4ee7df118033af368854992763d96f860f8bd218b28b9ad27dd7ad4291cabfd7
|
||||
+36
-5
@@ -7785,6 +7785,7 @@ static int register_meta_command(ShellExState *p,
|
||||
if( rc==SQLITE_DONE ){
|
||||
psei->ppMetaCommands[nc++] = pMC;
|
||||
psei->numMetaCommands = nc;
|
||||
notify_subscribers(psi, NK_NewDotCommand, pMC);
|
||||
return SQLITE_OK;
|
||||
}else{
|
||||
psei->ppMetaCommands[nc] = 0;
|
||||
@@ -7840,7 +7841,7 @@ static int subscribe_events(ShellExState *p, ExtensionId eid, void *pvUserData,
|
||||
if( (eventHandler==0 || eventHandler==pes->eventHandler)
|
||||
&& (pes->eid==0 || eid==eid)
|
||||
&& (eid!=0 ||eventHandler!=0 ||/* for shell use */ pvUserData==p ) ){
|
||||
int nLeft = pesLim - pesLim;
|
||||
int nLeft = pesLim - pes;
|
||||
assert(pes->eventHandler!=0);
|
||||
pes->eventHandler(pes->pvUserData, NK_Unsubscribe, pes->eid, p);
|
||||
if( nLeft>1 ) memmove(pes, pes+1, (nLeft-1)*sizeof(*pes));
|
||||
@@ -7877,6 +7878,26 @@ static int subscribe_events(ShellExState *p, ExtensionId eid, void *pvUserData,
|
||||
return SQLITE_OK;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Unsubscribe all event listeners having an ExtensionId > 0. This is
|
||||
* done just prior closing the shell DB (when dynamic extensions will
|
||||
* be unloaded and accessing them in any way is good for a crash.)
|
||||
*/
|
||||
static void unsubscribe_extensions(ShellInState *psi){
|
||||
ShellExState *psx = XSS(psi);
|
||||
int esix = 0;
|
||||
|
||||
if( psi->numExtLoaded<=1 ) return; /* Ignore shell pseudo-extension. */
|
||||
while( esix<psi->numSubscriptions ){
|
||||
struct EventSubscription *pes = psi->pSubscriptions+esix;
|
||||
if( pes->eid > 0 ){
|
||||
int nsin = psi->numSubscriptions;
|
||||
subscribe_events(psx, pes->eid, psx, NK_Unsubscribe, 0);
|
||||
esix = esix + 1 + (psi->numSubscriptions - nsin);
|
||||
}else ++esix;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static FILE *currentOutputFile(ShellExState *p){
|
||||
@@ -13439,8 +13460,7 @@ static DotCmdRC meta_command_errors(char *zErr, char *azArg[], int nArg,
|
||||
pArg1st = azArg[0];
|
||||
break;
|
||||
default:
|
||||
assert(0);
|
||||
z = "?";
|
||||
z = "? Bizarre return from %s";
|
||||
break;
|
||||
}
|
||||
if( !zErr ){
|
||||
@@ -15146,9 +15166,20 @@ int SQLITE_CDECL SHELL_MAIN(int argc, wchar_t **wargv){
|
||||
sqlite3_free(data.zEditor);
|
||||
#if SHELL_DYNAMIC_EXTENSION
|
||||
notify_subscribers(&data, NK_DbAboutToClose, datax.dbShell);
|
||||
sqlite3_close(datax.dbShell);
|
||||
notify_subscribers(&data, NK_ShutdownImminent, 0);
|
||||
/* It is necessary that the shell DB be closed after the user DBs.
|
||||
* This is because loaded extensions are held by the shell DB and
|
||||
* are therefor (automatically) unloaded when it is closed. */
|
||||
notify_subscribers(&data, NK_ExtensionUnload, datax.dbShell);
|
||||
/* Forcefully unsubscribe any extension which ignored above or did
|
||||
* not unsubscribe upon getting above event. */
|
||||
unsubscribe_extensions(&data);
|
||||
/* This must be done before any extensions unload. */
|
||||
free_all_shext_tracking(&data);
|
||||
/* Unload extensions and free the DB used for dealing with them. */
|
||||
sqlite3_close(datax.dbShell);
|
||||
/* This notification can only reach statically linked extensions. */
|
||||
notify_subscribers(&data, NK_ShutdownImminent, 0);
|
||||
/* Forcefull unsubscribe static extension event listeners. */
|
||||
subscribe_events(&datax, 0, &datax, NK_Unsubscribe, 0);
|
||||
#endif
|
||||
#if !SQLITE_SHELL_IS_UTF8
|
||||
|
||||
+7
-2
@@ -277,8 +277,13 @@ typedef enum {
|
||||
* pvSubject is the newly set .dbUser value. */
|
||||
NK_DbUserVanishing, /* Current ShellExState .dbUser will soon vanish,
|
||||
* pvSubject is the vanishing .dbUser value. */
|
||||
NK_DbAboutToClose, /* A ShellExState-visible DB will soon be closed,
|
||||
* pvSubject is the sqlite3 pointer soon to close. */
|
||||
NK_DbAboutToClose, /* A possibly ShellExState-visible DB will soon be
|
||||
* closed, pvSubject is the DB's sqlite3 pointer. */
|
||||
NK_ExtensionUnload, /* The ShellExState .dbShell DB will soon be closed,
|
||||
* soon to be followed by unloading of all dynamic
|
||||
* extensions; pvSubject is the DB's sqlite3 pointer. */
|
||||
NK_NewDotCommand, /* A new MetaCommand has been registered, pvSubject
|
||||
* is the just-added MetaCommand object (pointer). */
|
||||
NK_CountOf /* Present count of preceding members (evolves) */
|
||||
} NoticeKind;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user