-C Get\sshell\sTCL\sextension\smore\ssmoothly\sintegrated.
-D 2022-03-25T06:46:20.823
+C CLI\spreparation\sfor\sshell's\sdb\sto\sbe\sused\sfrom\sTCL
+D 2022-03-25T18:27:47.085
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
F src/resolve.c ea935b87d6fb36c78b70cdc7b28561dc8f33f2ef37048389549c7b5ef9b0ba5e
F src/rowset.c ba9515a922af32abe1f7d39406b9d35730ed65efab9443dc5702693b60854c92
F src/select.c 4890a3cfee0bc60ff231c3a44db37968859ab0be156983dbcc0c096109832cdd
-F src/shell.c.in 06593f6e93bbbcb5e58e29f1100ff1fa9e211642dc946d28ef1748be06383854
-F src/shext_linkage.h 511a218406b45240b3ad8a849a0898fac52ea64f7ad133548a9126d0f7d637cf
+F src/shell.c.in dd69e8a81fd8fc8a0fc1c470fa5c028ec01b3d106afc26cfa84399e534405e73
+F src/shext_linkage.h 113e2e22378002999e297747b5651d3550cdd5b573a1977d274be5a7390d00c0
F src/sqlite.h.in 5845213799feca09cd69d18ff841a85fe0df31021f46aaa1797e703e80dc1d70
F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
F src/sqlite3ext.h f49e28c25bd941e79794db5415fdf7b202deb3bc072ed6f1ed273d578703684e
F src/test_rtree.c 671f3fae50ff116ef2e32a3bf1fe21b5615b4b7b
F src/test_schema.c f5d6067dfc2f2845c4dd56df63e66ee826fb23877855c785f75cc2ca83fd0c1b
F src/test_server.c a2615049954cbb9cfb4a62e18e2f0616e4dc38fe
-F src/test_shellext.c 6cbc7cbc4c1c55a747fcc8367fb9b45fe3d2d253e1b4235c8d4c4e6bd667b5ce
+F src/test_shellext.c c864d87b4a1dc9fb47e8aeaa69d8a7f236a1346b1187e248634bb797afc3892a
F src/test_sqllog.c 540feaea7280cd5f926168aee9deb1065ae136d0bbbe7361e2ef3541783e187a
F src/test_superlock.c 4839644b9201da822f181c5bc406c0b2385f672e
F src/test_syscall.c 1073306ba2e9bfc886771871a13d3de281ed3939
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
-P 353943108b36cabb3b9c745aa7f4d9a42cd100b7d0444373184b45cc1715837e
-R b9914f8d3b5384fdfccd2318410fb67e
+P 29fd246fb2605a2731dd6327a354daedd8182225a97234939c79fc66e1e9e83e
+R 20ee2f610aa91298bb1bd5f8f19a382e
U larrybr
-Z 0021f50f48c3d55a3facb3cd03bb67af
+Z 795c799e5971cf2292cc8435f8c2bc8b
# Remove this line to create a well-formed Fossil manifest.
ExpertInfo expert; /* Valid if previous command was ".expert OPT..." */
#if SHELL_DYNAMIC_EXTENSION
+ /* extension management */
int numExtLoaded; /* Number of extensions presently loaded or emulated */
ShExtInfo *pShxLoaded; /* Tracking and use info for loaded shell extensions */
int ixExtPending; /* Index of pending extension load operations if !0 */
+ /* scripting integration */
ScriptHooks scripting; /* Hooks for scripting support from loaded extension */
ExtensionId scriptXid; /* Id of extension which has set scripting hooks */
+ /* shell event subscription list */
+ int numSubscriptions; /* Number of active entries in below list */
+ struct EventSubscription {
+ ExtensionId eid;
+ void *pvUserData;
+ ShellEventNotify eventHandler;
+ } *pSubscriptions; /* The currently active shell event subscriptions */
#endif
ShellExState *pSXS; /* Pointer to companion, exposed shell state */
}
#endif
+#if SHELL_DYNAMIC_EXTENSION
+static int notify_subscribers(ShellInState *psi, NoticeKind nk) {
+ int six = 0;
+ int rcFlags = 0;
+ ShellExState *psx = XSS(psi);
+ while( six < psi->numSubscriptions ){
+ struct EventSubscription *pes = psi->pSubscriptions + six++;
+ rcFlags |= pes->eventHandler(pes->pvUserData, nk, psx);
+ }
+ return rcFlags;
+}
+#endif
+
/*
** Try to deduce the type of file for zName based on its content. Return
** one of the SHELL_OPEN_* constants.
}
}
#endif
- }
- if( psi->bSafeModeFuture && DBX(psx)!=0 ){
- sqlite3_set_authorizer(DBX(psx), safeModeAuth, psx);
+ if( psi->bSafeModeFuture && DBX(psx)!=0 ){
+ sqlite3_set_authorizer(DBX(psx), safeModeAuth, psx);
+ }
+#if SHELL_DYNAMIC_EXTENSION
+ notify_subscribers(psi, NK_DbUserAppeared);
+#endif
}
}
/*
-** Attempt to close the databaes connection. Report errors.
+** Attempt to close the database connection. Report errors.
*/
void close_db(sqlite3 *db){
int rc;
return SQLITE_OK;
}
+#if SHELL_DYNAMIC_EXTENSION
+/*
+ * Subscribe to (or unsubscribe from) messages about various changes.
+ * Unsubscribe, effected when nkMin==NK_Unsubscribe, always succeeds.
+ * Return SQLITE_OK on success, or one of these error codes:
+ * SQLITE_ERROR when the nkMin value is unsupported by this host;
+ * SQLITE_NOMEM when a required allocation failed; or
+ * SQLITE_MISUSE when the provided eid or eventHandler is invalid.
+ */
+static int subscribe_events(ShellExState *p, ExtensionId eid, void *pvUserData,
+ NoticeKind nkMin, ShellEventNotify eventHandler){
+ ShellInState *psi = ISS(p);
+ struct EventSubscription *pes = psi->pSubscriptions;
+ struct EventSubscription *pesLim = pes + psi->numSubscriptions;
+ if( nkMin==NK_Unsubscribe ){
+ /* unsubscribe (if now subscribed) */
+ while( pes < pesLim ){
+ if( (eventHandler==0 || eventHandler==pes->eventHandler)
+ && (pes->eid==0 || eid==eid)
+ && (eid!=0 ||eventHandler!=0 ||/* for shell use */ pvUserData==p ) ){
+ int nLeft = pesLim - pesLim;
+ assert(pes->eventHandler!=0);
+ pes->eventHandler(pes->pvUserData, NK_Unsubscribe, p);
+ if( nLeft>1 ) memmove(pes, pes+1, (nLeft-1)*sizeof(*pes));
+ --pesLim;
+ --psi->numSubscriptions;
+ }else{
+ ++pes;
+ }
+ }
+ if( psi->numSubscriptions==0 ){
+ sqlite3_free(psi->pSubscriptions);
+ psi->pSubscriptions = 0;
+ }
+ return SQLITE_OK;
+ }else{
+ /* subscribe only if minimum NoticeKind supported by this host */
+ if( nkMin > NK_CountOf ) return SQLITE_ERROR;
+ if( eventHandler==0 || eid==0 ) return SQLITE_MISUSE;
+ while( pes < pesLim ){
+ /* Never add duplicate handlers, but may renew their user data. */
+ if( pes->eid==eid && pes->eventHandler==eventHandler ){
+ pes->pvUserData = pvUserData;
+ return SQLITE_OK;
+ }
+ }
+ assert(pes==pesLim);
+ pes = sqlite3_realloc(pes, (psi->numSubscriptions+1)*sizeof(*pes));
+ if( pes==0 ) return SQLITE_NOMEM;
+ psi->pSubscriptions = pes;
+ pes += (psi->numSubscriptions++);
+ pes->eid = eid;
+ pes->pvUserData = pvUserData;
+ pes->eventHandler = eventHandler;
+ return SQLITE_OK;
+ }
+}
+#endif
static FILE *currentOutputFile(ShellExState *p){
return ISS(p)->out;
};
static ShellExtensionAPI shellExtAPI = {
- &extHelpers, 4, {
+ &extHelpers, 5, {
register_meta_command,
register_out_mode,
register_importer,
hook_scripting,
+ subscribe_events,
0
}
};
if( psi->pAuxDb != &psi->aAuxDb[i] && i>=0 && i<ArraySize(psi->aAuxDb) ){
psi->pAuxDb->db = DBI(psi);
psi->pAuxDb = &psi->aAuxDb[i];
+#if SHELL_DYNAMIC_EXTENSION
+ if( DBI(psi)!=0 ) notify_subscribers(psi, NK_DbUserVanishing);
+#endif
globalDb = DBI(psi) = psi->pAuxDb->db;
+#if SHELL_DYNAMIC_EXTENSION
+ if( DBI(psi)!=0 ) notify_subscribers(psi, NK_DbUserAppeared);
+#endif
psi->pAuxDb->db = 0;
}
}else if( nArg==3 && strcmp(azArg[1], "close")==0
}
/* Close the existing database */
+#if SHELL_DYNAMIC_EXTENSION
+ if( DBX(p)!=0 ) notify_subscribers(psi, NK_DbUserVanishing);
+#endif
session_close_all(psi, -1);
close_db(DBX(p));
DBX(p) = 0;
shell_bail:
set_table_name(&datax, 0);
if( datax.dbUser ){
+#if SHELL_DYNAMIC_EXTENSION
+ notify_subscribers(&data, NK_DbUserVanishing);
+#endif
session_close_all(&data, -1);
close_db(datax.dbUser);
}
clearTempFile(&data);
sqlite3_free(data.zEditor);
#if SHELL_DYNAMIC_EXTENSION
+ notify_subscribers(&data, NK_ShutdownImminent);
free_all_shext_tracking(&data);
+ subscribe_events(&datax, 0, &datax, NK_Unsubscribe, 0);
sqlite3_close(datax.dbShell);
#endif
#if !SQLITE_SHELL_IS_UTF8
} helpers;
} ExtensionHelpers;
+/* This enum is stable excepting that it grows at the end. Members will not
+ * change value across successive shell versions, except for NK_CountOf. An
+ * extension which is built to rely upon particular notifications can pass
+ * an NK_CountOf value upon which it relies to subscribe(...) as nkMin,
+ * which will fail if the hosting shell's NK_CountOf value is lower.
+ */
+typedef enum {
+ NK_Unsubscribe, /* event handler is being unsubsribed
+ * Also passed to subscribeEvents(...) as nkMin
+ * to unsubscribe event handler(s) */
+ NK_ShutdownImminent, /* a shell exit (or return) will soon occur */
+ NK_DbUserAppeared, /* a new ShellExState .dbUser value has been set */
+ NK_DbUserVanishing, /* current ShellExState .dbUser will soon vanish */
+ NK_CountOf /* present count of preceding members (evolves) */
+} NoticeKind;
+
+/* Callback signature for shell event handlers. */
+typedef
+int (*ShellEventNotify)(void *pvUserData, NoticeKind nk, ShellExState *psx);
+
/* Various shell extension helpers and feature registration functions */
typedef struct ShellExtensionAPI {
/* Utility functions for use by extensions */
ExtensionHelpers * pExtHelpers;
- /* Functions for extension to register its implementors with shell */
+ /* Functions for an extension to register its implementors with shell */
const int numRegistrars; /* 4 for this version */
union {
struct ShExtAPI {
/* Provide scripting support to host shell. (See ScriptHooks above.) */
int (*hookScripting)(ShellExState *p,
ExtensionId eid, ScriptHooks *pSH);
+ /* Subscribe to (or unsubscribe from) messages about various changes. */
+ int (*subscribeEvents)(ShellExState *p, ExtensionId eid, void *pvUserData,
+ NoticeKind nkMin, ShellEventNotify eventHandler);
/* Preset to 0 at extension load, a sentinel for expansion */
void (*sentinel)(void);
} named;
}
}
+static int shellEventHandle(void *pv, NoticeKind nk, ShellExState *psx){
+ FILE *out = pExtHelpers->currentOutputFile(psx);
+ if( nk==NK_ShutdownImminent ){
+ BatBeing *pbb = (BatBeing *)pv;
+ fprintf(out, "Bat cave meteor strike detected after %d calls.\n",
+ pbb->numCalls);
+ }else if( nk==NK_Unsubscribe ){
+ fprintf(out, "BatBeing incommunicado.\n");
+ }else if( nk==NK_DbUserAppeared || nk==NK_DbUserVanishing ){
+ const char *zWhat = (nk==NK_DbUserAppeared)? "appeared" : "vanishing";
+ fprintf(out, "dbUser (%p) %s\n", psx->dbUser, zWhat);
+ }
+ return 0;
+}
DEFINE_SHDB_TO_SHEXTLINK(shext_link);
pShExtApi = & pShExtLink->pShellExtensionAPI->api.named;
pExtHelpers = & pShExtLink->pShellExtensionAPI->pExtHelpers->helpers.named;
+ pShExtApi->subscribeEvents(psx, sqlite3_testshellext_init, &batty,
+ NK_CountOf, shellEventHandle);
batty.pPrint = pExtHelpers->findMetaCommand("print", psx, &rc);
- rc = pShExtApi->registerMetaCommand(psx, sqlite3_testshellext_init,pmc);
+ rc = pShExtApi->registerMetaCommand(psx, sqlite3_testshellext_init, pmc);
if( rc!=0 ) ++nErr;
pShExtLink->eid = sqlite3_testshellext_init;
}