Merge the changes to support the "fiddle" extension.

FossilOrigin-Name: 58585f01aa4747d3a09771fb462066bd037914f435ff04fa16ed9b0571e7912a
This commit is contained in:
drh 2022-05-19 16:59:46 +00:00
commit 9d023c227f
9 changed files with 903 additions and 25 deletions

View File

@ -1512,3 +1512,36 @@ 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_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_opt = -O0
#emcc_opt = -O1
#emcc_opt = -O2
#emcc_opt = -O3
emcc_opt = -Oz
emcc_flags = $(emcc_opt) $(SHELL_OPT) \
-sEXPORTED_RUNTIME_METHODS=ccall,cwrap \
-sEXPORTED_FUNCTIONS=_fiddle_exec \
-sEXIT_RUNTIME=1 \
--pre-js $(fiddle_dir)/module-pre.js \
--post-js $(fiddle_dir)/module-post.js \
--shell-file $(fiddle_dir)/fiddle.in.html \
$(fiddle_cflags)
# $(fiddle_cflags) is intended to be passed to make via the CLI in
# order to override, e.g., -Ox for one-off builds.
$(fiddle_html): Makefile sqlite3.c shell.c \
$(fiddle_dir)/fiddle.in.html \
$(fiddle_dir)/module-pre.js $(fiddle_dir)/module-post.js
emcc -o $@ $(emcc_flags) sqlite3.c shell.c
fiddle: $(fiddle_html)

7
ext/fiddle/Makefile Normal file
View File

@ -0,0 +1,7 @@
# This makefile exists primarily to simplify/speed up development from
# emacs. It is not part of the canonical build process.
default:
make -C ../.. fiddle -e emcc_opt=-O0
clean:
make -C ../../ clean-fiddle

239
ext/fiddle/fiddle.in.html Normal file
View File

