]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
CLI extension's registerMetaCommand done, and used by test_shellext.c (demo code)
authorlarrybr <larrybr@noemail.net>
Fri, 11 Mar 2022 22:59:24 +0000 (22:59 +0000)
committerlarrybr <larrybr@noemail.net>
Fri, 11 Mar 2022 22:59:24 +0000 (22:59 +0000)
FossilOrigin-Name: 9be5e0b503f63dbbf1375460ce74a0aad6f202c2e5b2a748bba1d3af82c7d2d8

manifest
manifest.uuid
src/obj_interfaces.h
src/shell.c.in
src/shext_linkage.h
src/test_shellext.c
tool/mkshellc.tcl

index 202128825b20cb982e1a1d385fc87a04d37256b3..a589a60da3ea94ba05083fdcd23f680072240d11 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C (WIP)\sAdd\sDB-dispatch\sfor\sdot\scommands\supon\sshell\sextension\sload.
-D 2022-03-08T16:44:55.714
+C CLI\sextension's\sregisterMetaCommand\sdone,\sand\sused\sby\stest_shellext.c\s(demo\scode)
+D 2022-03-11T22:59:24.198
 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
 F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
@@ -533,7 +533,7 @@ F src/mutex_noop.c 9d4309c075ba9cc7249e19412d3d62f7f94839c4
 F src/mutex_unix.c dd2b3f1cc1863079bc1349ac0fec395a500090c4fe4e11ab775310a49f2f956d
 F src/mutex_w32.c caa50e1c0258ac4443f52e00fe8aaea73b6d0728bd8856bedfff822cae418541
 F src/notify.c 89a97dc854c3aa62ad5f384ef50c5a4a11d70fcc69f86de3e991573421130ed6
-F src/obj_interfaces.h 194b36e15805ee8a1d8056986ccc3cb5762b4ca55ae2deb13ceb5adde965f8e6
+F src/obj_interfaces.h ea17e13ea521955fcdbeb2257b2439d2047b4c5d042a971a4639846023a9f373
 F src/os.c b1c4f2d485961e9a5b6b648c36687d25047c252222e9660b7cc25a6e1ea436ab
 F src/os.h 26890f540b475598cd9881dcc68931377b8d429d3ea3e2eeb64470cde64199f8
 F src/os_common.h b2f4707a603e36811d9b1a13278bffd757857b85
@@ -555,8 +555,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 be543c63ec129a39321dc1ba906f97913d849444bb5b0613c155861ed8db3891
-F src/shext_linkage.h d93e4c1df29eac40bdc268d006c9632d9235a6cfe7cb8bc3e04e9855b5ca8b26
+F src/shell.c.in 8ecf1834819828c7ba2c375722c01a3db346ce5fd8d1313839c14d89db517259
+F src/shext_linkage.h 6a9830f48061677ead5c05b537af3452b655f088db3a1298d1bbdd71279d8fe1
 F src/sqlite.h.in e82ac380b307659d0892f502b742f825504e78729f4edaadce946003b9c00816
 F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
 F src/sqlite3ext.h a95cb9ed106e3d39e2118e4dcc15a14faec3fa50d0093425083d340d9dfd96e6
@@ -605,7 +605,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 ea066d41fb25c55a3cf8b65f82e8beacc5ac477a19b770822003c1e48a9ab03b
+F src/test_shellext.c 295696a0f7e8663f80c9e63e90ce2dba7e42739a4e0d498e040797a5610cbd60
 F src/test_sqllog.c 540feaea7280cd5f926168aee9deb1065ae136d0bbbe7361e2ef3541783e187a
 F src/test_superlock.c 4839644b9201da822f181c5bc406c0b2385f672e
 F src/test_syscall.c 1073306ba2e9bfc886771871a13d3de281ed3939
@@ -1883,7 +1883,7 @@ F tool/mkopcodec.tcl 33d20791e191df43209b77d37f0ff0904620b28465cca6990cf8d60da61
 F tool/mkopcodeh.tcl 5dab48c49a25452257494e9601702ab63adaba6bd54a9b382615fa52661c8f8c
 F tool/mkopts.tcl 680f785fdb09729fd9ac50632413da4eadbdf9071535e3f26d03795828ab07fa
 F tool/mkpragmatab.tcl bd07bd59d45d0f3448e123d6937e9811195f9908a51e09d774609883055bfd3d
