1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-07-30 19:03:16 +03:00

In wasm builds, ifdef out shell commands which require file I/O, pipes, or which trigger an exit() (.quit and .exit). Documented some of the quirks and limitations of the C/WASM crossover. Keep the JS code from calling into the C code after an exit() has been triggered.

FossilOrigin-Name: bee436e62a956e49b0df4a92abff2c89f2b44e21d8f593716df0331f8fc49814
This commit is contained in:
stephan
2022-05-18 22:58:34 +00:00
parent b0dae2b3c3
commit 02520cc8f6
5 changed files with 132 additions and 25 deletions

View File

@ -53,13 +53,22 @@
.button-bar button {
margin: 0.25em 1em;
}
label {
label[for] {
cursor: pointer;
}
fieldset {
border-radius: 0.5em;
}
.error {
color: red;
background-color: yellow;
}
.hidden {
position: absolute !important;
opacity: 0 !important;
pointer-events: none !important;
display: none !important;
}
</style>
</head>
<body>
@ -97,6 +106,34 @@ select * from t;</textarea>
<label for='opt-cb-sbs'>Side-by-side</label>
</div>
</fieldset>
<hr>
<header>
Notes and Caveats
(<input id='cb-notes-caveats' type='checkbox'>
<label for='cb-notes-caveats'>hide</label>)
</header>
<div id='notes-caveats'>
<p>
This JavaScript application runs a C application which has been
compiled into WASM (Web Assembly). As such, it has certain
limitations. Those include, but are not limited to:
</p>
<ul>
<li>It <strong>cannot recover after a call to
<code>exit()</code></strong>. If the native code triggers
an exit, reloading the page is the only way to restart
the application. (Making the app restartable without reloading
the page would require significant surgery in the C code.)
</li>
<li>It <strong>cannot perform any file I/O</strong>, and running
any command which attempts to do so might trigger an
<code>exit()</code>.
</li>
<li>A number of dot-commands available in the CLI shell are
explicitly removed from this version of the shell.
</li>
</ul>
</div><!-- #notes-caveats -->
<script type='text/javascript'>
(function(){
/**
@ -126,7 +163,14 @@ select * from t;</textarea>
btnClearOut.addEventListener('click',function(){
taOutput.value = '';
},false);
const doExec = Module.cwrap('fiddle_exec', null, ['string']);
const doExec = function f(sql){
if(!f._) f._ = Module.cwrap('fiddle_exec', null, ['string']);
if(Module._isDead){
Module.printErr("shell module has exit()ed. Cannot run SQL.");
}else{
f._(sql);
}
};
const btnRun = document.querySelector('#btn-run');
btnRun.addEventListener('click',function(){
const sql = taInput.value.trim();
@ -136,11 +180,18 @@ select * from t;</textarea>
},false);
doExec(null)/*sets up the db and outputs the header*/;
let e = document.querySelector('#opt-cb-sbs');
const mainWrapper = document.querySelector('#main-wrapper');
e.addEventListener('change', function(){
mainWrapper.classList[this.checked ? 'add' : 'remove']('side-by-side');
}, false);
document.querySelector('#opt-cb-sbs')
.addEventListener('change', function(){
document.querySelector('#main-wrapper').classList[
this.checked ? 'add' : 'remove'
]('side-by-side');
}, false);
document.querySelector('#cb-notes-caveats')
.addEventListener('change', function(){
document.querySelector('#notes-caveats').classList[
this.checked ? 'add' : 'remove'
]('hidden');
}, false);
/* For all buttons with data-cmd=X, map a click handler which
calls doExec(X). */
@ -217,6 +268,7 @@ select * from t;</textarea>
window.onerror = function(/*message, source, lineno, colno, error*/) {
const err = arguments[4];
if(err && 'ExitStatus'==err.name){
Module._isDead = true;
Module.printErr("FATAL ERROR:", err.message);
Module.printErr("Restarting the app requires reloading the page.");
const taOutput = document.querySelector('#output');

View File

@ -64,7 +64,30 @@ 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.
WASM loader is pedantic about that detail.
# Known Quirks and Limitations
Some "impedence mismatch" between C and WASM/JavaScript is to be
expected.
## No I/O
sqlite3 shell commands which require file I/O or pipes are disabled in
the WASM build.
## `exit()` Triggered from C
When C code calls `exit()`, as happens (for example) when running an
"unsafe" command when safe mode is active, WASM's connection to the
sqlite3 shell environment has no sensible choice but to shut down
because `exit()` leaves it in a state we can no longer recover
from. The JavaScript-side application attempts to recognize this and
warn the user that restarting the application is necessary. Currently
the only way to restart it is to reload the page. Restructuring the
shell code such that it could be "rebooted" without restarting the
JS app would require some invasive changes which are not currently
on any TODO list but have not been entirely ruled out long-term.
[emscripten]: https://emscripten.org

View File

@ -1,5 +1,5 @@
C Improved\shandling/reporting\sof\sconditions\swhich\strigger\san\sexit()\sfrom\snative\scode,\se.g.\scalling\sthe\s'.read'\scommand.\sAdded\sa\sHelp\sbutton\swhich\ssimply\ssubmits\sthe\s'.help'\scommand.\sAdded\scommented-out\svariants\sof\svarious\s-Ox\sflags\sto\ssimplify\sexperimenting\swith\sthem.
D 2022-05-18T21:18:21.599
C In\swasm\sbuilds,\sifdef\sout\sshell\scommands\swhich\srequire\sfile\sI/O,\spipes,\sor\swhich\strigger\san\sexit()\s(.quit\sand\s.exit).\sDocumented\ssome\sof\sthe\squirks\sand\slimitations\sof\sthe\sC/WASM\scrossover.\sKeep\sthe\sJS\scode\sfrom\scalling\sinto\sthe\sC\scode\safter\san\sexit()\shas\sbeen\striggered.
D 2022-05-18T22:58:34.214
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
@ -56,8 +56,8 @@ F ext/expert/sqlite3expert.c 6ca30d73b9ed75bd56d6e0d7f2c962d2affaa72c505458619d0
F ext/expert/sqlite3expert.h ca81efc2679a92373a13a3e76a6138d0310e32be53d6c3bfaedabd158ea8969b
F ext/expert/test_expert.c d56c194b769bdc90cf829a14c9ecbc1edca9c850b837a4d0b13be14095c32a72
F ext/fiddle/Makefile ea647919e6ac4b50edde1490f60ee87e8ccd75141e4aa650718c6f28eb323bbc
F ext/fiddle/fiddle.in.html be6f4402b5b3e6287004b1b4d76c049d1fc0a5ae3b642578241e77e853fda30e
F ext/fiddle/index.md 08d25ec6fe2a56923e8ea6e5d6c80907bf3a60f9c40a6841a8f402e402dd5f22
F ext/fiddle/fiddle.in.html 4836ba55dba4f29e6dc356e4ab763a6bcb0e13a7835da24b68f746078121a655
F ext/fiddle/index.md d9c1c308d8074341bc3b11d1d39073cd77754cb3ca9aeb949f23fdd8323d81cf
F ext/fts1/README.txt 20ac73b006a70bcfd80069bdaf59214b6cf1db5e
F ext/fts1/ft_hash.c 3927bd880e65329bdc6f506555b228b28924921b
F ext/fts1/ft_hash.h 06df7bba40dadd19597aa400a875dbc2fed705ea
@ -557,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 1a7cdd4b71b747fcf41902068657a7e73273a2c1a98d0ceb044dbd5874c2f0e4
F src/shell.c.in be0687bf657dfa3df50153063d5629ec1324f44da11b21acd3920384f86450b7
F src/sqlite.h.in d15c307939039086adca159dd340a94b79b69827e74c6d661f343eeeaefba896
F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
F src/sqlite3ext.h a988810c9b21c0dc36dc7a62735012339dc76fc7ab448fb0792721d30eacb69d
@ -1957,8 +1957,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 9bf042b2eb2137239a59e421e89eb463e719b264eac3db2adae44e321b9a4ad3
R 7e80413f51d07b2a693b13b3778bbe1a
P bf06ddf4125d2726019fa16d312726c8551094be991509499b5688f6a68a7747
R f918d85b43414b7ea69ea32ee376905e
U stephan
Z 307b97d13682722e9b5c5bfc0074797a
Z d35b2897d114db6dc646414a4ea884d4
# Remove this line to create a well-formed Fossil manifest.

View File

@ -1 +1 @@
bf06ddf4125d2726019fa16d312726c8551094be991509499b5688f6a68a7747
bee436e62a956e49b0df4a92abff2c89f2b44e21d8f593716df0331f8fc49814

View File

@ -4241,13 +4241,14 @@ static int run_schema_dump_query(
** Text of help messages.
**
** The help text for each individual command begins with a line that starts
** with ".". Subsequent lines are supplimental information.
** with ".". Subsequent lines are supplemental information.
**
** There must be two or more spaces between the end of the command and the
** start of the description of what that command does.
*/
static const char *(azHelp[]) = {
#if defined(SQLITE_HAVE_ZLIB) && !defined(SQLITE_OMIT_VIRTUALTABLE)
#if defined(SQLITE_HAVE_ZLIB) && !defined(SQLITE_OMIT_VIRTUALTABLE) \
&& !defined(SQLITE_SHELL_WASM_MODE)
".archive ... Manage SQL archives",
" Each command must have exactly one of the following options:",
" -c, --create Create a new archive",
@ -4273,10 +4274,12 @@ static const char *(azHelp[]) = {
#ifndef SQLITE_OMIT_AUTHORIZATION
".auth ON|OFF Show authorizer callbacks",
#endif
#ifndef SQLITE_SHELL_WASM_MODE
".backup ?DB? FILE Backup DB (default \"main\") to FILE",
" Options:",
" --append Use the appendvfs",
" --async Write to FILE without journal and fsync()",
#endif
".bail on|off Stop after hitting an error. Default OFF",
".binary on|off Turn binary output on or off. Default OFF",
".cd DIRECTORY Change the working directory to DIRECTORY",
@ -4303,9 +4306,13 @@ static const char *(azHelp[]) = {
" trace Like \"full\" but enable \"PRAGMA vdbe_trace\"",
#endif
" trigger Like \"full\" but also show trigger bytecode",
#ifndef SQLITE_SHELL_WASM_MODE
".excel Display the output of next command in spreadsheet",
" --bom Put a UTF8 byte-order mark on intermediate file",
#endif
#ifndef SQLITE_SHELL_WASM_MODE
".exit ?CODE? Exit this program with return-code CODE",
#endif
".expert EXPERIMENTAL. Suggest indexes for queries",
".explain ?on|off|auto? Change the EXPLAIN formatting mode. Default: auto",
".filectrl CMD ... Run various sqlite3_file_control() operations",
@ -4371,6 +4378,7 @@ static const char *(azHelp[]) = {
" TABLE The name of SQL table used for \"insert\" mode",
".nonce STRING Suspend safe mode for one command if nonce matches",
".nullvalue STRING Use STRING in place of NULL values",
#ifndef SQLITE_SHELL_WASM_MODE
".once ?OPTIONS? ?FILE? Output for the next SQL command only to FILE",
" If FILE begins with '|' then open as a pipe",
" --bom Put a UTF8 byte-order mark at the beginning",
@ -4379,6 +4387,7 @@ static const char *(azHelp[]) = {
".open ?OPTIONS? ?FILE? Close existing database and reopen FILE",
" Options:",
" --append Use appendvfs to append database to the end of FILE",
#endif
#ifndef SQLITE_OMIT_DESERIALIZE
" --deserialize Load into memory using sqlite3_deserialize()",
" --hexdb Load the output of \"dbtotxt\" as an in-memory db",
@ -4410,9 +4419,11 @@ static const char *(azHelp[]) = {
" --reset Reset the count for each input and interrupt",
#endif
".prompt MAIN CONTINUE Replace the standard prompts",
#ifndef SQLITE_SHELL_WASM_MODE
".quit Exit this program",
".read FILE Read input from FILE or command output",
" If FILE begins with \"|\", it is a command that generates the input.",
#endif
#if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_ENABLE_DBPAGE_VTAB)
".recover Recover as much data as possible from corrupt db.",
" --freelist-corrupt Assume the freelist is corrupt",
@ -4421,8 +4432,10 @@ static const char *(azHelp[]) = {
" --no-rowids Do not attempt to recover rowid values",
" that are not also INTEGER PRIMARY KEYs",
#endif
#ifndef SQLITE_SHELL_WASM_MODE
".restore ?DB? FILE Restore content of DB (default \"main\") from FILE",
".save ?OPTIONS? FILE Write database to FILE (an alias for .backup ...)",
#endif
".scanstats on|off Turn sqlite3_stmt_scanstatus() metrics on or off",
".schema ?PATTERN? Show the CREATE statements matching PATTERN",
" Options:",
@ -4456,7 +4469,7 @@ static const char *(azHelp[]) = {
" --sha3-384 Use the sha3-384 algorithm",
" --sha3-512 Use the sha3-512 algorithm",
" Any other argument is a LIKE pattern for tables to hash",
#ifndef SQLITE_NOHAVE_SYSTEM
#if !defined(SQLITE_NOHAVE_SYSTEM) && !defined(SQLITE_SHELL_WASM_MODE)
".shell CMD ARGS... Run CMD ARGS... in a system shell",
#endif
".show Show the current values for various settings",
@ -4465,11 +4478,13 @@ static const char *(azHelp[]) = {
" on Turn on automatic stat display",
" stmt Show statement stats",
" vmstep Show the virtual machine step count only",
#ifndef SQLITE_NOHAVE_SYSTEM
#if !defined(SQLITE_NOHAVE_SYSTEM) && !defined(SQLITE_SHELL_WASM_MODE)
".system CMD ARGS... Run CMD ARGS... in a system shell",
#endif
".tables ?TABLE? List names of tables matching LIKE pattern TABLE",
#ifndef SQLITE_SHELL_WASM_MODE
".testcase NAME Begin redirecting output to 'testcase-out.txt'",
#endif
".testctrl CMD ... Run various sqlite3_test_control() operations",
" Run \".testctrl\" with no arguments for details",
".timeout MS Try opening locked tables for MS milliseconds",
@ -8146,7 +8161,8 @@ static int do_meta_command(char *zLine, ShellState *p){
}else
#endif
#if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_HAVE_ZLIB)
#if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_HAVE_ZLIB) \
&& !defined(SQLITE_SHELL_WASM_MODE)
if( c=='a' && strncmp(azArg[0], "archive", n)==0 ){
open_db(p, 0);
failIfSafeMode(p, "cannot run .archive in safe mode");
@ -8154,6 +8170,7 @@ static int do_meta_command(char *zLine, ShellState *p){
}else
#endif
#ifndef SQLITE_SHELL_WASM_MODE
if( (c=='b' && n>=3 && strncmp(azArg[0], "backup", n)==0)
|| (c=='s' && n>=3 && strncmp(azArg[0], "save", n)==0)
){
@ -8222,6 +8239,7 @@ static int do_meta_command(char *zLine, ShellState *p){
}
close_db(pDest);
}else
#endif /* !defined(SQLITE_SHELL_WASM_MODE) */
if( c=='b' && n>=3 && strncmp(azArg[0], "bail", n)==0 ){
if( nArg==2 ){
@ -8603,10 +8621,12 @@ static int do_meta_command(char *zLine, ShellState *p){
}
}else
#ifndef SQLITE_SHELL_WASM_MODE
if( c=='e' && strncmp(azArg[0], "exit", n)==0 ){
if( nArg>1 && (rc = (int)integerValue(azArg[1]))!=0 ) exit(rc);
rc = 2;
}else
#endif
/* The ".explain" command is automatic now. It is largely pointless. It
** retained purely for backwards compatibility */
@ -9608,6 +9628,7 @@ static int do_meta_command(char *zLine, ShellState *p){
}
}else
#ifndef SQLITE_SHELL_WASM_MODE
if( (c=='o'
&& (strncmp(azArg[0], "output", n)==0||strncmp(azArg[0], "once", n)==0))
|| (c=='e' && n==5 && strcmp(azArg[0],"excel")==0)
@ -9723,6 +9744,7 @@ static int do_meta_command(char *zLine, ShellState *p){
}
sqlite3_free(zFile);
}else
#endif /* !defined(SQLITE_SHELL_WASM_MODE) */
if( c=='p' && n>=3 && strncmp(azArg[0], "parameter", n)==0 ){
open_db(p,0);
@ -9892,10 +9914,13 @@ static int do_meta_command(char *zLine, ShellState *p){
}
}else
#ifndef SQLITE_SHELL_WASM_MODE
if( c=='q' && strncmp(azArg[0], "quit", n)==0 ){
rc = 2;
}else
#endif
#ifndef SQLITE_SHELL_WASM_MODE
if( c=='r' && n>=3 && strncmp(azArg[0], "read", n)==0 ){
FILE *inSaved = p->in;
int savedLineno = p->lineno;
@ -9930,7 +9955,9 @@ static int do_meta_command(char *zLine, ShellState *p){
p->in = inSaved;
p->lineno = savedLineno;
}else
#endif /* !defined(SQLITE_SHELL_WASM_MODE) */
#ifndef SQLITE_SHELL_WASM_MODE
if( c=='r' && n>=3 && strncmp(azArg[0], "restore", n)==0 ){
const char *zSrcFile;
const char *zDb;
@ -9982,6 +10009,7 @@ static int do_meta_command(char *zLine, ShellState *p){
}
close_db(pSrc);
}else
#endif /* !defined(SQLITE_SHELL_WASM_MODE) */
if( c=='s' && strncmp(azArg[0], "scanstats", n)==0 ){
if( nArg==2 ){
@ -10607,7 +10635,7 @@ static int do_meta_command(char *zLine, ShellState *p){
sqlite3_free(zSql);
}else
#ifndef SQLITE_NOHAVE_SYSTEM
#if !defined(SQLITE_NOHAVE_SYSTEM) && !defined(SQLITE_SHELL_WASM_MODE)
if( c=='s'
&& (strncmp(azArg[0], "shell", n)==0 || strncmp(azArg[0],"system",n)==0)
){
@ -10628,7 +10656,7 @@ static int do_meta_command(char *zLine, ShellState *p){
sqlite3_free(zCmd);
if( x ) raw_printf(stderr, "System command returns %d\n", x);
}else
#endif /* !defined(SQLITE_NOHAVE_SYSTEM) */
#endif /* !defined(SQLITE_NOHAVE_SYSTEM) && !defined(SQLITE_SHELL_WASM_MODE) */
if( c=='s' && strncmp(azArg[0], "show", n)==0 ){
static const char *azBool[] = { "off", "on", "trigger", "full"};
@ -10808,6 +10836,7 @@ static int do_meta_command(char *zLine, ShellState *p){
sqlite3_free(azResult);
}else
#ifndef SQLITE_SHELL_WASM_MODE
/* Begin redirecting output to the file "testcase-out.txt" */
if( c=='t' && strcmp(azArg[0],"testcase")==0 ){
output_reset(p);
@ -10821,6 +10850,7 @@ static int do_meta_command(char *zLine, ShellState *p){
sqlite3_snprintf(sizeof(p->zTestcase), p->zTestcase, "?");
}
}else
#endif /* !defined(SQLITE_SHELL_WASM_MODE) */
#ifndef SQLITE_UNTESTABLE
if( c=='t' && n>=8 && strncmp(azArg[0], "testctrl", n)==0 ){
@ -12525,7 +12555,9 @@ void fiddle_exec(const char * zSql){
int rc = 0;
if(!once){
/* Simulate an argv array for main() */
static char * argv[] = {"fiddle", "-bail", "-safe"};
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));