]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
In wasm builds, ifdef out shell commands which require file I/O, pipes, or which...
authorstephan <stephan@noemail.net>
Wed, 18 May 2022 22:58:34 +0000 (22:58 +0000)
committerstephan <stephan@noemail.net>
Wed, 18 May 2022 22:58:34 +0000 (22:58 +0000)
FossilOrigin-Name: bee436e62a956e49b0df4a92abff2c89f2b44e21d8f593716df0331f8fc49814

ext/fiddle/fiddle.in.html
ext/fiddle/index.md
manifest
manifest.uuid
src/shell.c.in

index 4568c4b2cf99a118923b88e087706200feb64fda..9e761d6ffa78ddf0573283166b106a293cf4280d 100644 (file)
       .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');
index 9f5ab41b9a552d45342bda85336b6c9e1274f634..9d1f8d83eae116aebca6c227f1364ef8f0cd533a 100644 (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
index 570065c9dc5f4a7cc26b4c7472d571f7f76590b8..15c9edcabf0395d6edc9e77750b33933e95ec757 100644 (file)
--- a/manifest
+++ b/manifest
@@ -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.
index da2117dbd323caed502399da0ba386c66bfb5e0e..61fcbbb319367494ff3ef22c9e8db7a5d9fc61ce 100644 (file)
@@ -1 +1 @@
-bf06ddf4125d2726019fa16d312726c8551094be991509499b5688f6a68a7747
\ No newline at end of file
+bee436e62a956e49b0df4a92abff2c89f2b44e21d8f593716df0331f8fc49814
\ No newline at end of file
index fc097a13ef1fe98b7531d11c4a955a691e65748e..fc9ffb11523d8967e88c44ee933ff7df0041a134 100644 (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));