@ -0,0 +1,239 @@
<!doctype html>
<html lang="en-us">
<head>
<meta charset="utf-8">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>sqlite3 fiddle</title>
<!-- script src="jqterm/jqterm-bundle.min.js"></script>
<link rel="stylesheet" href="jqterm/jquery.terminal.min.css"/ -->
<style>
/* emcscript-related styling, used during the intialization phase... */
.emscripten { padding-right: 0; margin-left: auto; margin-right: auto; display: block; }
div.emscripten { text-align: center; }
div.emscripten_border { border: 1px solid black; }
#spinner { overflow: visible; }
#spinner > * {
margin-top: 1em;
}
.spinner {
height: 50px;
width: 50px;
margin: 0px auto;
animation: rotation 0.8s linear infinite;
border-left: 10px solid rgb(0,150,240);
border-right: 10px solid rgb(0,150,240);
border-bottom: 10px solid rgb(0,150,240);
border-top: 10px solid rgb(100,0,200);
border-radius: 100%;
background-color: rgb(200,100,250);
}
@keyframes rotation {
from {transform: rotate(0deg);}
to {transform: rotate(360deg);}
}
/* The following styles are for app-level use. */
textarea {
font-family: monospace;
flex: 1 1 auto;
}
header {
font-size: 130%;
font-weight: bold;
}
#main-wrapper {
display: flex;
flex-direction: column-reverse;
flex: 20 1 auto;
}
#main-wrapper.side-by-side {
flex-direction: row-reverse;
}
#main-wrapper.swapio {
flex-direction: column;
}
#main-wrapper.side-by-side.swapio {
flex-direction: row;
}
.ta-wrapper{
display: flex;
flex-direction: column;
align-items: stretch;
margin: 0 0.25em;
flex: 1 1 auto;
}
.ta-wrapper.input { flex: 10 1 auto; }
.ta-wrapper.output { flex: 20 1 auto; }
.ta-wrapper textarea {
font-size: 110%;
filter: invert(100%);
flex: 10 1 auto;
}
/*#main-wrapper:not(.side-by-side) .ta-wrapper.input {
flex: 5 1 auto;
}*/
.button-bar {
display: flex;
justify-content: center;
flex: 0 1 auto;
}
.button-bar button {
margin: 0.25em 1em;
}
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;
}
.initially-hidden {
position: absolute !important;
opacity: 0 !important;
pointer-events: none !important;
display: none !important;
}
fieldset.options {
font-size: 75%;
}
fieldset > legend {
padding: 0 0.5em;
}
span.labeled-input {
padding: 0.25em;
margin: 0.25em 0.5em;
border-radius: 0.25em;
white-space: nowrap;
background: #0002;
}
#notes-caveats {
border-top: 1px dotted;
padding-top: 0.25em;
margin-top: 0.5em;
}
.center { text-align: center; }
body.terminal-mode {
max-height: calc(100% - 2em);
display: flex;
flex-direction: column;
align-items: stretch;
}
#jqterminal {
}
.app-view {
flex: 20 1 auto;
}
#titlebar {
display: flex;
justify-content: space-between;
margin-bottom: 0.5em;
}
#view-split {
display: flex;
flex-direction: column;
}
</style>
</head>
<body>
<header id='titlebar'><span>sqlite3 fiddle</span></header>
<figure id="spinner">
<div class="spinner"></div>
<div class='center'><strong>Initializing app...</strong></div>
<div class='center'>
On a slow internet connection this may take a moment. If this
message displays for "a long time", intialization may have
failed and the JavaScript console may contain clues as to why.
</div>
</figure>
<div class="emscripten" id="status">Downloading...</div>
<div class="emscripten">
<progress value="0" max="100" id="progress" hidden='1'></progress>
</div>
<div id='jqterminal' class='app-view hidden initially-hidden'>
This is a placeholder for a terminal-like view.
</div>
<div id='view-split' class='app-view initially-hidden'>
<fieldset class='options'>
<legend>Options</legend>
<div class=''>
<span class='labeled-input'>
<input type='checkbox' id='opt-cb-sbs'
data-csstgt='#main-wrapper'
data-cssclass='side-by-side'
data-config='sideBySide'>
<label for='opt-cb-sbs'>Side-by-side</label>
</span>
<span class='labeled-input'>
<input type='checkbox' id='opt-cb-swapio'
data-csstgt='#main-wrapper'
data-cssclass='swapio'
data-config='swapInOut'>
<label for='opt-cb-swapio'>Swap in/out</label>
</span>
<span class='labeled-input'>
<input type='checkbox' id='opt-cb-autoscroll'
data-config='autoScrollOutput'>
<label for='opt-cb-autoscroll'>Auto-scroll output</label>
</span>
<span class='labeled-input'>
<input type='checkbox' id='opt-cb-autoclear'
data-config='autoClearOutput'>
<label for='opt-cb-autoclear'>Auto-clear output</label>
</span>
</div>
</fieldset>
<div id='main-wrapper' class=''>
<div class='ta-wrapper input'>
<textarea id="input"
placeholder="Shell input. Ctrl-enter/shift-enter runs it.">
-- Use ctrl-enter or shift-enter to execute SQL
.nullvalue NULL
.mode box
CREATE TABLE t(a,b);
INSERT INTO t(a,b) VALUES('abc',123),('def',456),(NULL,789),('ghi',012);
SELECT * FROM t;</textarea>
<div class='button-bar'>
<button id='btn-run'>Run</button>
<button id='btn-clear'>Clear</button>
<button data-cmd='.help'>Help</button>
</div>
</div>
<div class='ta-wrapper output'>
<textarea id="output" readonly
placeholder="Shell output."></textarea>
<div class='button-bar'>
<button id='btn-clear-output'>Clear</button>
</div>
</div>
</div>
</div> <!-- .app-view -->
<!-- Maintenance notes:
- emscripten module init goes is in fiddle-pre.js and gets
prepended to the generated script code.
- App-specific code is in fiddle-post.js and gets appended to
the generated script code.
- The following placeholder (if you're reading this in the
input template file) gets replaced by a generated
amalgamation of: module-pre.js, emcc-generated bootstrapping
code, and module-post.js.
-->
{{{ SCRIPT }}}
</body>
</html>

