From: stephan Date: Wed, 18 May 2022 17:14:24 +0000 (+0000) Subject: Initial version of an sqlfiddle-style application using a WASM build of the sqlite3... X-Git-Tag: version-3.39.0~132^2~18 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=f8cd3d2b5c71b305754407e20ef282a0aeb2e0eb;p=thirdparty%2Fsqlite.git Initial version of an sqlfiddle-style application using a WASM build of the sqlite3 shell. FossilOrigin-Name: af9c21c9e0caf05adac7a9fcde39a9164c89f1c78b767b6fdd74a1405a3d373f --- diff --git a/Makefile.in b/Makefile.in index e5d30d030b..92b2525d58 100644 --- a/Makefile.in +++ b/Makefile.in @@ -1512,3 +1512,24 @@ sqlite3.def: $(REAL_LIBOBJ) sqlite3.dll: $(REAL_LIBOBJ) sqlite3.def $(TCC) -shared -o $@ sqlite3.def \ -Wl,"--strip-all" $(REAL_LIBOBJ) + + +# +# fiddle section +# +fiddle_dir = ext/fiddle +fiddle_tmpl = $(fiddle_dir)/fiddle.in.html +fiddle_html = $(fiddle_dir)/fiddle.html +fiddle_generated = $(fiddle_html) \ + $(fiddle_dir)/fiddle.js \ + $(fiddle_dir)/fiddle.wasm +clean-fiddle: + rm -f $(fiddle_generated) +clean: clean-fiddle +emcc_flags = -sEXPORTED_RUNTIME_METHODS=ccall,cwrap \ + -sEXPORTED_FUNCTIONS=_fiddle_exec \ + --shell-file $(fiddle_tmpl) +$(fiddle_html): Makefile sqlite3.c shell.c $(fiddle_tmpl) + emcc -o $@ $(emcc_flags) sqlite3.c shell.c + +fiddle: $(fiddle_html) diff --git a/ext/fiddle/Makefile b/ext/fiddle/Makefile new file mode 100644 index 0000000000..a3bc352aec --- /dev/null +++ b/ext/fiddle/Makefile @@ -0,0 +1,5 @@ +default: + make -C ../.. fiddle + +clean: + make -C ../../ clean-fiddle diff --git a/ext/fiddle/fiddle.in.html b/ext/fiddle/fiddle.in.html new file mode 100644 index 0000000000..ddd7d6984c --- /dev/null +++ b/ext/fiddle/fiddle.in.html @@ -0,0 +1,208 @@ + + + + + + sqlite3 fiddle + + + +
sqlite3 fiddle
+
emscripten
+
Downloading...
+
+ +
+
+
+ +
+ + +
+
+
+ +
+ +
+
+
+
+ Options +
+ + +
+
+
+ + {{{ SCRIPT }}} + + diff --git a/ext/fiddle/index.md b/ext/fiddle/index.md new file mode 100644 index 0000000000..9f5ab41b9a --- /dev/null +++ b/ext/fiddle/index.md @@ -0,0 +1,71 @@ +This directory houses a "fiddle"-style application which embeds a +[Web Assembly (WASM)](https://en.wikipedia.org/wiki/WebAssembly) +build of the sqlite3 shell app into an HTML page, effectively running +the shell in a client-side browser. + +It requires [emscripten][] and that the build environment be set up for +emscripten. A mini-HOWTO for setting that up follows... + +First, install the Emscripten SDK, as documented +[here](https://emscripten.org/docs/getting_started/downloads.html) and summarized +below for Linux environments: + +``` +# Clone the emscripten repository: +$ git clone https://github.com/emscripten-core/emsdk.git +$ cd emsdk + +# Download and install the latest SDK tools: +$ ./emsdk install latest + +# Make the "latest" SDK "active" for the current user: +$ ./emsdk activate latest +``` + +Those parts only need to be run once. The following needs to be run for each +shell instance which needs the `emcc` compiler: + +``` +# Activate PATH and other environment variables in the current terminal: +$ source ./emsdk_env.sh + +$ which emcc +/path/to/emsdk/upstream/emscripten/emcc +``` + +That `env` script needs to be sourced for building this application from the +top of the sqlite3 build tree: + +``` +$ make fiddle +``` + +Or: + +``` +$ cd ext/fiddle +$ make +``` + +That will generate the fiddle application under +[ext/fiddle](/dir/ext/fiddle), as `fiddle.html`. That application +cannot, due to XMLHttpRequest security limitations, run if the HTML +file is opened directly in the browser (i.e. if it is opened using a +`file://` URL), so it needs to be served via an HTTP server. For +example, using [althttpd][]: + +``` +$ cd ext/fiddle +$ althttpd -debug 1 -jail 0 -port 9090 -root . +``` + +Then browse to `http://localhost:9090/fiddle.html`. + +Note that when serving this app via [althttpd][], it must be a version +from 2022-05-17 or newer so that it recognizes the `.wasm` file +extension and responds with the mimetype `application/wasm`, as the +wasm loader is pedantic about that detail. + + +[emscripten]: https://emscripten.org +[althttpd]: https://sqlite.org/althttpd diff --git a/manifest b/manifest index 7f1d327e7e..2cc14ec5e2 100644 --- a/manifest +++ b/manifest @@ -1,9 +1,9 @@ -C Fix\sharmless\scompiler\swarnings\sin\sthe\snew\sunixFullPathname\simplementation. -D 2022-05-17T15:11:57.632 +C Initial\sversion\sof\san\ssqlfiddle-style\sapplication\susing\sa\sWASM\sbuild\sof\sthe\ssqlite3\sshell. +D 2022-05-18T17:14:24.622 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 -F Makefile.in b210ad2733317f1a4353085dfb9d385ceec30b0e6a61d20a5accabecac6b1949 +F Makefile.in ff32504cde350caaf4d52abfdca5dd1fa88b21e53071a0f9cc3539b6789c3606 F Makefile.linux-gcc f609543700659711fbd230eced1f01353117621dccae7b9fb70daa64236c5241 F Makefile.msc b28a8a7a977e7312f6859f560348e1eb110c21bd6cf9fab0d16537c0a514eef3 F README.md 8b8df9ca852aeac4864eb1e400002633ee6db84065bd01b78c33817f97d31f5e @@ -55,6 +55,9 @@ F ext/expert/expert1.test 3c642a4e7bbb14f21ddab595436fb465a4733f47a0fe5b2855e1d5 F ext/expert/sqlite3expert.c 6ca30d73b9ed75bd56d6e0d7f2c962d2affaa72c505458619d0ff5d9cdfac204 F ext/expert/sqlite3expert.h ca81efc2679a92373a13a3e76a6138d0310e32be53d6c3bfaedabd158ea8969b F ext/expert/test_expert.c d56c194b769bdc90cf829a14c9ecbc1edca9c850b837a4d0b13be14095c32a72 +F ext/fiddle/Makefile ea647919e6ac4b50edde1490f60ee87e8ccd75141e4aa650718c6f28eb323bbc +F ext/fiddle/fiddle.in.html 85db5e736f82fd2cae1c4f61b5af62239cc5db363b9eb6f4e383e0d97c59b322 +F ext/fiddle/index.md 08d25ec6fe2a56923e8ea6e5d6c80907bf3a60f9c40a6841a8f402e402dd5f22 F ext/fts1/README.txt 20ac73b006a70bcfd80069bdaf59214b6cf1db5e F ext/fts1/ft_hash.c 3927bd880e65329bdc6f506555b228b28924921b F ext/fts1/ft_hash.h 06df7bba40dadd19597aa400a875dbc2fed705ea @@ -554,7 +557,7 @@ F src/random.c 097dc8b31b8fba5a9aca1697aeb9fd82078ec91be734c16bffda620ced7ab83c F src/resolve.c a4eb3c617027fd049b07432f3b942ea7151fa793a332a11a7d0f58c9539e104f F src/rowset.c ba9515a922af32abe1f7d39406b9d35730ed65efab9443dc5702693b60854c92 F src/select.c 74060a09f66c0c056f3c61627e22cb484af0bbfa29d7d14dcf17c684742c15de -F src/shell.c.in 176cad562152cbbafe7ecc9c83c82850e2c3d0cf33ec0a52d67341d35c842f22 +F src/shell.c.in 1892f21aafee8eca543881ef429f6166386ca1ae0051252e91d1235bd6f4217b F src/sqlite.h.in d15c307939039086adca159dd340a94b79b69827e74c6d661f343eeeaefba896 F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 F src/sqlite3ext.h a988810c9b21c0dc36dc7a62735012339dc76fc7ab448fb0792721d30eacb69d @@ -1954,8 +1957,11 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P d8b249e8cdf0babe1427d0587dbdc27a52ec06a5ef3a20dfb05a0ea4adb85858 -R 56c8d067b3c475439a951ae630953e32 -U drh -Z 99cc78493b68b2d09cacc03a9e60389c +P f7e1ceb5b59a876cfd04a8aac0ee2b322c970555b9c361b4953d711ef6596e37 +R f9ce230cf39758aaeb408ee00f09f455 +T *branch * fiddle +T *sym-fiddle * +T -sym-trunk * +U stephan +Z 33db0432b22b9541a19d442c2dec2c8e # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index e1be3622c7..9427d521e0 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -f7e1ceb5b59a876cfd04a8aac0ee2b322c970555b9c361b4953d711ef6596e37 \ No newline at end of file +af9c21c9e0caf05adac7a9fcde39a9164c89f1c78b767b6fdd74a1405a3d373f \ No newline at end of file diff --git a/src/shell.c.in b/src/shell.c.in index cace8bf2f4..b46a00de66 100644 --- a/src/shell.c.in +++ b/src/shell.c.in @@ -229,6 +229,16 @@ static void setTextMode(FILE *file, int isOutput){ # define setTextMode(X,Y) #endif +/* +** When compiling with emcc (a.k.a. emscripten), we're building a +** WebAssembly (WASM) bundle and need to disable and rewire a few +** things. +*/ +#ifdef __EMSCRIPTEN__ +#define SQLITE_SHELL_WASM_MODE +#else +#undef SQLITE_SHELL_WASM_MODE +#endif /* True if the timer is enabled */ static int enableTimer = 0; @@ -691,6 +701,7 @@ static char *local_getline(char *zLine, FILE *in){ ** be freed by the caller or else passed back into this routine via the ** zPrior argument for reuse. */ +#ifndef SQLITE_SHELL_WASM_MODE static char *one_input_line(FILE *in, char *zPrior, int isContinuation){ char *zPrompt; char *zResult; @@ -710,7 +721,7 @@ static char *one_input_line(FILE *in, char *zPrior, int isContinuation){ } return zResult; } - +#endif /* !SQLITE_SHELL_WASM_MODE */ /* ** Return the value of a hexadecimal digit. Return -1 if the input @@ -1009,16 +1020,18 @@ INCLUDE test_windirent.h INCLUDE test_windirent.c #define dirent DIRENT #endif -INCLUDE ../ext/misc/shathree.c -INCLUDE ../ext/misc/fileio.c -INCLUDE ../ext/misc/completion.c -INCLUDE ../ext/misc/appendvfs.c INCLUDE ../ext/misc/memtrace.c +INCLUDE ../ext/misc/shathree.c INCLUDE ../ext/misc/uint.c INCLUDE ../ext/misc/decimal.c INCLUDE ../ext/misc/ieee754.c INCLUDE ../ext/misc/series.c INCLUDE ../ext/misc/regexp.c +#ifndef SQLITE_SHELL_WASM_MODE +INCLUDE ../ext/misc/fileio.c +INCLUDE ../ext/misc/completion.c +INCLUDE ../ext/misc/appendvfs.c +#endif #ifdef SQLITE_HAVE_ZLIB INCLUDE ../ext/misc/zipfile.c INCLUDE ../ext/misc/sqlar.c @@ -1149,8 +1162,18 @@ struct ShellState { char *zNonce; /* Nonce for temporary safe-mode excapes */ EQPGraph sGraph; /* Information for the graphical EXPLAIN QUERY PLAN */ ExpertInfo expert; /* Valid if previous command was ".expert OPT..." */ +#ifdef SQLITE_SHELL_WASM_MODE + struct { + const char * zInput; /* Input string from wasm/JS proxy */ + char const * zPos; /* Cursor pos into zInput */ + } wasm; +#endif }; +#ifdef SQLITE_SHELL_WASM_MODE +static ShellState shellState; +#endif + /* Allowed values for ShellState.autoEQP */ @@ -4991,14 +5014,16 @@ static void open_db(ShellState *p, int openFlags){ #ifndef SQLITE_OMIT_LOAD_EXTENSION sqlite3_enable_load_extension(p->db, 1); #endif - sqlite3_fileio_init(p->db, 0, 0); sqlite3_shathree_init(p->db, 0, 0); - sqlite3_completion_init(p->db, 0, 0); sqlite3_uint_init(p->db, 0, 0); sqlite3_decimal_init(p->db, 0, 0); sqlite3_regexp_init(p->db, 0, 0); sqlite3_ieee_init(p->db, 0, 0); sqlite3_series_init(p->db, 0, 0); +#ifndef SQLITE_SHELL_WASM_MODE + sqlite3_fileio_init(p->db, 0, 0); + sqlite3_completion_init(p->db, 0, 0); +#endif #if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_ENABLE_DBPAGE_VTAB) sqlite3_dbdata_init(p->db, 0, 0); #endif @@ -11467,6 +11492,39 @@ static void echo_group_input(ShellState *p, const char *zDo){ if( ShellHasFlag(p, SHFLG_Echo) ) utf8_printf(p->out, "%s\n", zDo); } +#ifdef SQLITE_SHELL_WASM_MODE +/* +** Alternate one_input_line() impl for wasm mode. This is not in the primary impl +** because we need the global shellState and cannot access it from that function +** without moving lots of code around (creating a larger/messier diff). +*/ +static char *one_input_line(FILE *in, char *zPrior, int isContinuation){ + /* Parse the next line from shellState.wasm.zInput. */ + const char *zBegin = shellState.wasm.zPos; + const char *z = zBegin; + char *zLine = 0; + int nZ = 0; + + UNUSED_PARAMETER(in); + UNUSED_PARAMETER(isContinuation); + if(!z || !*z){ + return 0; + } + while(*z && isspace(*z)) ++z; + zBegin = z; + for(; *z && '\n'!=*z; ++nZ, ++z){} + if(nZ>0 && '\r'==zBegin[nZ-1]){ + --nZ; + } + shellState.wasm.zPos = z; + zLine = realloc(zPrior, nZ+1); + shell_check_oom(zLine); + memcpy(zLine, zBegin, (size_t)nZ); + zLine[nZ] = 0; + return zLine; +} +#endif /* SQLITE_SHELL_WASM_MODE */ + /* ** Read input from *in and process it. If *in==0 then input ** is interactive - the user is typing it it. Otherwise, input @@ -11848,6 +11906,10 @@ static char *cmdline_option_value(int argc, char **argv, int i){ # endif #endif +#ifdef SQLITE_SHELL_WASM_MODE +# define main fiddle_main +#endif + #if SQLITE_SHELL_IS_UTF8 int SQLITE_CDECL main(int argc, char **argv){ #else @@ -11858,7 +11920,11 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){ sqlite3_uint64 mem_main_enter = sqlite3_memory_used(); #endif char *zErrMsg = 0; +#ifdef SQLITE_SHELL_WASM_MODE +# define data shellState +#else ShellState data; +#endif const char *zInitFile = 0; int i; int rc = 0; @@ -11874,8 +11940,13 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){ setBinaryMode(stdin, 0); setvbuf(stderr, 0, _IONBF, 0); /* Make sure stderr is unbuffered */ +#ifdef SQLITE_SHELL_WASM_MODE + stdin_is_interactive = 0; + stdout_is_console = 1; +#else stdin_is_interactive = isatty(0); stdout_is_console = isatty(1); +#endif #if !defined(_WIN32_WCE) if( getenv("SQLITE_DEBUG_BREAK") ){ @@ -12131,7 +12202,9 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){ #endif } data.out = stdout; +#ifndef SQLITE_SHELL_WASM_MODE sqlite3_appendvfs_init(0,0,0); +#endif /* Go ahead and open the database file if it already exists. If the ** file does not exist, delay opening it. This prevents empty database @@ -12397,6 +12470,9 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){ rc = process_input(&data); } } +#ifndef SQLITE_SHELL_WASM_MODE + /* In WASM mode we have to leave the db state in place so that + ** client code can "push" SQL into it after this call returns. */ free(azCmd); set_table_name(&data, 0); if( data.db ){ @@ -12429,5 +12505,45 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){ (unsigned int)(sqlite3_memory_used()-mem_main_enter)); } #endif +#endif /* !SQLITE_SHELL_WASM_MODE */ return rc; } + + +#ifdef SQLITE_SHELL_WASM_MODE +/* +** Trivial exportable function for emscripten. Needs to be exported using: +** +** emcc ..flags... -sEXPORTED_FUNCTIONS=_fiddle_exec -sEXPORTED_RUNTIME_METHODS=ccall,cwrap +** +** (Note the underscore before the function name.) It processes zSql +** as if it were input to the sqlite3 shell and redirects all output +** to the wasm binding. +*/ +void fiddle_exec(char const * zSql){ + static int once = 0; + int rc = 0; + if(!once){ + /* Simulate an argv array for main() */ + static char * argv[] = {"fiddle", "-bail", "-safe"}; + rc = fiddle_main((int)(sizeof(argv)/sizeof(argv[0])), argv); + once = rc ? -1 : 1; + memset(&shellState.wasm, 0, sizeof(shellState.wasm)); + printf( + "SQLite version %s %.19s\n" /*extra-version-info*/, + sqlite3_libversion(), sqlite3_sourceid() + ); + puts("WASM shell"); + puts("Enter \".help\" for usage hints."); + puts("Connected to a transient in-memory database."); + } + if(once<0){ + puts("DB init failed. Not executing SQL."); + }else if(zSql && *zSql){ + shellState.wasm.zInput = zSql; + shellState.wasm.zPos = zSql; + process_input(&shellState); + memset(&shellState.wasm, 0, sizeof(shellState.wasm)); + } +} +#endif /* SQLITE_SHELL_WASM_MODE */