From: larrybr Date: Sun, 10 Apr 2022 09:17:29 +0000 (+0000) Subject: Create parallel C and C++ test/demo shell extensions, with header tweaks to make... X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=7c7f3d14356b06a6092d024aedad62be1ea49eac;p=thirdparty%2Fsqlite.git Create parallel C and C++ test/demo shell extensions, with header tweaks to make that work. Fix a bug exposed when multiple shell extensions were loaded. FossilOrigin-Name: 2596e7c439b6c4c89921079a78ba6174e99f766b30fab8763be20c1b631e49ec --- diff --git a/manifest b/manifest index 50183c67da..3cd411c81a 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Sync\sw/trunk,\sfor\s.import\sfix. -D 2022-04-09T19:39:58.635 +C Create\sparallel\sC\sand\sC++\stest/demo\sshell\sextensions,\swith\sheader\stweaks\sto\smake\sthat\swork.\sFix\sa\sbug\sexposed\swhen\smultiple\sshell\sextensions\swere\sloaded. +D 2022-04-10T09:17:29.899 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -534,7 +534,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 ea17e13ea521955fcdbeb2257b2439d2047b4c5d042a971a4639846023a9f373 +F src/obj_interfaces.h c80525ea603357a79ddae45ba98eec2ac60a9092d1800811d92f3a0e0208d8a6 F src/os.c b1c4f2d485961e9a5b6b648c36687d25047c252222e9660b7cc25a6e1ea436ab F src/os.h 26890f540b475598cd9881dcc68931377b8d429d3ea3e2eeb64470cde64199f8 F src/os_common.h b2f4707a603e36811d9b1a13278bffd757857b85 @@ -556,8 +556,8 @@ F src/random.c 097dc8b31b8fba5a9aca1697aeb9fd82078ec91be734c16bffda620ced7ab83c F src/resolve.c 18d99e7146852d6064559561769fcca0743eb32b14a97da6dbed373a30ee0e76 F src/rowset.c ba9515a922af32abe1f7d39406b9d35730ed65efab9443dc5702693b60854c92 F src/select.c 7c106b3f36d483242b0a9c696614cd53d6f29e1ac81da6a3f0e9ea92f4211cc3 -F src/shell.c.in 3984e1de9e70357ea0a017409f4421eacf94175a30969d68f6cc135a4c1fb9f9 -F src/shext_linkage.h 88a3f215fdb090fcc3b3577a05bafd234f1a556bad3f2f4ac990a177aebf4b2c +F src/shell.c.in 9ae99f072005ee180a06c10ba53f465ae1db315573abc7767c1060c1b3c0198d +F src/shext_linkage.h 4f58ea112273f3e6769221739b7160852cf0d7cdd4ee757f6c696e90fb7ffcb9 F src/sqlite.h.in 2a35f62185eb5e7ecc64a2f68442b538ce9be74f80f28a00abc24837edcf1c17 F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 F src/sqlite3ext.h f49e28c25bd941e79794db5415fdf7b202deb3bc072ed6f1ed273d578703684e @@ -606,7 +606,8 @@ 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 0794ffe8c7762d027a28a2b445277c72a68b28eafba27d38ef197a9655599b42 +F src/test_shellext.c fa658ee13e39d2b4bbb49c1b7fe060990ee0928e0412ffcdecbf98ddba3ef12e +F src/test_shellext.cpp 95d1f67570683b0cc61e6b5a447aadba39ffe10fffa7d6015ba855fb05f10865 F src/test_sqllog.c 540feaea7280cd5f926168aee9deb1065ae136d0bbbe7361e2ef3541783e187a F src/test_superlock.c 4839644b9201da822f181c5bc406c0b2385f672e F src/test_syscall.c 1073306ba2e9bfc886771871a13d3de281ed3939 @@ -1951,8 +1952,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 43eb311e517b79cde9e17c1a80baed8d13d9d943dd9ee44b31831159df8715fc 21e96600d90c1cda84777abe22a11058eba46c9faefeb05f8c31bc0e7fa84b19 -R 67ea5330fa92b3979fbd7e883fdffd1f +P 861ab023be283782131df5ccd9eff50cf2f06f48f1ea7defd3f08177a3df5889 +R 29bf4bbfab5e25a57abea25dca7857f3 U larrybr -Z b47b9cb62392429660769c050bed79ba +Z 916c41d1a88108766c11b193884acec2 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 6826a57cdb..30250f8904 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -861ab023be283782131df5ccd9eff50cf2f06f48f1ea7defd3f08177a3df5889 \ No newline at end of file +2596e7c439b6c4c89921079a78ba6174e99f766b30fab8763be20c1b631e49ec \ No newline at end of file diff --git a/src/obj_interfaces.h b/src/obj_interfaces.h index bb632aa76b..bad9431369 100644 --- a/src/obj_interfaces.h +++ b/src/obj_interfaces.h @@ -76,9 +76,8 @@ ARG_FIRST_ ## na(ot) ARGS_EXPAND(na)args ) #ifdef __cplusplus -# define INTERFACE_BEGIN(iname) struct iname { \ - PURE_VMETHOD(void, destruct, iname, 0, ()) -# define INTERFACE_END(iname) } +# define INTERFACE_BEGIN(iname) struct iname { virtual void destruct() = 0 +# define INTERFACE_END(iname) }; # define CONCRETE_BEGIN(iname, derived) class derived : public iname { \ CONCRETE_METHOD(void, destruct, derived, 0, ()) # define CONCRETE_END(derived) } @@ -107,4 +106,16 @@ ARG_FIRST_ ## na(ot) ARGS_EXPAND(na)args ) #endif +/* Some preprocessing to make struct type definitions usable for C and C++ + * code using just the typename. Usage: + * AGGTYPE_BEGIN(Framus) { member; ... } AGGTYPE_END(Framus); + */ +#ifndef __cplusplus +#define AGGTYPE_BEGIN(tname) typedef struct tname +#define AGGTYPE_END(tname) tname +#else +#define AGGTYPE_BEGIN(tname) struct tname +#define AGGTYPE_END(tname) +#endif + #endif /* !defined(OBJIFACE_H) */ diff --git a/src/shell.c.in b/src/shell.c.in index 74c6ed74e0..744f55229a 100644 --- a/src/shell.c.in +++ b/src/shell.c.in @@ -8103,9 +8103,11 @@ static int subscribe_events(ShellExState *p, ExtensionId eid, void *pvUserData, pes->pvUserData = pvUserData; return SQLITE_OK; } + ++pes; } assert(pes==pesLim); - pes = sqlite3_realloc(pes, (psi->numSubscriptions+1)*sizeof(*pes)); + pes = sqlite3_realloc(psi->pSubscriptions, + (psi->numSubscriptions+1)*sizeof(*pes)); if( pes==0 ) return SQLITE_NOMEM; psi->pSubscriptions = pes; pes += (psi->numSubscriptions++); diff --git a/src/shext_linkage.h b/src/shext_linkage.h index fcac113c06..d582a03b12 100644 --- a/src/shext_linkage.h +++ b/src/shext_linkage.h @@ -305,31 +305,28 @@ typedef struct Prompts { const char *zContinue; } Prompts; -typedef struct ExtensionHelpers { +AGGTYPE_BEGIN(ExtensionHelpers) { int helperCount; /* Helper count, not including sentinel */ - union { - struct ExtHelpers { - int (*failIfSafeMode)(ShellExState *p, const char *zErrMsg, ...); - FILE * (*currentOutputFile)(ShellExState *p); - struct InSource * (*currentInputSource)(ShellExState *p); - char * (*strLineGet)(char *zBuf, int ncMax, struct InSource *pInSrc); - MetaCommand * (*findMetaCommand)(const char *cmdName, ShellExState *p, - /* out */ int *pnFound); - DotCmdRC (*runMetaCommand)(MetaCommand *pmc, char *azArg[], int nArg, - ShellExState *psx); - void (*setColumnWidths)(ShellExState *p, char *azWidths[], int nWidths); - int (*nowInteractive)(ShellExState *p); - const char * (*shellInvokedAs)(void); - const char * (*shellStartupDir)(void); - char * (*oneInputLine)(struct InSource *pInSrc, char *zPrior, - int isContinuation, Prompts *pCue); - void (*freeInputLine)(char *zLine); - int (*enable_load_extension)(sqlite3 *db, int onoff); - void (*sentinel)(void); - } named ; - void (*nameless[13+1])(); /* Same as named but anonymous plus a sentinel. */ + struct ExtHelpers { + int (*failIfSafeMode)(ShellExState *p, const char *zErrMsg, ...); + FILE * (*currentOutputFile)(ShellExState *p); + struct InSource * (*currentInputSource)(ShellExState *p); + char * (*strLineGet)(char *zBuf, int ncMax, struct InSource *pInSrc); + MetaCommand * (*findMetaCommand)(const char *cmdName, ShellExState *p, + /* out */ int *pnFound); + DotCmdRC (*runMetaCommand)(MetaCommand *pmc, char *azArg[], int nArg, + ShellExState *psx); + void (*setColumnWidths)(ShellExState *p, char *azWidths[], int nWidths); + int (*nowInteractive)(ShellExState *p); + const char * (*shellInvokedAs)(void); + const char * (*shellStartupDir)(void); + char * (*oneInputLine)(struct InSource *pInSrc, char *zPrior, + int isContinuation, Prompts *pCue); + void (*freeInputLine)(char *zLine); + int (*enable_load_extension)(sqlite3 *db, int onoff); + void *pSentinel; /* Always set to 0, above never are. */ } helpers; -} ExtensionHelpers; +} AGGTYPE_END(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 @@ -366,52 +363,48 @@ typedef int (*ShellEventNotify)(void *pvUserData, NoticeKind nk, void *pvSubject, ShellExState *psx); /* Various shell extension helpers and feature registration functions */ -typedef struct ShellExtensionAPI { +AGGTYPE_BEGIN(ShellExtensionAPI) { /* Utility functions for use by extensions */ ExtensionHelpers * pExtHelpers; /* Functions for an extension to register its implementors with shell */ const int numRegistrars; /* 6 for this version */ - union { - struct ShExtAPI { - /* Register a meta-command */ - int (*registerMetaCommand)(ShellExState *p, - ExtensionId eid, MetaCommand *pMC); - /* Register query result data display (or other disposition) mode */ - int (*registerExporter)(ShellExState *p, - ExtensionId eid, ExportHandler *pEH); - /* Register an import variation from (various sources) for .import */ - int (*registerImporter)(ShellExState *p, - ExtensionId eid, ImportHandler *pIH); - /* Provide scripting support to host shell. (See ScriptSupport above.) */ - int (*registerScripting)(ShellExState *p, - ExtensionId eid, ScriptSupport *pSS); - /* Subscribe to (or unsubscribe from) messages about various changes. - * See above NoticeKind enum and ShellEventNotify callback typedef. */ - int (*subscribeEvents)(ShellExState *p, ExtensionId eid, void *pvUserData, - NoticeKind nkMin, ShellEventNotify eventHandler); - /* Notify host shell that an ad-hoc dot command exists and provide for - * its help text to appear in .help output. Only an extension which has - * registered an "unknown" MetaCommand may use this. - * If zHelp==0, any such provision is removed. If zHelp!=0, original or - * replacement help text is associated with command zName. - * Help text before the first newline is primary, issued as summary help. - * Text beyond that is secondary, issued as the complete command help. */ - int (*registerAdHocCommand)(ShellExState *p, ExtensionId eid, - const char *zName, const char *zHelp); - /* Preset to 0 at extension load, a sentinel for expansion */ - void (*sentinel)(void); - } named; - void (*pFunctions[6+1])(); /* 0-terminated sequence of function pointers */ + struct ShExtAPI { + /* Register a meta-command */ + int (*registerMetaCommand)(ShellExState *p, + ExtensionId eid, MetaCommand *pMC); + /* Register query result data display (or other disposition) mode */ + int (*registerExporter)(ShellExState *p, + ExtensionId eid, ExportHandler *pEH); + /* Register an import variation from (various sources) for .import */ + int (*registerImporter)(ShellExState *p, + ExtensionId eid, ImportHandler *pIH); + /* Provide scripting support to host shell. (See ScriptSupport above.) */ + int (*registerScripting)(ShellExState *p, + ExtensionId eid, ScriptSupport *pSS); + /* Subscribe to (or unsubscribe from) messages about various changes. + * See above NoticeKind enum and ShellEventNotify callback typedef. */ + int (*subscribeEvents)(ShellExState *p, ExtensionId eid, void *pvUserData, + NoticeKind nkMin, ShellEventNotify eventHandler); + /* Notify host shell that an ad-hoc dot command exists and provide for + * its help text to appear in .help output. Only an extension which has + * registered an "unknown" MetaCommand may use this. + * If zHelp==0, any such provision is removed. If zHelp!=0, original or + * replacement help text is associated with command zName. + * Help text before the first newline is primary, issued as summary help. + * Text beyond that is secondary, issued as the complete command help. */ + int (*registerAdHocCommand)(ShellExState *p, ExtensionId eid, + const char *zName, const char *zHelp); + void *pSentinel; /* Always set to 0, above never are. */ } api; -} ShellExtensionAPI; +} AGGTYPE_END(ShellExtensionAPI); /* Struct passed to extension init function to establish linkage. The * lifetime of instances spans only the init call itself. Extensions * should make a copy, if needed, of pShellExtensionAPI for later use. * Its referent is static, persisting for the process duration. */ -typedef struct ShellExtensionLink { +AGGTYPE_BEGIN(ShellExtensionLink) { int sizeOfThis; /* sizeof(ShellExtensionLink) for expansion */ ShellExtensionAPI *pShellExtensionAPI; ShellExState *pSXS; /* For use in extension feature registrations */ @@ -438,7 +431,7 @@ typedef struct ShellExtensionLink { */ int nLoadArgs; char **azLoadArgs; -} ShellExtensionLink; +} AGGTYPE_END(ShellExtensionLink); /* String used with SQLite "Pointer Passing Interfaces" as a type marker. * That API subset is used by the shell to pass its extension API to the @@ -463,12 +456,27 @@ typedef struct ShellExtensionLink { /* * Define boilerplate macros analogous to SQLITE_EXTENSION_INIT# + * Note that the argument names are reused across the macro set. + * This reflects the fact that, for the macros to be useful, the + * same objects must be referenced from different places. Hence, + * the actual arguments must appear in all of the invocations. */ -/* Place at file scope prior to usage of the arguments by extension code. */ + +/* Place at file scope prior to usage of the arguments by extension code. + * This defines 3 static objects, named per the arguments and set or used + * for an extension to link into the shell host. + */ +#ifndef __cplusplus #define SHELL_EXTENSION_INIT1( shell_api_ptr, ext_helpers_ptr, link_func ) \ static struct ShExtAPI *shell_api_ptr = 0; \ static struct ExtHelpers *ext_helpers_ptr = 0; \ DEFINE_SHDB_TO_SHEXTLINK(link_func) +#else +#define SHELL_EXTENSION_INIT1( shell_api_ptr, ext_helpers_ptr, link_func ) \ + static ShellExtensionAPI::ShExtAPI *shell_api_ptr = 0; \ + static ExtensionHelpers::ExtHelpers *ext_helpers_ptr = 0; \ + DEFINE_SHDB_TO_SHEXTLINK(link_func) +#endif /* Place within sqlite3_x_init() among its local variable declarations. */ #define SHELL_EXTENSION_INIT2( link_ptr, link_func, db_ptr ) \ @@ -477,8 +485,8 @@ typedef struct ShellExtensionLink { /* Place within sqlite3_x_init() code prior to usage of the *_ptr arguments. */ #define SHELL_EXTENSION_INIT3( shell_api_ptr, ext_helpers_ptr, link_ptr ) \ if( (link_ptr)!=0 ){ \ - shell_api_ptr = &link_ptr->pShellExtensionAPI->api.named; \ - ext_helpers_ptr = &link_ptr->pShellExtensionAPI->pExtHelpers->helpers.named; \ + shell_api_ptr = &link_ptr->pShellExtensionAPI->api; \ + ext_helpers_ptr = &link_ptr->pShellExtensionAPI->pExtHelpers->helpers; \ } /* This test may be used within sqlite3_x_init() after SHELL_EXTENSION_INIT3 */ diff --git a/src/test_shellext.c b/src/test_shellext.c index 809cd427fc..3eb2863425 100644 --- a/src/test_shellext.c +++ b/src/test_shellext.c @@ -18,8 +18,9 @@ SQLITE_EXTENSION_INIT1; -static struct ShExtAPI *pShExtApi = 0; -static struct ExtHelpers *pExtHelpers = 0; +SHELL_EXTENSION_INIT1(pShExtApi, pExtHelpers, shextLinkFetcher); +#define SHX_API(entry) pShExtApi->entry +#define SHX_HELPER(entry) pExtHelpers->entry typedef struct BatBeing BatBeing; static void sayHowMany( BatBeing *pbb, FILE *out, ShellExState *psx ); @@ -112,8 +113,6 @@ static int shellEventHandle(void *pv, NoticeKind nk, return 0; } -DEFINE_SHDB_TO_SHEXTLINK(shext_link); - /* ** Extension load function. */ @@ -126,26 +125,26 @@ int sqlite3_testshellext_init( const sqlite3_api_routines *pApi ){ int nErr = 0; - ShellExtensionLink *pShExtLink; + int iLdErr; SQLITE_EXTENSION_INIT2(pApi); - pShExtLink = shext_link(db); - if( pShExtLink && pShExtLink->pShellExtensionAPI->numRegistrars>=1 ){ + SHELL_EXTENSION_INIT2(pShExtLink, shextLinkFetcher, db); + + SHELL_EXTENSION_INIT3(pShExtApi, pExtHelpers, pShExtLink); + iLdErr = SHELL_EXTENSION_LOADFAIL_WHY(pShExtLink, 5, 5); + if( iLdErr!=EXLD_Ok ){ + *pzErrMsg = sqlite3_mprintf("Load failed, cause %d\n", iLdErr); + return SQLITE_ERROR; + }else{ ShellExState *psx = pShExtLink->pSXS; MetaCommand *pmc = (MetaCommand *)&batty; int rc; - 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); + SHX_API(subscribeEvents)(psx, sqlite3_testshellext_init, &batty, + NK_CountOf, shellEventHandle); + batty.pPrint = SHX_HELPER(findMetaCommand)("print", psx, &rc); + rc = SHX_API(registerMetaCommand)(psx, sqlite3_testshellext_init, pmc); if( rc!=0 ) ++nErr; pShExtLink->eid = sqlite3_testshellext_init; } - else{ - printf("No ShellExtensionLink pointer or registration API.\n"); - ++nErr; - } return nErr ? SQLITE_ERROR : SQLITE_OK; } diff --git a/src/test_shellext.cpp b/src/test_shellext.cpp new file mode 100644 index 0000000000..89d52181a7 --- /dev/null +++ b/src/test_shellext.cpp @@ -0,0 +1,145 @@ +/* +** 2022 Feb 28 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +************************************************************************* +** Test extension for testing the shell's .load -shellext ... function. +** To build from the SQLite project root: +** g++ -shared -fPIC -Wall -I. -g src/test_shellext.cpp -o test_shellext.so +*/ +#include +#include "shx_link.h" + +SQLITE_EXTENSION_INIT1; + +SHELL_EXTENSION_INIT1(pShExtApi, pExtHelpers, shextLinkFetcher); +#define SHX_API(entry) pShExtApi->entry +#define SHX_HELPER(entry) pExtHelpers->entry + +struct BatBeing : MetaCommand { + + ~BatBeing() {}; // No held resources; copy/assign is fine and dying is easy. + + void destruct() { this->~BatBeing(); } + + const char *name() { return "bat_being"; }; + + const char *help(const char *zHK) { + if( !zHK ) + return ".bat_being ?whatever? Demonstrates vigilantism weekly\n"; + if( !*zHK ) + return " Options summon side-kick and villains.\n"; + return 0; + }; + + DotCmdRC argsCheck(char **pzErrMsg, int nArgs, char *azArgs[]) { + return DCR_Ok; + }; + DotCmdRC execute(ShellExState *psx, char **pzErrMsg, + int nArgs, char *azArgs[]); + + BatBeing(MetaCommand *pp = 0) { + numCalls = 0; + pPrint = pp; + }; + + // Default copy/assign are fine; nothing held. + + int numCalls; + MetaCommand * pPrint; +}; + +static void sayHowMany( BatBeing *pbb, FILE *out, ShellExState *psx ){ + if( pbb->pPrint ){ + static char cmd[] = "print"; + char *az[] = { cmd, 0 }; + char *zErr = 0; + DotCmdRC rc; + az[1] = sqlite3_mprintf("This execute has been called %d times.\n", + ++pbb->numCalls); + rc = pbb->pPrint->execute(psx, &zErr, 2, az); + sqlite3_free(az[1]); + if( rc!= DCR_Ok ){ + fprintf(out, "print() failed: %d\n", rc); + } + } +} + +DotCmdRC BatBeing::execute(ShellExState *psx, char **pzErrMsg, + int nArgs, char *azArgs[]) { + FILE *out = SHX_HELPER(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(this, out, psx); + return DCR_Ok; +} + +/* Define/initialize BatBeing as a MetaCommand subclass using above v-table. + * This compiles in a type-safe manner because the batty_methods v-table + * and methods it incorporates strictly match the MetaCommand interface. + */ +static BatBeing batty(0); + +static int shellEventHandle(void *pv, NoticeKind nk, + void *pvSubject, ShellExState *psx){ + FILE *out = SHX_HELPER(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", pvSubject, zWhat); + if( psx->dbUser != pvSubject ) fprintf(out, "not dbx(%p)\n", psx->dbUser); + }else if( nk==NK_DbAboutToClose ){ + fprintf(out, "db(%p) closing\n", pvSubject); + } + return 0; +} + +/* +** Extension load function. +*/ +extern "C" +#ifdef _WIN32 +__declspec(dllexport) +#endif +int sqlite3_testshellext_init( + sqlite3 *db, + char **pzErrMsg, + const sqlite3_api_routines *pApi +){ + int iLdErr; + int nErr = 0; + SQLITE_EXTENSION_INIT2(pApi); + SHELL_EXTENSION_INIT2(pShExtLink, shextLinkFetcher, db); + + SHELL_EXTENSION_INIT3(pShExtApi, pExtHelpers, pShExtLink); + iLdErr = SHELL_EXTENSION_LOADFAIL_WHY(pShExtLink, 5, 5); + if( iLdErr!=EXLD_Ok ){ + *pzErrMsg = sqlite3_mprintf("Load failed, cause %d\n", iLdErr); + return SQLITE_ERROR; + }else{ + ShellExState *psx = pShExtLink->pSXS; + int rc; + + SHX_API(subscribeEvents)(psx, sqlite3_testshellext_init, &batty, + NK_CountOf, shellEventHandle); + batty.pPrint = SHX_HELPER(findMetaCommand)("print", psx, &rc); + rc = SHX_API(registerMetaCommand)(psx, sqlite3_testshellext_init, &batty); + if( rc!=0 ) ++nErr; + pShExtLink->eid = sqlite3_testshellext_init; + } + return nErr ? SQLITE_ERROR : SQLITE_OK; +}