94
ext/fiddle/index.md Normal file
View File

@ -0,0 +1,94 @@
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.
# 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
[althttpd]: https://sqlite.org/althttpd

233
ext/fiddle/module-post.js Normal file
View File

@ -0,0 +1,233 @@
/* This is the --post-js file for emcc. It gets appended to the
generated fiddle.js. It should contain all app-level code.
Maintenance achtung: do not call any wasm-bound functions from
outside of the onRuntimeInitialized() function. They are not
permitted to be called until after the module init is complete,
which does not happen until after this file is processed. Once that
init is finished, Module.onRuntimeInitialized() will be
triggered. All app-level init code should go into that callback or
be triggered via it. Calling wasm-bound functions before that
callback is run will trigger an assertion in the wasm environment.
*/
window.Module.onRuntimeInitialized = function(){
'use strict';
const Module = window.Module /* wasm module as set up by emscripten */;
delete Module.onRuntimeInitialized;
/* querySelectorAll() proxy */
const EAll = function(/*[element=document,] cssSelector*/){
return (arguments.length>1 ? arguments[0] : document)
.querySelectorAll(arguments[arguments.length-1]);
};
/* querySelector() proxy */
const E = function(/*[element=document,] cssSelector*/){
return (arguments.length>1 ? arguments[0] : document)
.querySelector(arguments[arguments.length-1]);
};
// Unhide all elements which start out hidden
EAll('.initially-hidden').forEach((e)=>e.classList.remove('initially-hidden'));
const taInput = E('#input');
const btnClearIn = E('#btn-clear');
btnClearIn.addEventListener('click',function(){
taInput.value = '';
},false);
// Ctrl-enter and shift-enter both run the current SQL.
taInput.addEventListener('keydown',function(ev){
if((ev.ctrlKey || ev.shiftKey) && 13 === ev.keyCode){
ev.preventDefault();
ev.stopPropagation();
btnRun.click();
}
}, false);
const taOutput = E('#output');
const btnClearOut = E('#btn-clear-output');
btnClearOut.addEventListener('click',function(){
taOutput.value = '';
if(Module.jqTerm) Module.jqTerm.clear();
},false);
/* Sends the given text to the shell. If it's null or empty, this
is a no-op except that the very first call will initialize the
db and output an informational header. */
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.");
return;
}
if(Module.config.autoClearOutput) taOutput.value='';
f._(sql);
};
const btnRun = E('#btn-run');
btnRun.addEventListener('click',function(){
const sql = taInput.value.trim();
if(sql){
doExec(sql);
}
},false);
const mainWrapper = E('#main-wrapper');
/* For each checkboxes with data-csstgt, set up a handler which
toggles the given CSS class on the element matching
E(data-csstgt). */
EAll('input[type=checkbox][data-csstgt]')
.forEach(function(e){
const tgt = E(e.dataset.csstgt);
const cssClass = e.dataset.cssclass || 'error';
e.checked = tgt.classList.contains(cssClass);
e.addEventListener('change', function(){
tgt.classList[
this.checked ? 'add' : 'remove'
](cssClass)
}, false);
});
/* For each checkbox with data-config=X, set up a binding to
Module.config[X]. These must be set up AFTER data-csstgt
checkboxes so that those two states can be synced properly. */
EAll('input[type=checkbox][data-config]')
.forEach(function(e){
const confVal = !!Module.config[e.dataset.config];
if(e.checked !== confVal){
/* Ensure that data-csstgt mappings (if any) get
synced properly. */
e.checked = confVal;
e.dispatchEvent(new Event('change'));
}
e.addEventListener('change', function(){
Module.config[this.dataset.config] = this.checked;
}, false);
});
/* For each button with data-cmd=X, map a click handler which
calls doExec(X). */
const cmdClick = function(){doExec(this.dataset.cmd);};
EAll('button[data-cmd]').forEach(
e => e.addEventListener('click', cmdClick, false)
);
/**
Given a DOM element, this routine measures its "effective
height", which is the bounding top/bottom range of this element
and all of its children, recursively. For some DOM structure
cases, a parent may have a reported height of 0 even though
children have non-0 sizes.
Returns 0 if !e or if the element really has no height.
*/
const effectiveHeight = function f(e){
if(!e) return 0;
if(!f.measure){
f.measure = function callee(e, depth){
if(!e) return;
const m = e.getBoundingClientRect();
if(0===depth){
callee.top = m.top;
callee.bottom = m.bottom;
}else{
callee.top = m.top ? Math.min(callee.top, m.top) : callee.top;
callee.bottom = Math.max(callee.bottom, m.bottom);
}
Array.prototype.forEach.call(e.children,(e)=>callee(e,depth+1));
if(0===depth){
//console.debug("measure() height:",e.className, callee.top, callee.bottom, (callee.bottom - callee.top));
f.extra += callee.bottom - callee.top;
}
return f.extra;
};
}
f.extra = 0;
f.measure(e,0);
return f.extra;
};
/**
Returns a function, that, as long as it continues to be invoked,
will not be triggered. The function will be called after it stops
being called for N milliseconds. If `immediate` is passed, call
the callback immediately and hinder future invocations until at
least the given time has passed.
If passed only 1 argument, or passed a falsy 2nd argument,
the default wait time set in this function's $defaultDelay
property is used.
Source: underscore.js, by way of https://davidwalsh.name/javascript-debounce-function
*/
const debounce = function f(func, wait, immediate) {
var timeout;
if(!wait) wait = f.$defaultDelay;
return function() {
const context = this, args = Array.prototype.slice.call(arguments);
const later = function() {
timeout = undefined;
if(!immediate) func.apply(context, args);
};
const callNow = immediate && !timeout;
clearTimeout(timeout);
timeout = setTimeout(later, wait);
if(callNow) func.apply(context, args);
};
};
debounce.$defaultDelay = 500 /*arbitrary*/;
const ForceResizeKludge = (function(){
/* Workaround for Safari mayhem regarding use of vh CSS units....
We cannot use vh units to set the terminal area size because
Safari chokes on that, so we calculate that height here. Larger
than ~95% is too big for Firefox on Android, causing the input
area to move off-screen. */
const bcl = document.body.classList;
const appViews = EAll('.app-view');
const resized = function f(){
if(f.$disabled) return;
const wh = window.innerHeight;
var ht;
var extra = 0;
const elemsToCount = [
E('body > header')
];
elemsToCount.forEach((e)=>e ? extra += effectiveHeight(e) : false);
ht = wh - extra;
appViews.forEach(function(e){
e.style.height =
e.style.maxHeight = [
"calc(", (ht>=100 ? ht : 100), "px",
" - 3em"/*fudge value*/,")"
/* ^^^^ hypothetically not needed, but both Chrome/FF on
Linux will force scrollbars on the body if this value is
too small (<0.75em in my tests). */
].join('');
});
};
resized.$disabled = true/*gets deleted when setup is finished*/;
window.addEventListener('resize', debounce(resized, 250), false);
return resized;
})();
Module.print(null/*clear any output generated by the init process*/);
if(window.jQuery && window.jQuery.terminal){
/* Set up the terminal-style view... */
const eTerm = window.jQuery('#jqterminal').empty();
Module.jqTerm = eTerm.terminal(doExec,{
prompt: 'sqlite> ',
greetings: false /* note that the docs incorrectly call this 'greeting' */
});
//Module.jqTerm.clear(/*remove the "greeting"*/);
/* Set up a button to toggle the views... */
const head = E('header#titlebar');
const btnToggleView = jQuery("<button>Toggle View</button>")[0];
head.appendChild(btnToggleView);
btnToggleView.addEventListener('click',function f(){
EAll('.app-view').forEach(e=>e.classList.toggle('hidden'));
if(document.body.classList.toggle('terminal-mode')){
ForceResizeKludge();
}
}, false);
btnToggleView.click();
}
doExec(null/*init the db and output the header*/);
delete ForceResizeKludge.$disabled;
ForceResizeKludge();
};

