]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
CLI preparation for shell's db to be used from TCL
authorlarrybr <larrybr@noemail.net>
Fri, 25 Mar 2022 18:27:47 +0000 (18:27 +0000)
committerlarrybr <larrybr@noemail.net>
Fri, 25 Mar 2022 18:27:47 +0000 (18:27 +0000)
FossilOrigin-Name: 57501e7fbda84a22cd8ab15beff8af4766a3851aa3c6c705484fe1eab808caa9

manifest
manifest.uuid
src/shell.c.in
src/shext_linkage.h
src/test_shellext.c

index b1f4be46ec718941cf634b2ff60d0667fa6ec0c6..03860984ab970a7b4ecfb73f001936b3b14ceeae 100644 (file)
--- 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.
index 6feb5de2bb088c7136b9712a0b0694f3ae826e19..c588a02a7674d142ada5013d0c0de20f19b9ae7f 100644 (file)
@@ -1 +1 @@
-29fd246fb2605a2731dd6327a354daedd8182225a97234939c79fc66e1e9e83e
\ No newline at end of file
+57501e7fbda84a22cd8ab15beff8af4766a3851aa3c6c705484fe1eab808caa9
\ No newline at end of file
index e0be6627c615744880ad787ee5897d795dc6c3f0..97f80dac85d5000f9644d48197e3b9824a221505 100644 (file)
@@ -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 && 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
@@ -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
index 7f4c24b94f160f38c234ac249048a0126ec98814..6e49a36f4db5239595ca680ecf6b2edf7a79abcd 100644 (file)
@@ -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;
index 5be936720ee9637a210e76b048c29eb6a0bd6ff6..67a1d0fe972a9878917ca48db143b73870dd7d18 100644 (file)
@@ -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;
   }