-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
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
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
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
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.
-861ab023be283782131df5ccd9eff50cf2f06f48f1ea7defd3f08177a3df5889
\ No newline at end of file
+2596e7c439b6c4c89921079a78ba6174e99f766b30fab8763be20c1b631e49ec
\ No newline at end of file
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) }
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) */
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++);
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
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 */
*/
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
/*
* 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 ) \
/* 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 */
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 );
return 0;
}
-DEFINE_SHDB_TO_SHEXTLINK(shext_link);
-
/*
** Extension load function.
*/
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;
}
--- /dev/null
+/*
+** 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 <stdio.h>
+#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;
+}