110
ext/fiddle/module-pre.js Normal file
View File

@ -0,0 +1,110 @@
/* This is the --pre-js file for emcc. It gets prepended to the
generated fiddle.js. It should contain only code which is relevant
to the setup and initialization of the wasm module. */
(function(){
'use strict';
/**
What follows is part of the emscripten core setup. Do not
modify it without understanding what it's doing.
*/
const statusElement = document.getElementById('status');
const progressElement = document.getElementById('progress');
const spinnerElement = document.getElementById('spinner');
const Module = window.Module = {
/* Config object. Referenced by certain Module methods and
app-level code. */
config: {
/* If true, the Module.print() impl will auto-scroll
the output widget to the bottom when it receives output,
else it won't. */
autoScrollOutput: true,
/* If true, the output area will be cleared before each
command is run, else it will not. */
autoClearOutput: false,
/* If true, Module.print() will echo its output to
the console, in addition to its normal output widget. */
printToConsole: true,
/* If true, display input/output areas side-by-side. */
sideBySide: false,
/* If true, swap positions of the input/output areas. */
swapInOut: false
},
preRun: [],
postRun: [],
//onRuntimeInitialized: function(){},
print: (function f() {
/* Maintenance reminder: we currently require/expect a textarea
output element. It might be nice to extend this to behave
differently if the output element is a non-textarea element,
in which case it would need to append the given text as a TEXT
node and add a line break. */
const outputElem = document.getElementById('output');
outputElem.value = ''; // clear browser cache
return function(text) {
if(arguments.length > 1) text = Array.prototype.slice.call(arguments).join(' ');
// These replacements are necessary if you render to raw HTML
//text = text.replace(/&/g, "&amp;");
//text = text.replace(/</g, "&lt;");
//text = text.replace(/>/g, "&gt;");
//text = text.replace('\n', '<br>', 'g');
if(null===text){/*special case: clear output*/
outputElem.value = '';
return;
}
if(window.Module.config.printToConsole) console.log(text);
if(window.Module.jqTerm) window.Module.jqTerm.echo(text);
outputElem.value += text + "\n";
if(window.Module.config.autoScrollOutput){
outputElem.scrollTop = outputElem.scrollHeight;
}
};
})(),
setStatus: function f(text) {
if(!f.last) f.last = { time: Date.now(), text: '' };
if(text === f.last.text) return;
const m = text.match(/([^(]+)\((\d+(\.\d+)?)\/(\d+)\)/);
const now = Date.now();
if(m && now - f.last.time < 30) return; // if this is a progress update, skip it if too soon
f.last.time = now;
f.last.text = text;
if(m) {
text = m[1];
progressElement.value = parseInt(m[2])*100;
progressElement.max = parseInt(m[4])*100;
progressElement.hidden = false;
spinnerElement.hidden = false;
} else {
progressElement.remove();
if(!text) spinnerElement.remove();
}
if(text) statusElement.innerText = text;
else statusElement.remove();
},
totalDependencies: 0,
monitorRunDependencies: function(left) {
this.totalDependencies = Math.max(this.totalDependencies, left);
this.setStatus(left
? ('Preparing... (' + (this.totalDependencies-left)
+ '/' + this.totalDependencies + ')')
: 'All downloads complete.');
}
};
Module.printErr = Module.print/*capture stderr output*/;
Module.setStatus('Downloading...');
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');
if(taOutput) taOutput.classList.add('error');
}
Module.setStatus('Exception thrown, see JavaScript console');
spinnerElement.style.display = 'none';
Module.setStatus = function(text) {
if(text) console.error('[post-exception status] ' + text);
};
};
})();

