From 82a5e9332beac59ac359f68592d1ad152408fb6d Mon Sep 17 00:00:00 2001 From: larrybr Date: Fri, 11 Mar 2022 22:59:24 +0000 Subject: [PATCH] CLI extension's registerMetaCommand done, and used by test_shellext.c (demo code) FossilOrigin-Name: 9be5e0b503f63dbbf1375460ce74a0aad6f202c2e5b2a748bba1d3af82c7d2d8 --- manifest | 20 +++---- manifest.uuid | 2 +- src/obj_interfaces.h | 24 ++++++-- src/shell.c.in | 137 ++++++++++++++++++++++++++++++------------- src/shext_linkage.h | 14 ++--- src/test_shellext.c | 98 ++++++++++++++++++++++++++++--- tool/mkshellc.tcl | 2 +- 7 files changed, 224 insertions(+), 73 deletions(-) diff --git a/manifest b/manifest index 202128825b..a589a60da3 100644 --- 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. diff --git a/manifest.uuid b/manifest.uuid index 16e195cea3..930b491221 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -19f2a747b8f4d84d8695beb9b23a455f1dead24c94aa90466510555a796027d7 \ No newline at end of file +9be5e0b503f63dbbf1375460ce74a0aad6f202c2e5b2a748bba1d3af82c7d2d8 \ No newline at end of file diff --git a/src/obj_interfaces.h b/src/obj_interfaces.h index 1e5eec15a4..bb632aa76b 100644 --- a/src/obj_interfaces.h +++ b/src/obj_interfaces.h @@ -20,21 +20,22 @@ * 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 @@ -90,8 +91,19 @@ } 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 diff --git a/src/shell.c.in b/src/shell.c.in index 7380434a3d..f60a9ef626 100644 --- a/src/shell.c.in +++ b/src/shell.c.in @@ -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; iepShxLoaded[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; icpMethods->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; inumExtLoaded; ++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; jnumMetaCommands; ++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; jnumOutModeHandlers; ++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; jnumImportHandlers; ++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 diff --git a/src/shext_linkage.h b/src/shext_linkage.h index e9a2e71700..5a5fdb03c2 100644 --- a/src/shext_linkage.h +++ b/src/shext_linkage.h @@ -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) \ diff --git a/src/test_shellext.c b/src/test_shellext.c index 1777ae0bf1..52fb1d431e 100644 --- a/src/test_shellext.c +++ b/src/test_shellext.c @@ -17,7 +17,82 @@ 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; } diff --git a/tool/mkshellc.tcl b/tool/mkshellc.tcl index dce7caa6ec..65432e9ca0 100644 --- a/tool/mkshellc.tcl +++ b/tool/mkshellc.tcl @@ -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 { -- 2.47.3