-F tool/mkshellc.tcl 173780dab3cd26a7c58a05e401af6d2fa2d12187f349b86cec3e2cafe7526678
+F tool/mkshellc.tcl 8231651ce215baea7d003d7489d962d0dc286cbabc41a858790dd188fe889651
 F tool/mksourceid.c 36aa8020014aed0836fd13c51d6dc9219b0df1761d6b5f58ff5b616211b079b9
 F tool/mkspeedsql.tcl a1a334d288f7adfe6e996f2e712becf076745c97
 F tool/mksqlite3c-noext.tcl 4f7cfef5152b0c91920355cbfc1d608a4ad242cb819f1aea07f6d0274f584a7f
@@ -1949,8 +1949,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 16af0e4560349f720d4e2fbb4c08211e6a9a573c1fb4eb287d8bc2aefc3c7ce0
-R ba0fed400154a7ccccdadff6ecb8ddc3
+P 19f2a747b8f4d84d8695beb9b23a455f1dead24c94aa90466510555a796027d7
+R f5ab49aa4d163198ec36e8804abd988c
 U larrybr
-Z 5a4c35db10a1fd0fdc6db78a8ff5892d
+Z 7b79332717703ce0f5d855b6c23450ad
 # Remove this line to create a well-formed Fossil manifest.
index 16e195cea37c6e549d89b3fb86fbf9f429c04b49..930b491221761db23733df364b08b1b011511b67 100644 (file)
@@ -1 +1 @@
-19f2a747b8f4d84d8695beb9b23a455f1dead24c94aa90466510555a796027d7
\ No newline at end of file
+9be5e0b503f63dbbf1375460ce74a0aad6f202c2e5b2a748bba1d3af82c7d2d8
\ No newline at end of file
index 1e5eec15a457b7ae5e5ff44dce4884cfc2946657..bb632aa76b92b0907926d50bee04f8c11bfc729a 100644 (file)
  *     IMPLEMENTING( returnType, methodName, ClassName, argCount, args ) [b]
  *   (for C implementations only)
  *     VTABLE_NAME( ClassName ) [c]
+ *     DECORATE_METHOD( DerivedName, methodName )
  *   (for C++ implementations only [d])
  *     CONCRETE_BEGIN( InterfaceName, DerivedName )
  *     CONCRETE_METHOD( returnType, methodName, ClassName, argCount, args )
  *     CONCRETE_END( DerivedName )
  * Notes on these macros:
  *   1. These macros should be used in the order shown. Many should be
- *      terminated with either ';' or a curly-braced construct (which
- *      helps auto-indentation tools to operate sanely.)
+ *      terminated with either ';' or a curly-braced construct. (This
+ *      helps auto-indentation tools to operate usefully or sanely.)
  *   2. The "args" parameter is a parenthesized list of the additional
  *     arguments, those beyond an explicit "InterfaceName *pThis" for C
  *     or the implicit "this" for C++.
  *   3. The argCount parameter must number the additional arguments.
  *   4. A leading method, named "destruct" without additional arguments
  *     and returning void, is declared for all interfaces. This is not