View File

@ -1,9 +1,9 @@
C Fix\sharmless\scompiler\swarnings\sin\sthe\snew\sunixFullPathname\simplementation.
D 2022-05-17T15:11:57.632
C Merge\sthe\schanges\sto\ssupport\sthe\s"fiddle"\sextension.
D 2022-05-19T16:59:46.843
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
F Makefile.in b210ad2733317f1a4353085dfb9d385ceec30b0e6a61d20a5accabecac6b1949
F Makefile.in 5dbc61c076215a580d59d1f21b5e62955d2e570321b63f00a31b188f1f5089a6
F Makefile.linux-gcc f609543700659711fbd230eced1f01353117621dccae7b9fb70daa64236c5241
F Makefile.msc b28a8a7a977e7312f6859f560348e1eb110c21bd6cf9fab0d16537c0a514eef3
F README.md 8b8df9ca852aeac4864eb1e400002633ee6db84065bd01b78c33817f97d31f5e
@ -55,6 +55,11 @@ 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 b2904d52c10a7c984cfab95c54fb85f33aa8a6b2653faf1527d08ce57114be46
F ext/fiddle/fiddle.in.html ca27f4b0f0477096e78d8b9b44109c234d9305531ab63ecd559a739bdea0b11c
F ext/fiddle/index.md d9c1c308d8074341bc3b11d1d39073cd77754cb3ca9aeb949f23fdd8323d81cf
F ext/fiddle/module-post.js 3d1a368312c598f73eb5d1d715c464ca473d491ad5df4d0636fbcf91a74817a9
F ext/fiddle/module-pre.js a7b046c0f764b100a5bedd3880bece8e6fb5908fb73cb91fb7a9b692bc938862
F ext/fts1/README.txt 20ac73b006a70bcfd80069bdaf59214b6cf1db5e
F ext/fts1/ft_hash.c 3927bd880e65329bdc6f506555b228b28924921b
F ext/fts1/ft_hash.h 06df7bba40dadd19597aa400a875dbc2fed705ea
@ -554,7 +559,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 cc3e19b2d2eefbadc4139b016c097d6478eae01d14eca993368ee5cff8820fff
F src/sqlite.h.in d15c307939039086adca159dd340a94b79b69827e74c6d661f343eeeaefba896
F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
F src/sqlite3ext.h a988810c9b21c0dc36dc7a62735012339dc76fc7ab448fb0792721d30eacb69d
@ -1954,8 +1959,9 @@ 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
P f7e1ceb5b59a876cfd04a8aac0ee2b322c970555b9c361b4953d711ef6596e37 56b82ae806c61b95e62042ca70ed952ce01832b02da55c2b315f9201989514ab
R f9801562283af03ef889fbffa87ba13d
T +closed 56b82ae806c61b95e62042ca70ed952ce01832b02da55c2b315f9201989514ab
U drh
Z 99cc78493b68b2d09cacc03a9e60389c
Z 5ac79eb27823f3c1a240f3c2dd67e98a
# Remove this line to create a well-formed Fossil manifest.

