From: larrybr Date: Fri, 25 Mar 2022 18:27:47 +0000 (+0000) Subject: CLI preparation for shell's db to be used from TCL X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=09b83080455efd86997cee2bf90f31d4015193f0;p=thirdparty%2Fsqlite.git CLI preparation for shell's db to be used from TCL FossilOrigin-Name: 57501e7fbda84a22cd8ab15beff8af4766a3851aa3c6c705484fe1eab808caa9 --- diff --git a/manifest b/manifest index b1f4be46ec..03860984ab 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -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 @@ -556,8 +556,8 @@ F src/random.c 097dc8b31b8fba5a9aca1697aeb9fd82078ec91be734c16bffda620ced7ab83c 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 @@ -606,7 +606,7 @@ F src/test_quota.h 2a8ad1952d1d2ca9af0ce0465e56e6c023b5e15d 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 @@ -1950,8 +1950,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 353943108b36cabb3b9c745aa7f4d9a42cd100b7d0444373184b45cc1715837e -R b9914f8d3b5384fdfccd2318410fb67e +P 29fd246fb2605a2731dd6327a354daedd8182225a97234939c79fc66e1e9e83e +R 20ee2f610aa91298bb1bd5f8f19a382e U larrybr -Z 0021f50f48c3d55a3facb3cd03bb67af +Z 795c799e5971cf2292cc8435f8c2bc8b # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 6feb5de2bb..c588a02a76 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -29fd246fb2605a2731dd6327a354daedd8182225a97234939c79fc66e1e9e83e \ No newline at end of file +57501e7fbda84a22cd8ab15beff8af4766a3851aa3c6c705484fe1eab808caa9 \ No newline at end of file diff --git a/src/shell.c.in b/src/shell.c.in index e0be6627c6..97f80dac85 100644 --- a/src/shell.c.in +++ b/src/shell.c.in @@ -1372,11 +1372,20 @@ typedef struct ShellInState { 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 */ @@ -4680,6 +4689,19 @@ static int session_filter(void *pCtx, const char *zTab){ } #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. @@ -5110,14 +5132,17 @@ static void open_db(ShellExState *psx, int openFlags){ } } #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; @@ -7779,6 +7804,64 @@ static int hook_scripting(ShellExState *p, ExtensionId eid, ScriptHooks *pSH){ 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; @@ -7823,11 +7906,12 @@ static ExtensionHelpers extHelpers = { }; static ShellExtensionAPI shellExtAPI = { - &extHelpers, 4, { + &extHelpers, 5, { register_meta_command, register_out_mode, register_importer, hook_scripting, + subscribe_events, 0 } }; @@ -8305,7 +8389,13 @@ DISPATCHABLE_COMMAND( connection ? 1 4 ){ if( psi->pAuxDb != &psi->aAuxDb[i] && i>=0 && iaAuxDb) ){ 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 @@ -10100,6 +10190,9 @@ DISPATCHABLE_COMMAND( open 3 1 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; @@ -15009,6 +15102,9 @@ int SQLITE_CDECL SHELL_MAIN(int argc, wchar_t **wargv){ 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); } @@ -15027,7 +15123,9 @@ int SQLITE_CDECL SHELL_MAIN(int argc, wchar_t **wargv){ 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 diff --git a/src/shext_linkage.h b/src/shext_linkage.h index 7f4c24b94f..6e49a36f4d 100644 --- a/src/shext_linkage.h +++ b/src/shext_linkage.h @@ -243,12 +243,32 @@ typedef struct ExtensionHelpers { } 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 { @@ -264,6 +284,9 @@ typedef struct ShellExtensionAPI { /* 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; diff --git a/src/test_shellext.c b/src/test_shellext.c index 5be936720e..67a1d0fe97 100644 --- a/src/test_shellext.c +++ b/src/test_shellext.c @@ -93,6 +93,20 @@ static void sayHowMany( BatBeing *pbb, FILE *out, ShellExState *psx ){ } } +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); @@ -118,8 +132,10 @@ int sqlite3_testshellext_init( 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; }