- *     the C++ destructor. (It might delegate to a destructor.)
+ *     the C++ destructor. (It may sometimes delegate to a destructor.)
  *   [a. The convenience is that the signatures from the interface may
  *     be reused for method implementations with a copy and paste. ]
  *   [b. This macro may be useful for function/method definitions which
@@ -71,7 +72,7 @@
 
 #define PURE_VMETHOD(rt, mn, ot, na, args) VMETHOD_BEGIN(rt, mn) \
  ARG_FIRST_ ## na(ot) ARGS_EXPAND(na)args PURE_VMETHOD_END
-#define CONCRETE_METHOD(rt, mn, ot, na, args) rt mn( \
+#define CONCRETE_METHOD(rt, mn, ot, na, args) rt (*mn)( \
  ARG_FIRST_ ## na(ot) ARGS_EXPAND(na)args )
 
 #ifdef __cplusplus
@@ -81,7 +82,7 @@
 # define CONCRETE_BEGIN(iname, derived) class derived : public iname { \
     CONCRETE_METHOD(void, destruct, derived, 0, ())
 # define CONCRETE_END(derived) }
-# define IMPLEMENTING(rt, mn, derived, na, args) rt derived::mn(  \
+# define DEFINE_METHOD(rt, mn, derived, na, args) rt derived::mn(  \
  ARG_FIRST_ ## na(derived) ARGS_EXPAND(na)args )
 #else
 # define VTABLE_NAME(name) name ## _Vtable
   } iname; typedef struct VTABLE_NAME(iname) {         \
   PURE_VMETHOD(void, destruct, iname, 0, ())
 # define INTERFACE_END(iname) } VTABLE_NAME(iname)
+
+#define DERIVED_METHOD(rt, mn, ot,dt, na, args) rt DECORATE_METHOD(dt,mn)( \
+ ARG_FIRST_ ## na(ot) ARGS_EXPAND(na)args )
+
+# define CONCRETE_BEGIN(iname, derived) struct VTABLE_NAME(derived) { \
+    CONCRETE_METHOD(void, destruct, iname, 0, ())
+# define CONCRETE_END(derived) }
+# define INSTANCE_BEGIN(derived) struct derived { \
+  struct VTABLE_NAME(derived) *pMethods
+# define INSTANCE_END(derived) }
+
 # define DECORATE_METHOD(ot, mn)  ot ## _ ## mn
-# define IMPLEMENTING(rt, mn, ot, na, args) rt DECORATE_METHOD(ot, mn)(  \
+# define DEFINE_METHOD(rt, mn, ot, na, args) rt DECORATE_METHOD(ot, mn)(  \
  ARG_FIRST_ ## na(ot) ARGS_EXPAND(na)args )
 #endif
 
index 7380434a3d24a2e433a75a84b17ad61efa4efd79..f60a9ef626bc6943f4e0d9be008466a890304723 100644 (file)
@@ -1227,7 +1227,6 @@ struct EQPGraph {
  */
 typedef struct ShExtInfo {
   ExtensionId extId;        /* The xInit function pointer */
-  void *pExtHandle;         /* Used for unloading (if possible) */
   void (*extDtor)(void *);  /* Extension shutdown on exit or unload */
   /* Each shell extension library registers 0 or more of its extension
    * implementations, interfaces to which are kept in below dynamic.
@@ -7720,7 +7719,9 @@ FROM (\
       sqlite3_finalize(pStmt);
       assert(rc==SQLITE_DONE);
     }
-    assert(db_int(*pDb, zHasDupes)==0); /* Consider: remove this */
+    /* This assert is maybe overly cautious for above de-dup DML, but that can
+     * be replaced via #define's. So this check is made for debug builds. */
+    assert(db_int(*pDb, zHasDupes)==0);
     rc = sqlite3_prepare_v2(*pDb, zCollectVar, -1, &pStmt, 0);
     rc_err_oom_die(rc);
     rc = sqlite3_step(pStmt);
@@ -7751,6 +7752,51 @@ FROM (\
 /* Register a meta-command */
 static int register_meta_command(ShellExState *p,
                                  ExtensionId eid, MetaCommand *pMC){
+  ShellInState *psi = ISS(p);
+  ShExtInfo *psei = 0;
+  const char *zSql
+    = "INSERT INTO ShellCommands (name, extIx, cmdIx) VALUES(?, ?, ?)";
+  int nle = psi->numExtLoaded;
+  int ie;
+  if( pMC==0 ) return SQLITE_ERROR;
+  assert(psi->pShxLoaded!=0 && nle>0 && p->dbShell!=0);
+  for( ie=0; ie<nle; ++ie ){
+    if( psi->pShxLoaded[ie].extId==eid ){
+      psei = &psi->pShxLoaded[ie];
+      break;
+    }
+  }
+  if( psei==0 ){
+    ShExtInfo sei = {eid,0};
+    psi->pShxLoaded = sqlite3_realloc(psi->pShxLoaded,
+                                      (nle+1)*sizeof(ShExtInfo));
+    shell_check_oom(psi->pShxLoaded);
+    psei = &psi->pShxLoaded[psi->numExtLoaded];
+    psi->pShxLoaded[psi->numExtLoaded++] = sei;
+    ie = nle;
+  }
+  {
+    const char *zName = pMC->pMethods->name(pMC);
+    sqlite3_stmt *pStmt;
+    int rc = sqlite3_prepare_v2(p->dbShell, zSql, -1, &pStmt, 0);
+    int nc = psei->numMetaCommands;
+    if( rc!=SQLITE_OK ) return SQLITE_ERROR;
+    psei->ppMetaCommands
+      = sqlite3_realloc(psei->ppMetaCommands, (nc+1)*sizeof(MetaCommand *));
+    shell_check_oom(psei->ppMetaCommands);
+    sqlite3_bind_text(pStmt, 1, zName, -1, 0);
+    sqlite3_bind_int(pStmt, 2, ie);
+    sqlite3_bind_int(pStmt, 3, nc);
+    rc = sqlite3_step(pStmt);
+    sqlite3_finalize(pStmt);
+    if( rc==SQLITE_DONE ){
+      psei->ppMetaCommands[nc++] = pMC;
+      psei->numMetaCommands = nc;
+      return SQLITE_OK;
+    }else{
+      psei->ppMetaCommands[nc] = 0;
+    }
+  }
   return SQLITE_ERROR;
 }
 
@@ -7833,10 +7879,13 @@ static void shell_linkage(
 # define SHELL_DB_STORE SHELL_STRINGIFY(SHELL_DB_FILE)
 #endif
 
+/* Do the initialization needed for use of dbShell for command lookup
+ * and dispatch and for I/O handler lookup and dispatch.
+ */
 static int begin_db_dispatch(ShellExState *psx){
   ShellInState *psi = ISS(psx);
   sqlite3_stmt *pStmt = 0;
-  int ic, rc;
+  int ic, rc, rc1, rc2;
   char *zErr = 0;
   const char *zSql;
   ShExtInfo sei = {0};
@@ -7864,19 +7913,25 @@ static int begin_db_dispatch(ShellExState *psx){
   if( rc!=SQLITE_OK ) return 1;
 #ifdef SHELL_DB_FILE
   sqlite3_exec(psx->dbShell, "DROP TABLE IF EXISTS ShellCommands", 0,0,0);
-#endif
-  rc = sqlite3_exec(psx->dbShell, "CREATE TABLE ShellCommands("
-                    "name TEXT, extIx INT, cmdIx INT,"
-                    "PRIMARY KEY(extIx,cmdIx)) WITHOUT ROWID",
-                    0, 0, &zErr);
-  if( rc!=SQLITE_OK || zErr!=0 ){
+  sqlite3_exec(psx->dbShell, "DROP VIEW IF EXISTS ActiveCommands", 0,0,0);
+#endif
+  rc1 = sqlite3_exec(psx->dbShell, "CREATE TABLE ShellCommands("
+                     "name TEXT, extIx INT, cmdIx INT,"
+                     "PRIMARY KEY(extIx,cmdIx)) WITHOUT ROWID",
+                     0, 0, &zErr);
+  rc2 = sqlite3_exec(psx->dbShell, "CREATE VIEW ActiveCommands AS SELECT "
+                     "s.name AS name, max(s.extIx) AS extIx, s.cmdIx AS cmdIx "
+                     "FROM ShellCommands s GROUP BY name,extIx",
+                     0, 0, &zErr);
+  if( rc1!=SQLITE_OK || rc2!=SQLITE_OK || zErr!=0 ){
     utf8_printf(STD_ERR, "Shell DB init failure, %s\n", zErr? zErr : "");
     return 1;
   }
   zSql = "INSERT INTO ShellCommands (name, extIx, cmdIx) VALUES(?, 0, ?)";
-  rc = sqlite3_prepare_v2(psx->dbShell, zSql, -1, &pStmt, 0);
-  if( rc!=SQLITE_OK ) return 1;
-  rc = sqlite3_exec(psx->dbShell, "BEGIN TRANSACTION", 0, 0, &zErr);
+  rc1 = sqlite3_prepare_v2(psx->dbShell, zSql, -1, &pStmt, 0);
+  rc2 = sqlite3_exec(psx->dbShell, "BEGIN TRANSACTION", 0, 0, &zErr);
+  if( rc1!=SQLITE_OK || rc2!=SQLITE_OK ) return 1;
+  assert(sei.numMetaCommands>0);
   for( ic=0; ic<sei.numMetaCommands; ++ic ){
     MetaCommand *pmc = sei.ppMetaCommands[ic];
     const char *zName = pmc->pMethods->name(pmc);
@@ -7896,36 +7951,39 @@ static int begin_db_dispatch(ShellExState *psx){
   return SQLITE_OK;
 }
 
+/* Call all existent loaded extension destructors, in reverse order
+ * of their objects' creation, then free the tracking dyna-arrays.
+ */
 static void free_shext_tracking(ShellInState *psi){
   int i, j;
   if( psi->pShxLoaded!=0 ){
-    for( i=0; i<psi->numExtLoaded; ++i ){
-      ShExtInfo *psei = &psi->pShxLoaded[i];
+    for( i=psi->numExtLoaded; i>0; --i ){
+      ShExtInfo *psei = &psi->pShxLoaded[i-1];
       if( psei->ppMetaCommands!=0 ){
-        if( i>0 ){ /* built-in commands need no freeing */
-          for( j=0; j<psei->numMetaCommands; ++j ){
-            MetaCommand *pmc = psei->ppMetaCommands[j];
-            if( pmc->pMethods->destruct!=0 ) pmc->pMethods->destruct(pmc);
-          }
+        for( j=psei->numMetaCommands; j>0; --j ){
+          MetaCommand *pmc = psei->ppMetaCommands[j-1];
+          if( pmc->pMethods->destruct!=0 ) pmc->pMethods->destruct(pmc);
         }
         sqlite3_free(psei->ppMetaCommands);
       }
       if( psei->ppOutModeHandlers!=0 ){
-        for( j=0; j<psei->numOutModeHandlers; ++j ){
-          OutModeHandler *pomh = psei->ppOutModeHandlers[j];
+        for( j=psei->numOutModeHandlers; j>0; --j ){
+          OutModeHandler *pomh = psei->ppOutModeHandlers[j-1];
           if( pomh->pMethods->destruct!=0 ) pomh->pMethods->destruct(pomh);
         }
         sqlite3_free(psei->ppOutModeHandlers);
       }
       if( psei->ppImportHandlers!=0 ){
-        for( j=0; j<psei->numImportHandlers; ++j ){
-          ImportHandler *pih = psei->ppImportHandlers[j];
+        for( j=psei->numImportHandlers; j>0; --j ){
+          ImportHandler *pih = psei->ppImportHandlers[j-1];
           if( pih->pMethods->destruct!=0 ) pih->pMethods->destruct(pih);
         }
         sqlite3_free(psei->ppImportHandlers);
       }
     }
     sqlite3_free(psi->pShxLoaded);
+    psi->pShxLoaded = 0;
+    psi->numExtLoaded = 0;
   }
 }
 
@@ -7944,9 +8002,10 @@ static int load_shell_extension(ShellExState *psx, const char *zFile,
   ShellExtensionLink shxLink = {
     sizeof(ShellExtensionLink),
     &shellExtAPI,
-    0, /* zErrMsg */
-    0, /* ExtensionId */
-    0  /* Extension destructor */
+    psx, /* pSXS */
+    0,   /* zErrMsg */
+    0,   /* ExtensionId */
+    0    /* Extension destructor */
   };
   int rc;
   if( psx->dbShell==0 ){
@@ -9145,8 +9204,8 @@ DISPATCHABLE_COMMAND( import ? 3 7 ){
     assert(dbCols==0);
     if( zColDefs==0 ){
       utf8_printf(stderr,"%s: empty file\n", sCtx.zFile);
-    import_fail:
       sqlite3_free(zCreate);
+    import_fail: /* entry from outer blocks */
       sqlite3_free(zSql);
       sqlite3_free(zFullTabName);
       import_cleanup(&sCtx);
@@ -9157,12 +9216,11 @@ DISPATCHABLE_COMMAND( import ? 3 7 ){
       utf8_printf(out, "%s\n", zCreate);
     }
     rc = sqlite3_exec(DBX(p), zCreate, 0, 0, 0);
+    sqlite3_free(zCreate);
     if( rc ){
       utf8_printf(STD_ERR, "%s failed:\n%s\n", zCreate, sqlite3_errmsg(DBX(p)));
       goto import_fail;
     }
-    sqlite3_free(zCreate);
-    zCreate = 0;
     rc = sqlite3_prepare_v2(DBX(p), zSql, -1, &pStmt, 0);
   }
   if( rc ){
@@ -12885,13 +12943,11 @@ static MetaMatchIter findMatchingMetaCmds(const char *cmdFragment,
     rv.pMC = (MetaCommand *)command_table;
   }else{
     /* Prepare rv.stmt to yield results glob-matching cmdFragment. */
-    /* ToDo: Verify this query is right in face of overridden commands. */
-    const char *zSql = "SELECT extIx, cmdIx FROM ("
-      " SELECT name,max(extIx),cmdIx FROM ShellCommands GROUP BY name,extIx"
-      ") WHERE name glob (?||'*')";
-    if( cmdFragment==0 ) cmdFragment = "";
+    const char *zSql =
+      "SELECT name, extIx, cmdIx FROM ActiveCommands "
+      "WHERE name glob (?||'*') ORDER BY name";
     sqlite3_prepare_v2(psx->dbShell, zSql, -1, &rv.stmt, 0);
-    sqlite3_bind_text(rv.stmt, 1, cmdFragment, -1, 0);
+    sqlite3_bind_text(rv.stmt, 1, cmdFragment? cmdFragment : "", -1, 0);
   }
   return rv;
 }
@@ -12909,8 +12965,8 @@ static MetaCommand * nextMatchingMetaCmd(MetaMatchIter *pMMI){
   }else{
     int rc = sqlite3_step(pMMI->stmt);
     if( rc==SQLITE_ROW ){
-      int extIx = sqlite3_column_int(pMMI->stmt, 0);
-      int cmdIx = sqlite3_column_int(pMMI->stmt, 1);
+      int extIx = sqlite3_column_int(pMMI->stmt, 1);
+      int cmdIx = sqlite3_column_int(pMMI->stmt, 2);
       return command_by_index(ISS(pMMI->psx), extIx, cmdIx);
     }else{
       sqlite3_finalize(pMMI->stmt);
@@ -12953,9 +13009,8 @@ MetaCommand *findMetaCommand(const char *cmdName, ShellExState *psx,
     int rc;
     int extIx = -1, cmdIx = -1, nf;
     sqlite3_stmt *pStmt = 0;
-    /* FixMe ToDo: This is not yet right where commands have been overridden. */
-    const char *zSql = "SELECT COUNT(*), max(extIx), cmdIx"
-      " FROM ShellCommands WHERE name glob (?||'*')";
+    const char *zSql = "SELECT COUNT(*), extIx, cmdIx"
+      " FROM ActiveCommands WHERE name glob (?||'*')";
     rc = sqlite3_prepare_v2(psx->dbShell, zSql, -1, &pStmt, 0);
     sqlite3_bind_text(pStmt, 1, cmdName, -1, 0);
     rc = sqlite3_step(pStmt);
@@ -12965,7 +13020,7 @@ MetaCommand *findMetaCommand(const char *cmdName, ShellExState *psx,
     sqlite3_finalize(pStmt);
     if( rc!= SQLITE_ROW ) return 0;
     *nFound = nf;
-    if( nf!=1 ) return 0;
+    if( nf!=1 ) return 0; /* Future: indicate ambiguity if > 1 */
     return command_by_index(ISS(psx), extIx, cmdIx);
   }else
 #endif
index e9a2e71700766c2185da34b9cc52bb7c93053a57..5a5fdb03c2e215a1e81cf0aec20252fca704d520 100644 (file)
@@ -52,7 +52,7 @@ typedef struct ShellExState {
   /* Number of lines written during a query result output */
   int resultCount;
   /* Whether to show column names for certain output modes (reference) */
-  u8 *pShowHeader;
+  unsigned char *pShowHeader;
   /* Column separator character for some modes, read-only */
   char *zFieldSeparator;
   /* Row separator character for some modes (MODE_Ascii), read-only */
@@ -147,10 +147,10 @@ PURE_VMETHOD(void, closeDataInStream, ImportHandler,
              2,( ShellExState *pSES, char **pzErr ));
 INTERFACE_END( ImportHandlerVtable );
 
-typedef struct {
+typedef struct ExtensionHelpers {
   int helperCount; /* Helper count, not including sentinel */
-  union ExtHelp {
-    struct {
+  union {
+    struct ExtHelpers {
       int (*failIfSafeMode)(ShellExState *p, const char *zErrMsg, ...);
       FILE * (*currentOutputFile)(ShellExState *p);
       struct InSource * (*currentInputSource)(ShellExState *p);
@@ -166,7 +166,7 @@ typedef struct {
 /* Various shell extension helpers and feature registration functions */
 typedef struct ShellExtensionAPI {
   /* Utility functions for use by extensions */
-  ExtensionHelpers * pExtHelp;
+  ExtensionHelpers * pExtHelpers;
 
   /* Functions for extension to register its implementors with shell */
   const int numRegistrars; /* 3 for this version */
@@ -196,7 +196,7 @@ typedef struct ShellExtensionAPI {
 typedef struct ShellExtensionLink {
   int sizeOfThis;        /* sizeof(ShellExtensionLink) for expansion */
   ShellExtensionAPI *pShellExtensionAPI;
-  ShellExState *pSSX;    /* For use in extension feature registrations */
+  ShellExState *pSXS;    /* For use in extension feature registrations */
   char *zErrMsg;         /* Extension error messages land here, if any. */
 
   /* An init "out" parameter, used as the loaded extension ID. Unless
@@ -223,7 +223,7 @@ typedef struct ShellExtensionLink {
  * pointer to a ShellExtensionLink instance during an extension's *init*()
  * call (during shell extension load) or 0 (during SQLite extension load.)
  */
-#define DEFINE_SHDB_TO_SHEXT_API(func_name) \
+#define DEFINE_SHDB_TO_SHEXTLINK(func_name) \
  static ShellExtensionLink * func_name(sqlite3 * db){ \
   ShellExtensionLink *rv = 0; sqlite3_stmt *pStmt = 0; \
   if( SQLITE_OK==sqlite3_prepare_v2(db,"SELECT shext_pointer(0)",-1,&pStmt,0) \
index 1777ae0bf1efc394dcc89fa0c2d1719442d60ee6..52fb1d431e62176737ac4913467d5e4885550ad3 100644 (file)
 
 SQLITE_EXTENSION_INIT1;
 
-DEFINE_SHDB_TO_SHEXT_API(shext_api);
+static struct ShExtAPI *pShExtApi = 0;
+static struct ExtHelpers *pExtHelpers = 0;
+
+/* These DERIVED_METHOD(...) macro calls' arguments were copied and
+ * pasted from the MetaCommand interface declaration in shext_linkage.h ,
+ * but with Interface,Derived substituted for the interface typename.
+ * The function bodies are not so easily written, of course. */
+
+DERIVED_METHOD(void, destruct, MetaCommand,BatBeing, 0, ()){
+  fprintf(stderr, "BatBeing unbecoming.\n");
+}
+
+DERIVED_METHOD(const char *, name, MetaCommand,BatBeing, 0,()){
+  return "bat_being";
+}
+
+DERIVED_METHOD(const char *, help, MetaCommand,BatBeing, 1,(int more)){
+  switch( more ){
+  case 0: return
+      ".bat_being ?whatever?    Demonstrates vigilantism weekly\n";
+  case 1: return "   Options summon side-kick and villains.\n";
+  default: return 0;
+  }
+}
+
+DERIVED_METHOD(int, argsCheck, MetaCommand,BatBeing, 3,
+             (char **pzErrMsg, int nArgs, char *azArgs[])){
+  return 0;
+}
+
+typedef struct BatBeing BatBeing;
+static void sayHowMany( struct BatBeing *pbb, FILE *out );
+
+DERIVED_METHOD(int, execute, MetaCommand,BatBeing, 4,
+             (ShellExState *psx, char **pzErrMsg, int nArgs, char *azArgs[])){
+  FILE *out = pExtHelpers->currentOutputFile(psx);
+  switch( nArgs ){
+  default: fprintf(out, "The Penguin, Joker and Riddler have teamed up!\n");
+  case 2: fprintf(out, "The Dynamic Duo arrives, and ... ");
+  case 1: fprintf(out, "@#$ KaPow! $#@\n");
+  }
+  sayHowMany((struct BatBeing *)pThis, out);
+  return 0;
+}
+
+/* Note that these CONCRETE_METHOD... macro calls' arguments were copied and
+ * pasted from the MetaCommand interface declaration in shext_linkage.h .
+ * In a future version of shext_linkage.h, this will all be a mondo maco. */
+CONCRETE_BEGIN(MetaCommand, BatBeing);
+CONCRETE_METHOD(const char *, name, MetaCommand, 0,());
+CONCRETE_METHOD(const char *, help, MetaCommand, 1,(int more));
+CONCRETE_METHOD(int, argsCheck, MetaCommand, 3,
+                 (char **pzErrMsg, int nArgs, char *azArgs[]));
+CONCRETE_METHOD(int, execute, MetaCommand, 4,
+                 (ShellExState *, char **pzErrMsg, int nArgs, char *azArgs[]));
+CONCRETE_END(BatBeing) batty_methods = {
+  DECORATE_METHOD(BatBeing,destruct),
+  DECORATE_METHOD(BatBeing,name),
+  DECORATE_METHOD(BatBeing,help),
+  DECORATE_METHOD(BatBeing,argsCheck),
+  DECORATE_METHOD(BatBeing,execute)
+};
+
+INSTANCE_BEGIN(BatBeing);
+  int numCalls;
+INSTANCE_END(BatBeing) batty = {
+  &batty_methods,
+  0
+};
+
+static void sayHowMany( struct BatBeing *pbb, FILE *out ){
+  fprintf(out, "This execute has been called %d times.\n", ++pbb->numCalls);
+}
+
+
+DEFINE_SHDB_TO_SHEXTLINK(shext_link);
 
 /*
 ** Extension load function.
@@ -31,13 +106,22 @@ int sqlite3_testshellext_init(
   const sqlite3_api_routines *pApi
 ){
   int nErr = 0;
-  ShellExtensionLink *papi;
+  ShellExtensionLink *pShExtLink;
   SQLITE_EXTENSION_INIT2(pApi);
-  papi = shext_api(db);
-  if( papi ){
-    printf("Got papi, equality=%d\n", &papi->zErrMsg==pzErrMsg);
+  pShExtLink = shext_link(db);
+  if( pShExtLink && pShExtLink->pShellExtensionAPI->numRegistrars>=1 ){
+    ShellExState *psx = pShExtLink->pSXS;
+    MetaCommand *pmc = (MetaCommand *)&batty;
+    int rc;
+
+    pShExtApi = & pShExtLink->pShellExtensionAPI->api.named;
+    pExtHelpers = & pShExtLink->pShellExtensionAPI->pExtHelpers->helpers.named;
+    rc = pShExtApi->registerMetaCommand(psx, sqlite3_testshellext_init,pmc);
+    if( rc!=0 ) ++nErr;
+  }
+  else{
+    printf("No ShellExtensionLink pointer or registration API.\n");
+    ++nErr;
   }
-  else
-    printf("No papi pointer.\n");
   return nErr ? SQLITE_ERROR : SQLITE_OK;
 }
index dce7caa6ec061bd1087227af84093aaa0a5d2ab1..65432e9ca007ede21dd98a654853dc0f34a420cd 100644 (file)
@@ -455,7 +455,7 @@ proc COLLECT_HELP_TEXT {inSrc tailCaptureEmpty ostrm} {
 proc CONDITION_COMMAND {inSrc tailCap ostrm} {
   # Name a command to be conditionally available, with the condition.
   foreach {cmd pp_expr} $tailCap { set pp_expr [string trim $pp_expr] ; break }
-  if {[regexp {^(!)?defined\(\s*(\w+)\s*\)} $pp_expr ma bang pp_var]} {
+  if {[regexp {^(!)?defined\(\s*(\w+)\s*\)\s*$} $pp_expr ma bang pp_var]} {
     if {$bang eq "!"} {
       set pp_expr "#ifndef $pp_var"
     } else {