View File

@ -1 +1 @@
f7e1ceb5b59a876cfd04a8aac0ee2b322c970555b9c361b4953d711ef6596e37
58585f01aa4747d3a09771fb462066bd037914f435ff04fa16ed9b0571e7912a

View File

@ -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
@ -798,7 +809,7 @@ static void freeText(ShellText *p){
** If the third argument, quote, is not '\0', then it is used as a
** quote character for zAppend.
*/
static void appendText(ShellText *p, char const *zAppend, char quote){
static void appendText(ShellText *p, const char *zAppend, char quote){
int len;
int i;
int nAppend = strlen30(zAppend);
@ -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 */
const char * zPos; /* Cursor pos into zInput */
} wasm;
#endif
};
#ifdef SQLITE_SHELL_WASM_MODE
static ShellState shellState;
#endif
/* Allowed values for ShellState.autoEQP
*/
@ -4218,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",
@ -4250,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",
@ -4280,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",
@ -4291,6 +4321,7 @@ static const char *(azHelp[]) = {
".fullschema ?--indent? Show schema and the content of sqlite_stat tables",
".headers on|off Turn display of headers on or off",
".help ?-all? ?PATTERN? Show help text for PATTERN",
#ifndef SQLITE_SHELL_WASM_MODE
".import FILE TABLE Import data from FILE into TABLE",
" Options:",
" --ascii Use \\037 and \\036 as column and row separators",
@ -4305,6 +4336,7 @@ static const char *(azHelp[]) = {
" from the \".mode\" output mode",
" * If FILE begins with \"|\" then it is a command that generates the",
" input text.",
#endif
#ifndef SQLITE_OMIT_TEST_CONTROL
".imposter INDEX TABLE Create imposter table TABLE on index INDEX",
#endif
@ -4318,10 +4350,12 @@ static const char *(azHelp[]) = {
".lint OPTIONS Report potential schema issues.",
" Options:",
" fkey-indexes Find missing foreign key indexes",
#ifndef SQLITE_OMIT_LOAD_EXTENSION
#if !defined(SQLITE_OMIT_LOAD_EXTENSION) && !defined(SQLITE_SHELL_WASM_MODE)
".load FILE ?ENTRY? Load an extension library",
#endif
#ifndef SQLITE_SHELL_WASM_MODE
".log FILE|off Turn logging on or off. FILE can be stderr/stdout",
#endif
".mode MODE ?OPTIONS? Set output mode",
" MODE is one of:",
" ascii Columns/rows delimited by 0x1F and 0x1E",
@ -4348,6 +4382,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",
@ -4356,6 +4391,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",
@ -4387,9 +4423,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",
@ -4398,8 +4436,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:",
@ -4433,7 +4473,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",
@ -4442,11 +4482,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",
@ -4991,14 +5033,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
@ -8121,7 +8165,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");
@ -8129,6 +8174,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)
){
@ -8197,6 +8243,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 ){
@ -8578,10 +8625,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 */
@ -8836,6 +8885,7 @@ static int do_meta_command(char *zLine, ShellState *p){
}
}else
#ifndef SQLITE_SHELL_WASM_MODE
if( c=='i' && strncmp(azArg[0], "import", n)==0 ){
char *zTable = 0; /* Insert data into this table */
char *zSchema = 0; /* within this schema (may default to "main") */
@ -9126,6 +9176,7 @@ static int do_meta_command(char *zLine, ShellState *p){
sCtx.nRow, sCtx.nErr, sCtx.nLine-1);
}
}else
#endif /* !defined(SQLITE_SHELL_WASM_MODE) */
#ifndef SQLITE_UNTESTABLE
if( c=='i' && strncmp(azArg[0], "imposter", n)==0 ){
@ -9315,7 +9366,7 @@ static int do_meta_command(char *zLine, ShellState *p){
lintDotCommand(p, azArg, nArg);
}else
#ifndef SQLITE_OMIT_LOAD_EXTENSION
#if !defined(SQLITE_OMIT_LOAD_EXTENSION) && !defined(SQLITE_SHELL_WASM_MODE)
if( c=='l' && strncmp(azArg[0], "load", n)==0 ){
const char *zFile, *zProc;
char *zErrMsg = 0;
@ -9337,6 +9388,7 @@ static int do_meta_command(char *zLine, ShellState *p){
}else
#endif
#ifndef SQLITE_SHELL_WASM_MODE
if( c=='l' && strncmp(azArg[0], "log", n)==0 ){
failIfSafeMode(p, "cannot run .log in safe mode");
if( nArg!=2 ){
@ -9348,6 +9400,7 @@ static int do_meta_command(char *zLine, ShellState *p){
p->pLog = output_file_open(zFile, 0);
}
}else
#endif
if( c=='m' && strncmp(azArg[0], "mode", n)==0 ){
const char *zMode = 0;
@ -9583,6 +9636,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)
@ -9698,6 +9752,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);
@ -9867,10 +9922,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;
@ -9905,7 +9963,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;
@ -9957,6 +10017,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 ){
@ -10582,7 +10643,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)
){
@ -10603,7 +10664,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"};
@ -10783,6 +10844,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);
@ -10796,6 +10858,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 ){
@ -11467,6 +11530,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 +11944,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 +11958,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 +11978,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 +12240,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 +12508,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 +12543,47 @@ 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(const char * 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 */