Compare commits
39 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d59ac28d4f | |||
| 7db1289b0f | |||
| 2d0a85c5de | |||
| e9e0208561 | |||
| 38240592ad | |||
| 6da6f31cca | |||
| 403445be23 | |||
| 618a375e9f | |||
| 0fb074ab61 | |||
| b8a2b20509 | |||
| 9af6beb977 | |||
| 29f2458aae | |||
| 348f7420b3 | |||
| 02520cc8f6 | |||
| b0dae2b3c3 | |||
| 0076e49700 | |||
| 8ae45e4c6b | |||
| 9f69b9411e | |||
| f8cd3d2b5c | |||
| b8b2d9c5e1 | |||
| 5348fbe332 | |||
| ff16267d7d | |||
| c7d7ebd755 | |||
| 07576c3fe8 | |||
| 96d5549778 | |||
| d83997ba7f | |||
| c93bf1d462 | |||
| 086b800fcd | |||
| 556527a154 | |||
| f69dad8c53 | |||
| 767bc8de8e | |||
| a6e8ee12e2 | |||
| f8d2745f99 | |||
| d383557960 | |||
| 67a99dbee8 | |||
| 8b0e5c3c7e | |||
| d631c6af80 | |||
| 902e2602c2 | |||
| d59a388b61 |
+33
@@ -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)
|
||||
|
||||
@@ -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
|
||||
@@ -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>
|
||||
@@ -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
|
||||
@@ -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();
|
||||
};
|
||||
@@ -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, "&");
|
||||
//text = text.replace(/</g, "<");
|
||||
//text = text.replace(/>/g, ">");
|
||||
//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);
|
||||
};
|
||||
};
|
||||
})();
|
||||
@@ -1,9 +1,9 @@
|
||||
C Zap\sstray\s--help\soutput\sfrom\sintermediate\sversion.
|
||||
D 2022-05-11T20:29:32.525
|
||||
C When\sbuilding\sfiddle\sfrom\sits\sown\sdirectory\s(e.g.\sfrom\semacs\sinstead\sof\sthe\snormal\sbuild\sprocess),\sforce\s-O0\sthe\ssake\sof\scompilation\sspeed.\sThe\stop-level\sbuild\snow\sdefaults\sto\s-Oz,\swhich\sis\smore\sappropriate\sfor\sthat\scase.
|
||||
D 2022-05-19T16:49:15.840
|
||||
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
|
||||
@@ -504,7 +509,7 @@ F src/date.c 15082566229d4b1e5f24fdb490bf9bcc68824b911d70e3573ef075a1b9e2d26f
|
||||
F src/dbpage.c 90661a87e1db8bfbc8d2ebbdcd3749651ddb287c555c07a28fb17c7c591ffb68
|
||||
F src/dbstat.c 861e08690fcb0f2ee1165eff0060ea8d4f3e2ea10f80dab7d32ad70443a6ff2d
|
||||
F src/delete.c a8e844af211a48b13b5b358be77a12c860c6a557c21990ad51a548e2536500ce
|
||||
F src/expr.c d955e8954e03e637c19033241806b04f6a937d0657f6261f2d9e4cdfe674b478
|
||||
F src/expr.c 19507ae3244402860cac2944be3b92bf9a8b50212fbfabaf7e9817127fec7c00
|
||||
F src/fault.c 460f3e55994363812d9d60844b2a6de88826e007
|
||||
F src/fkey.c d965ede15d8360c09ed59348940649ee647b192e784466837d7aefa836d1d91e
|
||||
F src/func.c a3407a6fbb0d4088d8d502e46f0ace63e0aeae7467ae23a9ca9815bbf9239761
|
||||
@@ -516,8 +521,8 @@ F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71
|
||||
F src/insert.c 173845e5a6bac96ae937409e4f876b631f26b31dabb9df8fd0eb3b130b2bb3a7
|
||||
F src/json.c 7749b98c62f691697c7ee536b570c744c0583cab4a89200fdd0fc2aa8cc8cbd6
|
||||
F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa
|
||||
F src/loadext.c 0705c2747212934183398f09891415d2f7f3113d0f543ccb205640210b20e617
|
||||
F src/main.c 135858d2ede0b83d779e71b07ede9c1d6b6eaab7b77bc2a85729584152769faf
|
||||
F src/loadext.c 853385cc7a604157e137585097949252d5d0c731768e16b044608e5c95c3614b
|
||||
F src/main.c e946f1d9e30bf49cf399ddf860cfd2319e3a97bbaabfb8e87d6ab985a20d54ad
|
||||
F src/malloc.c a9127efdcef92d6934c6339ea9813075b90edc0ce2e5c723556381a3828fb720
|
||||
F src/mem0.c 6a55ebe57c46ca1a7d98da93aaa07f99f1059645
|
||||
F src/mem1.c c12a42539b1ba105e3707d0e628ad70e611040d8f5e38cf942cee30c867083de
|
||||
@@ -537,28 +542,28 @@ F src/os.c b1c4f2d485961e9a5b6b648c36687d25047c252222e9660b7cc25a6e1ea436ab
|
||||
F src/os.h 1ff5ae51d339d0e30d8a9d814f4b8f8e448169304d83a7ed9db66a65732f3e63
|
||||
F src/os_common.h b2f4707a603e36811d9b1a13278bffd757857b85
|
||||
F src/os_setup.h 0dbaea40a7d36bf311613d31342e0b99e2536586
|
||||
F src/os_unix.c 02becb87c58245d2831f10f4f1a9108a465f8091cc598b2a9c78976f51a4e446
|
||||
F src/os_unix.c 2df2b33db88f00af13805d4573ee126bc5973f9e3b91d03c575fa7ba64e7dc41
|
||||
F src/os_win.c a8ea80037e81127ca01959daa87387cc135f325c88dc745376c4f760de852a10
|
||||
F src/os_win.h 7b073010f1451abe501be30d12f6bc599824944a
|
||||
F src/pager.c 42120492784fc9bcd9082b5c9b5e329b7318c357f9f3574a1bbfcf7418910356
|
||||
F src/pager.h f82e9844166e1585f5786837ddc7709966138ced17f568c16af7ccf946c2baa3
|
||||
F src/parse.y b86d56b446afb9c203d8354dc6c422818a62b4bbab52b76ab3da06d7b1d07e44
|
||||
F src/parse.y ad9a59d0078267e5aea4cf62f244c0ce6fcec9a91edd365e929c75d3a1716563
|
||||
F src/pcache.c 084e638432c610f95aea72b8509f0845d2791293f39d1b82f0c0a7e089c3bb6b
|
||||
F src/pcache.h 4f87acd914cef5016fae3030343540d75f5b85a1877eed1a2a19b9f284248586
|
||||
F src/pcache1.c 54881292a9a5db202b2c0ac541c5e3ef9a5e8c4f1c1383adb2601d5499a60e65
|
||||
F src/pragma.c d1aead03e8418ff586c7cfca344c50a914b8eb06abd841e8e91a982d823671da
|
||||
F src/pragma.h e690a356c18e98414d2e870ea791c1be1545a714ba623719deb63f7f226d8bb7
|
||||
F src/prepare.c c62820c15dcb63013519c8e41d9f928d7478672cc902cfd0581c733c271dbf45
|
||||
F src/printf.c 512574910a45341c8ad244bd3d4939968ebdfde215645b676fff01cc46e90757
|
||||
F src/printf.c 6166a30417b05c5b2f82e1f183f75faa2926ad60531c0b688a57dbc951441a20
|
||||
F src/random.c 097dc8b31b8fba5a9aca1697aeb9fd82078ec91be734c16bffda620ced7ab83c
|
||||
F src/resolve.c e9ee235c4151d2b7fa47435a219bfd30bf516a804d2f004639858087ebf3137b
|
||||
F src/resolve.c a4eb3c617027fd049b07432f3b942ea7151fa793a332a11a7d0f58c9539e104f
|
||||
F src/rowset.c ba9515a922af32abe1f7d39406b9d35730ed65efab9443dc5702693b60854c92
|
||||
F src/select.c 5096a2e8ab0511a413e7f5e45453fea4102d99c5636c46792581ae67899a76d7
|
||||
F src/shell.c.in afaed94af09f12ec99b4b9685e20273ad5bb5cdfcb6e2600cfb50f5a1ffe8e63
|
||||
F src/sqlite.h.in 2a35f62185eb5e7ecc64a2f68442b538ce9be74f80f28a00abc24837edcf1c17
|
||||
F src/select.c 74060a09f66c0c056f3c61627e22cb484af0bbfa29d7d14dcf17c684742c15de
|
||||
F src/shell.c.in cc3e19b2d2eefbadc4139b016c097d6478eae01d14eca993368ee5cff8820fff
|
||||
F src/sqlite.h.in d15c307939039086adca159dd340a94b79b69827e74c6d661f343eeeaefba896
|
||||
F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
|
||||
F src/sqlite3ext.h f49e28c25bd941e79794db5415fdf7b202deb3bc072ed6f1ed273d578703684e
|
||||
F src/sqliteInt.h 7c8146fc57f2e009152eb93ad41eab28490f9c31691559f281a80f9077ec1f2a
|
||||
F src/sqlite3ext.h a988810c9b21c0dc36dc7a62735012339dc76fc7ab448fb0792721d30eacb69d
|
||||
F src/sqliteInt.h fbe2d12cc13e80ac88c69ecde5ef9f052025fba78aa10f3eed0c71a5302e9a76
|
||||
F src/sqliteLimit.h d7323ffea5208c6af2734574bae933ca8ed2ab728083caa117c9738581a31657
|
||||
F src/status.c 4a3da6d77eeb3531cb0dbdf7047772a2a1b99f98c69e90ce009c75fe6328b2c0
|
||||
F src/table.c 0f141b58a16de7e2fbe81c308379e7279f4c6b50eb08efeec5892794a0ba30d1
|
||||
@@ -566,7 +571,7 @@ F src/tclsqlite.c 1f6673991147bc2cecc08a40d22f9803b84c805b24b499fe727f392256f734
|
||||
F src/test1.c 1356984e97bff07e4a8cc3863e892f05b3348678a74783bb6f350b76316736f1
|
||||
F src/test2.c 3efb99ab7f1fc8d154933e02ae1378bac9637da5
|
||||
F src/test3.c 61798bb0d38b915067a8c8e03f5a534b431181f802659a6616f9b4ff7d872644
|
||||
F src/test4.c 7c4420e01c577b5c4add2cb03119743b1a357543d347773b9e717195ea967159
|
||||
F src/test4.c 4533b76419e7feb41b40582554663ed3cd77aaa54e135cf76b3205098cd6e664
|
||||
F src/test5.c 328aae2c010c57a9829d255dc099d6899311672d
|
||||
F src/test6.c ae73a3a42bbc982fb9e301b84d30bda65a307be48c6dff20aba1461e17a9b0ce
|
||||
F src/test7.c 5612e9aecf934d6df7bba6ce861fdf5ba5456010
|
||||
@@ -618,7 +623,7 @@ F src/test_window.c cdae419fdcea5bad6dcd9368c685abdad6deb59e9fc8b84b153de513d394
|
||||
F src/test_wsd.c 41cadfd9d97fe8e3e4e44f61a4a8ccd6f7ca8fe9
|
||||
F src/threads.c 4ae07fa022a3dc7c5beb373cf744a85d3c5c6c3c
|
||||
F src/tokenize.c a38f52058b517929e264094abd0b5fd1e8e145a1aa43bc6f6a72ae5218f96c98
|
||||
F src/treeview.c 4153f2f044d13a3216e64271dfd1e59a3fa40222a45e6da248708088634e6f06
|
||||
F src/treeview.c 73facf395c8841653b9a54e789d8c80e15bc3d0d1cb9d16104c2d889c15e33cd
|
||||
F src/trigger.c 4fe4c1ac811755aff49d669d2e52e414eb5dfa6e172e849ab7b6825e70a571c0
|
||||
F src/update.c 2cfaded82ca80ff56afb8c3ae5e88284e0824bfd86119827cc22481959f96f92
|
||||
F src/upsert.c 8789047a8f0a601ea42fa0256d1ba3190c13746b6ba940fe2d25643a7e991937
|
||||
@@ -629,7 +634,7 @@ F src/vdbe.c a6a20956fa128c0b8d3c5592a9a3db890a46cf626f46ffee0cab2ac5bf8d04b2
|
||||
F src/vdbe.h 07641758ca8b4f4c6d81ea667ea167c541e6ece21f5574da11e3d21ec37e2662
|
||||
F src/vdbeInt.h ef43f7fdc5fde29fc3fd29c506c12830f366178fdb4edbbf0cbc3dfbd1278b5f
|
||||
F src/vdbeapi.c 354c893f1500cf524cc45c32879b9c68893a28b77e3442c24668d6afe4236217
|
||||
F src/vdbeaux.c f406d8d8b461f260aeaa69d265c5d175ff0a9f84d6153c87975cdfbf9d681922
|
||||
F src/vdbeaux.c 75c4f75ed7e1d12eb3d80093a160ec998c839f3008a1c3c967fc5acf522d0e3c
|
||||
F src/vdbeblob.c 5e61ce31aca17db8fb60395407457a8c1c7fb471dde405e0cd675974611dcfcd
|
||||
F src/vdbemem.c 7189090b72baa025f945a1ac8c61ee420c645254476e8a191d555db76dfea5d4
|
||||
F src/vdbesort.c 43756031ca7430f7aec3ef904824a7883c4ede783e51f280d99b9b65c0796e35
|
||||
@@ -640,10 +645,10 @@ F src/vxworks.h d2988f4e5a61a4dfe82c6524dd3d6e4f2ce3cdb9
|
||||
F src/wal.c b9df133a705093da8977da5eb202eaadb844839f1c7297c08d33471f5491843d
|
||||
F src/wal.h c3aa7825bfa2fe0d85bef2db94655f99870a285778baa36307c0a16da32b226a
|
||||
F src/walker.c f890a3298418d7cba3b69b8803594fdc484ea241206a8dfa99db6dd36f8cbb3b
|
||||
F src/where.c aa585b89bd65a81e44bdfb871b55f65bf8fda88e1bc85efda6c236fe8d2bd788
|
||||
F src/where.c 2ee27fed4a5564cddfc6710a539037d18e39e75cdf90b3f48d476c3b9767592e
|
||||
F src/whereInt.h 8da918f392bf202ccc0ee61291455b33ad171d209445f1ff3eaf62e0b6f6b363
|
||||
F src/wherecode.c 72f8eeed5527450c8e2258160a7bd04534a76c161230d100da0f43a86c6e29ac
|
||||
F src/whereexpr.c e036477ac8424de50ae5b36a71103405d3f86b33ba11125ec7a2a99d501b0622
|
||||
F src/wherecode.c 2a8a73bcf1886632f2b2247c79395f94852a4b74484d8aa70a005892ce73d339
|
||||
F src/whereexpr.c efed370c684dce04eab949202c5452bbde993efb198de43c7a88f59411ad2a2c
|
||||
F src/window.c fff1b51757438c664e471d5184634e48dcdf8ea34b640f3b1b0810b1e06de18c
|
||||
F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2
|
||||
F test/affinity2.test ce1aafc86e110685b324e9a763eab4f2a73f737842ec3b687bd965867de90627
|
||||
@@ -901,7 +906,7 @@ F test/exclusive.test 7ff63be7503990921838d5c9f77f6e33e68e48ed1a9d48cd28745bf650
|
||||
F test/exclusive2.test 984090e8e9d1b331d2e8111daf6e5d61dda0bef7
|
||||
F test/exec.test e949714dc127eaa5ecc7d723efec1ec27118fdd7
|
||||
F test/exists.test 79a75323c78f02bbe9c251ea502a092f9ef63dac
|
||||
F test/expr.test e1afcdb1038e4d3fa67a3df323347c38750946e2e1b4e385bdc75d26284f2dac
|
||||
F test/expr.test 5c06696478212e5a04e04b043f993373f6f8e5ce5a80f5548a84703b123b6caa
|
||||
F test/expr2.test c27327ae9c017a7ff6280123f67aff496f912da74d78c888926d68b46ec75fd8
|
||||
F test/exprfault.test 497cc0b8fe6a677f49b55cb485e040f709ec2834b84f25912fe9c2dfeeda33db
|
||||
F test/extension01.test 00d13cec817f331a687a243e0e5a2d87b0e358c9
|
||||
@@ -1089,7 +1094,7 @@ F test/hook2.test b9ff3b8c6519fb67f33192f1afe86e7782ee4ac8
|
||||
F test/icu.test 716a6b89fbabe5cc63e0cd4c260befb08fd7b9d761f04d43669233292f0753b1
|
||||
F test/ieee754.test b0945d12be7d255f3dfa18e2511b17ca37e0edd2b803231c52d05b86c04ab26e
|
||||
F test/imposter1.test c3f1db2d3db2c24611a6596a3fc0ffc14f1466c8
|
||||
F test/in.test 15de58ee017f43d36390812e9a51217d1b2db7758f97d0df48296ef178ea560b
|
||||
F test/in.test 55503d3633c434120d8b2d5966a34f0d9a55393a589050b1366afe4b188093c7
|
||||
F test/in2.test 5d4c61d17493c832f7d2d32bef785119e87bde75
|
||||
F test/in3.test 3cbf58c87f4052cee3a58b37b6389777505aa0c0
|
||||
F test/in4.test fdd1d8134da8376985c2edba6035a2de1f6c731524d2ffa651419e8fe2cd1c5a
|
||||
@@ -1148,12 +1153,13 @@ F test/join4.test 1a352e4e267114444c29266ce79e941af5885916
|
||||
F test/join5.test d22b6cba8fb59ab3f1c82701434c360705eb12d4ce200c449f37b018fc47681a
|
||||
F test/join6.test f809c025fa253f9e150c0e9afd4cef8813257bceeb6f46e04041228c9403cc2c
|
||||
F test/join7.test 8e72de4b45e5e930d18c305c7efe86015fb2552731e4e03ea226353036b0dab0
|
||||
F test/join8.test fef259c42d56bbe150b777726b185b0bc060d4290daf9f879abc68321f8ad6db
|
||||
F test/join8.test adf30584c6d0cc61816b24fd6f3cc8386ce89f579da4b65a6c8e05c2f2dce08e
|
||||
F test/join9.test 9056ddd3b0c0f4f9d658f4521038d9a37dc23ead8ca9a505d0b0db2b6a471e05
|
||||
F test/joinA.test 7eab225dc1c1ab258a5e62513a4ed7cabbd3db971d59d5d92f4fb6fa14c12f6a
|
||||
F test/joinB.test 1b2ba3fc8568b49411787fccbf540570c148e9b6a53a30f80691cb6268098ded
|
||||
F test/joinC.test 1f1a602c2127f55f136e2cbd3bf2d26546614bf8cffe5902ec1ac9c07f87f207
|
||||
F test/joinD.test 7f0f4dd1f2767330bf1fda5c9cc8a437015a54bcd2355036b4d04ddfc1519d76
|
||||
F test/joinE.test d5d182f3812771e2c0d97c9dcf5dbe4c41c8e21c82560e59358731c4a3981d6b
|
||||
F test/journal1.test c7b768041b7f494471531e17abc2f4f5ebf9e5096984f43ed17c4eb80ba34497
|
||||
F test/journal2.test 9dac6b4ba0ca79c3b21446bbae993a462c2397c4
|
||||
F test/journal3.test 7c3cf23ffc77db06601c1fcfc9743de8441cb77db9d1aa931863d94f5ffa140e
|
||||
@@ -1462,7 +1468,7 @@ F test/subtype1.test 7fe09496352f97053af1437150751be2d0a0cae8
|
||||
F test/superlock.test ec94f0556b6488d97f71c79f9061ae08d9ab8f12
|
||||
F test/swarmvtab.test 250231404fcac88f61a6c147bb0e3a118ed879278cd3ccb0ae2d3a729e1e8e26
|
||||
F test/swarmvtab2.test c948cb2fdfc5b01d85e8f6d6504854202dc1a0782ab2a0ed61538f27cbd0aa5c
|
||||
F test/swarmvtab3.test 247aa38b6ebd2b99db2075847ae47e789ac34f1c2ab5c720dfcffd990004c544
|
||||
F test/swarmvtab3.test 41a3ab47cb7a834d4e5336425103b617410a67bb95d335ef536f887587ece073
|
||||
F test/swarmvtabfault.test 8a67a9f27c61073a47990829e92bc0c64420a807cb642b15a25f6c788210ed95
|
||||
F test/symlink.test 72b22238d4405ba34df8e60b335d290a3b1129fd5c260835c944c1e4e77288a9
|
||||
F test/symlink2.test 9531f475a53d8781c4f81373f87faf2e2aff4f5fb2102ec6386e0c827916a670
|
||||
@@ -1953,8 +1959,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 c1eff632c41809214edea2850a93852fff66da3ca0dc393e8fe55e0976d422fd
|
||||
R 56f20e29d8d8bd4f94a878cd8e451873
|
||||
U larrybr
|
||||
Z ec90e5296e8a585e62fc3cef12515b0b
|
||||
P d46a6cb97406d90b9ad1da66369454e9b8f387ffbebcdf1c386c3c21f790c16f
|
||||
R f9801562283af03ef889fbffa87ba13d
|
||||
U stephan
|
||||
Z edd8c70d727c3da0dc3ea944cb532c03
|
||||
# Remove this line to create a well-formed Fossil manifest.
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
deb7372b18cc8fb9d305085498fd24b3c2c17bd920ae2d03fa885af02ad47008
|
||||
56b82ae806c61b95e62042ca70ed952ce01832b02da55c2b315f9201989514ab
|
||||
+12
-16
@@ -1069,6 +1069,7 @@ Expr *sqlite3ExprFunction(
|
||||
sqlite3ExprListDelete(db, pList); /* Avoid memory leak when malloc fails */
|
||||
return 0;
|
||||
}
|
||||
assert( !ExprHasProperty(pNew, EP_InnerON|EP_OuterON) );
|
||||
pNew->w.iOfst = (int)(pToken->z - pParse->zTail);
|
||||
if( pList
|
||||
&& pList->nExpr > pParse->db->aLimit[SQLITE_LIMIT_FUNCTION_ARG]
|
||||
@@ -1347,7 +1348,7 @@ static int dupedExprStructSize(const Expr *p, int flags){
|
||||
nSize = EXPR_FULLSIZE;
|
||||
}else{
|
||||
assert( !ExprHasProperty(p, EP_TokenOnly|EP_Reduced) );
|
||||
assert( !ExprHasProperty(p, EP_FromJoin) );
|
||||
assert( !ExprHasProperty(p, EP_OuterON) );
|
||||
assert( !ExprHasProperty(p, EP_MemToken) );
|
||||
assert( !ExprHasVVAProperty(p, EP_NoReduce) );
|
||||
if( p->pLeft || p->x.pList ){
|
||||
@@ -2173,7 +2174,7 @@ static int exprNodeIsConstant(Walker *pWalker, Expr *pExpr){
|
||||
/* If pWalker->eCode is 2 then any term of the expression that comes from
|
||||
** the ON or USING clauses of an outer join disqualifies the expression
|
||||
** from being considered constant. */
|
||||
if( pWalker->eCode==2 && ExprHasProperty(pExpr, EP_FromJoin) ){
|
||||
if( pWalker->eCode==2 && ExprHasProperty(pExpr, EP_OuterON) ){
|
||||
pWalker->eCode = 0;
|
||||
return WRC_Abort;
|
||||
}
|
||||
@@ -2320,10 +2321,10 @@ int sqlite3ExprIsTableConstraint(Expr *pExpr, const SrcItem *pSrc){
|
||||
return 0; /* rule (3) */
|
||||
}
|
||||
if( pSrc->fg.jointype & JT_LEFT ){
|
||||
if( !ExprHasProperty(pExpr, EP_FromJoin) ) return 0; /* rule (4a) */
|
||||
if( !ExprHasProperty(pExpr, EP_OuterON) ) return 0; /* rule (4a) */
|
||||
if( pExpr->w.iJoin!=pSrc->iCursor ) return 0; /* rule (4b) */
|
||||
}else{
|
||||
if( ExprHasProperty(pExpr, EP_FromJoin) ) return 0; /* rule (5) */
|
||||
if( ExprHasProperty(pExpr, EP_OuterON) ) return 0; /* rule (5) */
|
||||
}
|
||||
return sqlite3ExprIsTableConstant(pExpr, pSrc->iCursor); /* rules (1), (2) */
|
||||
}
|
||||
@@ -2745,11 +2746,7 @@ int sqlite3FindInIndex(
|
||||
|
||||
assert( pX->op==TK_IN );
|
||||
mustBeUnique = (inFlags & IN_INDEX_LOOP)!=0;
|
||||
if( pX->iTable && (inFlags & IN_INDEX_REUSE_CUR)!=0 ){
|
||||
iTab = pX->iTable;
|
||||
}else{
|
||||
iTab = pParse->nTab++;
|
||||
}
|
||||
iTab = pParse->nTab++;
|
||||
|
||||
/* If the RHS of this IN(...) operator is a SELECT, and if it matters
|
||||
** whether or not the SELECT result contains NULL values, check whether
|
||||
@@ -3083,9 +3080,8 @@ void sqlite3CodeRhsOfIN(
|
||||
assert( ExprUseYSub(pExpr) );
|
||||
sqlite3VdbeAddOp2(v, OP_Gosub, pExpr->y.sub.regReturn,
|
||||
pExpr->y.sub.iAddr);
|
||||
if( iTab!=pExpr->iTable ){
|
||||
sqlite3VdbeAddOp2(v, OP_OpenDup, iTab, pExpr->iTable);
|
||||
}
|
||||
assert( iTab!=pExpr->iTable );
|
||||
sqlite3VdbeAddOp2(v, OP_OpenDup, iTab, pExpr->iTable);
|
||||
sqlite3VdbeJumpHere(v, addrOnce);
|
||||
return;
|
||||
}
|
||||
@@ -5078,8 +5074,8 @@ static void exprCodeBetween(
|
||||
** so that the sqlite3ExprCodeTarget() routine will not attempt to move
|
||||
** it into the Parse.pConstExpr list. We should use a new bit for this,
|
||||
** for clarity, but we are out of bits in the Expr.flags field so we
|
||||
** have to reuse the EP_FromJoin bit. Bummer. */
|
||||
pDel->flags |= EP_FromJoin;
|
||||
** have to reuse the EP_OuterON bit. Bummer. */
|
||||
pDel->flags |= EP_OuterON;
|
||||
sqlite3ExprCodeTarget(pParse, &exprAnd, dest);
|
||||
}
|
||||
sqlite3ReleaseTempReg(pParse, regFree1);
|
||||
@@ -5764,7 +5760,7 @@ int sqlite3ExprImpliesExpr(
|
||||
static int impliesNotNullRow(Walker *pWalker, Expr *pExpr){
|
||||
testcase( pExpr->op==TK_AGG_COLUMN );
|
||||
testcase( pExpr->op==TK_AGG_FUNCTION );
|
||||
if( ExprHasProperty(pExpr, EP_FromJoin) ) return WRC_Prune;
|
||||
if( ExprHasProperty(pExpr, EP_OuterON) ) return WRC_Prune;
|
||||
switch( pExpr->op ){
|
||||
case TK_ISNOT:
|
||||
case TK_ISNULL:
|
||||
@@ -5861,7 +5857,7 @@ static int impliesNotNullRow(Walker *pWalker, Expr *pExpr){
|
||||
** False positives are not allowed, however. A false positive may result
|
||||
** in an incorrect answer.
|
||||
**
|
||||
** Terms of p that are marked with EP_FromJoin (and hence that come from
|
||||
** Terms of p that are marked with EP_OuterON (and hence that come from
|
||||
** the ON or USING clauses of OUTER JOINS) are excluded from the analysis.
|
||||
**
|
||||
** This routine is used to check if a LEFT JOIN can be converted into
|
||||
|
||||
+3
-2
@@ -503,11 +503,12 @@ static const sqlite3_api_routines sqlite3Apis = {
|
||||
/* Version 3.39.0 and later */
|
||||
#ifndef SQLITE_OMIT_DESERIALIZE
|
||||
sqlite3_deserialize,
|
||||
sqlite3_serialize
|
||||
sqlite3_serialize,
|
||||
#else
|
||||
0,
|
||||
0
|
||||
0,
|
||||
#endif
|
||||
sqlite3_db_name
|
||||
};
|
||||
|
||||
/* True if x is the directory separator character
|
||||
|
||||
+18
@@ -4626,6 +4626,24 @@ Btree *sqlite3DbNameToBtree(sqlite3 *db, const char *zDbName){
|
||||
return iDb<0 ? 0 : db->aDb[iDb].pBt;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return the name of the N-th database schema. Return NULL if N is out
|
||||
** of range.
|
||||
*/
|
||||
const char *sqlite3_db_name(sqlite3 *db, int N){
|
||||
#ifdef SQLITE_ENABLE_API_ARMOR
|
||||
if( !sqlite3SafetyCheckOk(db) ){
|
||||
(void)SQLITE_MISUSE_BKPT;
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
if( N<0 || N>=db->nDb ){
|
||||
return 0;
|
||||
}else{
|
||||
return db->aDb[N].zDbSName;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Return the filename of the database associated with a database
|
||||
** connection.
|
||||
|
||||
+2
-1
@@ -6484,7 +6484,7 @@ static void appendOnePathElement(
|
||||
return;
|
||||
}
|
||||
got = osReadlink(zIn, zLnk, sizeof(zLnk)-2);
|
||||
if( got<=0 || got>=sizeof(zLnk)-2 ){
|
||||
if( got<=0 || got>=(ssize_t)sizeof(zLnk)-2 ){
|
||||
pPath->rc = unixLogError(SQLITE_CANTOPEN_BKPT, "readlink", zIn);
|
||||
return;
|
||||
}
|
||||
@@ -6534,6 +6534,7 @@ static int unixFullPathname(
|
||||
char *zOut /* Output buffer */
|
||||
){
|
||||
DbPath path;
|
||||
UNUSED_PARAMETER(pVfs);
|
||||
path.rc = 0;
|
||||
path.nUsed = 0;
|
||||
path.nSymlink = 0;
|
||||
|
||||
+10
-1
@@ -1218,6 +1218,14 @@ expr(A) ::= expr(A) IS NOT expr(Y). {
|
||||
A = sqlite3PExpr(pParse,TK_ISNOT,A,Y);
|
||||
binaryToUnaryIfNull(pParse, Y, A, TK_NOTNULL);
|
||||
}
|
||||
expr(A) ::= expr(A) IS NOT DISTINCT FROM expr(Y). {
|
||||
A = sqlite3PExpr(pParse,TK_IS,A,Y);
|
||||
binaryToUnaryIfNull(pParse, Y, A, TK_ISNULL);
|
||||
}
|
||||
expr(A) ::= expr(A) IS DISTINCT FROM expr(Y). {
|
||||
A = sqlite3PExpr(pParse,TK_ISNOT,A,Y);
|
||||
binaryToUnaryIfNull(pParse, Y, A, TK_NOTNULL);
|
||||
}
|
||||
|
||||
expr(A) ::= NOT(B) expr(X).
|
||||
{A = sqlite3PExpr(pParse, @B, X, 0);/*A-overwrites-B*/}
|
||||
@@ -1263,7 +1271,8 @@ expr(A) ::= expr(A) between_op(N) expr(X) AND expr(Y). [BETWEEN] {
|
||||
** regardless of the value of expr1.
|
||||
*/
|
||||
sqlite3ExprUnmapAndDelete(pParse, A);
|
||||
A = sqlite3Expr(pParse->db, TK_INTEGER, N ? "1" : "0");
|
||||
A = sqlite3Expr(pParse->db, TK_STRING, N ? "true" : "false");
|
||||
if( A ) sqlite3ExprIdToTrueFalse(A);
|
||||
}else{
|
||||
Expr *pRHS = Y->a[0].pExpr;
|
||||
if( Y->nExpr==1 && sqlite3ExprIsConstant(pRHS) && A->op!=TK_VECTOR ){
|
||||
|
||||
+3
-1
@@ -954,7 +954,9 @@ void sqlite3RecordErrorByteOffset(sqlite3 *db, const char *z){
|
||||
** as the error offset.
|
||||
*/
|
||||
void sqlite3RecordErrorOffsetOfExpr(sqlite3 *db, const Expr *pExpr){
|
||||
while( pExpr && (ExprHasProperty(pExpr,EP_FromJoin) || pExpr->w.iOfst<=0) ){
|
||||
while( pExpr
|
||||
&& (ExprHasProperty(pExpr,EP_OuterON|EP_InnerON) || pExpr->w.iOfst<=0)
|
||||
){
|
||||
pExpr = pExpr->pLeft;
|
||||
}
|
||||
if( pExpr==0 ) return;
|
||||
|
||||
+1
-1
@@ -928,7 +928,7 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
|
||||
}
|
||||
sqlite3WalkExpr(pWalker, pExpr->pLeft);
|
||||
if( 0==sqlite3ExprCanBeNull(pExpr->pLeft) && !IN_RENAME_OBJECT ){
|
||||
testcase( ExprHasProperty(pExpr, EP_FromJoin) );
|
||||
testcase( ExprHasProperty(pExpr, EP_OuterON) );
|
||||
assert( !ExprHasProperty(pExpr, EP_IntValue) );
|
||||
if( pExpr->op==TK_NOTNULL ){
|
||||
pExpr->u.zToken = "true";
|
||||
|
||||
+21
-21
@@ -378,11 +378,11 @@ static int tableAndColumnIndex(
|
||||
}
|
||||
|
||||
/*
|
||||
** Set the EP_FromJoin property on all terms of the given expression.
|
||||
** Set the EP_OuterON property on all terms of the given expression.
|
||||
** And set the Expr.w.iJoin to iTable for every term in the
|
||||
** expression.
|
||||
**
|
||||
** The EP_FromJoin property is used on terms of an expression to tell
|
||||
** The EP_OuterON property is used on terms of an expression to tell
|
||||
** the OUTER JOIN processing logic that this term is part of the
|
||||
** join restriction specified in the ON or USING clause and not a part
|
||||
** of the more general WHERE clause. These terms are moved over to the
|
||||
@@ -404,7 +404,7 @@ static int tableAndColumnIndex(
|
||||
** the output, which is incorrect.
|
||||
*/
|
||||
void sqlite3SetJoinExpr(Expr *p, int iTable, u32 joinFlag){
|
||||
assert( joinFlag==EP_FromJoin || joinFlag==EP_InnerJoin );
|
||||
assert( joinFlag==EP_OuterON || joinFlag==EP_InnerON );
|
||||
while( p ){
|
||||
ExprSetProperty(p, joinFlag);
|
||||
assert( !ExprHasProperty(p, EP_TokenOnly|EP_Reduced) );
|
||||
@@ -425,17 +425,17 @@ void sqlite3SetJoinExpr(Expr *p, int iTable, u32 joinFlag){
|
||||
}
|
||||
|
||||
/* Undo the work of sqlite3SetJoinExpr(). In the expression p, convert every
|
||||
** term that is marked with EP_FromJoin and w.iJoin==iTable into
|
||||
** an ordinary term that omits the EP_FromJoin mark.
|
||||
** term that is marked with EP_OuterON and w.iJoin==iTable into
|
||||
** an ordinary term that omits the EP_OuterON mark.
|
||||
**
|
||||
** This happens when a LEFT JOIN is simplified into an ordinary JOIN.
|
||||
*/
|
||||
static void unsetJoinExpr(Expr *p, int iTable){
|
||||
while( p ){
|
||||
if( ExprHasProperty(p, EP_FromJoin)
|
||||
if( ExprHasProperty(p, EP_OuterON)
|
||||
&& (iTable<0 || p->w.iJoin==iTable) ){
|
||||
ExprClearProperty(p, EP_FromJoin);
|
||||
ExprSetProperty(p, EP_InnerJoin);
|
||||
ExprClearProperty(p, EP_OuterON);
|
||||
ExprSetProperty(p, EP_InnerON);
|
||||
}
|
||||
if( p->op==TK_COLUMN && p->iTable==iTable ){
|
||||
ExprClearProperty(p, EP_CanBeNull);
|
||||
@@ -463,8 +463,8 @@ static void unsetJoinExpr(Expr *p, int iTable){
|
||||
**
|
||||
** * ON and USING clauses result in extra terms being added to the
|
||||
** WHERE clause to enforce the specified constraints. The extra
|
||||
** WHERE clause terms will be tagged with EP_FromJoin or
|
||||
** EP_InnerJoin so that we know that they originated in ON/USING.
|
||||
** WHERE clause terms will be tagged with EP_OuterON or
|
||||
** EP_InnerON so that we know that they originated in ON/USING.
|
||||
**
|
||||
** The terms of a FROM clause are contained in the Select.pSrc structure.
|
||||
** The left most table is the first entry in Select.pSrc. The right-most
|
||||
@@ -489,7 +489,7 @@ static int sqlite3ProcessJoin(Parse *pParse, Select *p){
|
||||
u32 joinType;
|
||||
|
||||
if( NEVER(pLeft->pTab==0 || pRightTab==0) ) continue;
|
||||
joinType = (pRight->fg.jointype & JT_OUTER)!=0 ? EP_FromJoin : EP_InnerJoin;
|
||||
joinType = (pRight->fg.jointype & JT_OUTER)!=0 ? EP_OuterON : EP_InnerON;
|
||||
|
||||
/* If this is a NATURAL join, synthesize an approprate USING clause
|
||||
** to specify which columns should be joined.
|
||||
@@ -3762,7 +3762,7 @@ static Expr *substExpr(
|
||||
Expr *pExpr /* Expr in which substitution occurs */
|
||||
){
|
||||
if( pExpr==0 ) return 0;
|
||||
if( ExprHasProperty(pExpr, EP_FromJoin)
|
||||
if( ExprHasProperty(pExpr, EP_OuterON)
|
||||
&& pExpr->w.iJoin==pSubst->iTable
|
||||
){
|
||||
pExpr->w.iJoin = pSubst->iNewTable;
|
||||
@@ -3803,9 +3803,9 @@ static Expr *substExpr(
|
||||
if( pSubst->isOuterJoin ){
|
||||
ExprSetProperty(pNew, EP_CanBeNull);
|
||||
}
|
||||
if( ExprHasProperty(pExpr,EP_FromJoin|EP_InnerJoin) ){
|
||||
if( ExprHasProperty(pExpr,EP_OuterON|EP_InnerON) ){
|
||||
sqlite3SetJoinExpr(pNew, pExpr->w.iJoin,
|
||||
pExpr->flags & (EP_FromJoin|EP_InnerJoin));
|
||||
pExpr->flags & (EP_OuterON|EP_InnerON));
|
||||
}
|
||||
sqlite3ExprDelete(db, pExpr);
|
||||
pExpr = pNew;
|
||||
@@ -3969,7 +3969,7 @@ static int renumberCursorsCb(Walker *pWalker, Expr *pExpr){
|
||||
if( op==TK_COLUMN || op==TK_IF_NULL_ROW ){
|
||||
renumberCursorDoMapping(pWalker, &pExpr->iTable);
|
||||
}
|
||||
if( ExprHasProperty(pExpr, EP_FromJoin) ){
|
||||
if( ExprHasProperty(pExpr, EP_OuterON) ){
|
||||
renumberCursorDoMapping(pWalker, &pExpr->w.iJoin);
|
||||
}
|
||||
return WRC_Continue;
|
||||
@@ -4547,7 +4547,7 @@ static int flattenSubquery(
|
||||
pWhere = pSub->pWhere;
|
||||
pSub->pWhere = 0;
|
||||
if( isOuterJoin>0 ){
|
||||
sqlite3SetJoinExpr(pWhere, iNewParent, EP_FromJoin);
|
||||
sqlite3SetJoinExpr(pWhere, iNewParent, EP_OuterON);
|
||||
}
|
||||
if( pWhere ){
|
||||
if( pParent->pWhere ){
|
||||
@@ -4680,7 +4680,7 @@ static void constInsert(
|
||||
static void findConstInWhere(WhereConst *pConst, Expr *pExpr){
|
||||
Expr *pRight, *pLeft;
|
||||
if( NEVER(pExpr==0) ) return;
|
||||
if( ExprHasProperty(pExpr, EP_FromJoin) ) return;
|
||||
if( ExprHasProperty(pExpr, EP_OuterON) ) return;
|
||||
if( pExpr->op==TK_AND ){
|
||||
findConstInWhere(pConst, pExpr->pRight);
|
||||
findConstInWhere(pConst, pExpr->pLeft);
|
||||
@@ -4716,9 +4716,9 @@ static int propagateConstantExprRewriteOne(
|
||||
int i;
|
||||
if( pConst->pOomFault[0] ) return WRC_Prune;
|
||||
if( pExpr->op!=TK_COLUMN ) return WRC_Continue;
|
||||
if( ExprHasProperty(pExpr, EP_FixedCol|EP_FromJoin) ){
|
||||
if( ExprHasProperty(pExpr, EP_FixedCol|EP_OuterON) ){
|
||||
testcase( ExprHasProperty(pExpr, EP_FixedCol) );
|
||||
testcase( ExprHasProperty(pExpr, EP_FromJoin) );
|
||||
testcase( ExprHasProperty(pExpr, EP_OuterON) );
|
||||
return WRC_Continue;
|
||||
}
|
||||
for(i=0; i<pConst->nConst; i++){
|
||||
@@ -5003,12 +5003,12 @@ static int pushDownWhereTerms(
|
||||
|
||||
#if 0 /* Legacy code. Checks now done by sqlite3ExprIsTableConstraint() */
|
||||
if( isLeftJoin
|
||||
&& (ExprHasProperty(pWhere,EP_FromJoin)==0
|
||||
&& (ExprHasProperty(pWhere,EP_OuterON)==0
|
||||
|| pWhere->w.iJoin!=iCursor)
|
||||
){
|
||||
return 0; /* restriction (4) */
|
||||
}
|
||||
if( ExprHasProperty(pWhere,EP_FromJoin)
|
||||
if( ExprHasProperty(pWhere,EP_OuterON)
|
||||
&& pWhere->w.iJoin!=iCursor
|
||||
){
|
||||
return 0; /* restriction (5) */
|
||||
|
||||
+173
-18
@@ -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") */
|
||||
@@ -8962,7 +9012,6 @@ static int do_meta_command(char *zLine, ShellState *p){
|
||||
utf8_printf(stderr, "Error: cannot open \"%s\"\n", zFile);
|
||||
goto meta_command_exit;
|
||||
}
|
||||
rc = 0;
|
||||
if( eVerbose>=2 || (eVerbose>=1 && useOutputMode) ){
|
||||
char zSep[2];
|
||||
zSep[1] = 0;
|
||||
@@ -9127,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 ){
|
||||
@@ -9316,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;
|
||||
@@ -9338,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 ){
|
||||
@@ -9349,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;
|
||||
@@ -9584,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)
|
||||
@@ -9699,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);
|
||||
@@ -9868,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;
|
||||
@@ -9906,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;
|
||||
@@ -9958,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 ){
|
||||
@@ -10583,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)
|
||||
){
|
||||
@@ -10604,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"};
|
||||
@@ -10784,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);
|
||||
@@ -10797,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 ){
|
||||
@@ -11468,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
|
||||
@@ -11849,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
|
||||
@@ -11859,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;
|
||||
@@ -11875,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") ){
|
||||
@@ -12132,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
|
||||
@@ -12398,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 ){
|
||||
@@ -12430,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 */
|
||||
|
||||
@@ -6276,6 +6276,26 @@ int sqlite3_get_autocommit(sqlite3*);
|
||||
*/
|
||||
sqlite3 *sqlite3_db_handle(sqlite3_stmt*);
|
||||
|
||||
/*
|
||||
** CAPI3REF: Return The Schema Name For A Database Connection
|
||||
** METHOD: sqlite3
|
||||
**
|
||||
** ^The sqlite3_db_name(D,N) interface returns a pointer to the schema name
|
||||
** for the N-th database on database connection D, or a NULL pointer of N is
|
||||
** out of range.
|
||||
**
|
||||
** Space to hold the string that is returned by sqlite3_db_name() is managed
|
||||
** by SQLite itself. The string might be deallocated by any operation that
|
||||
** changes the schema, including [ATTACH] or [DETACH] or calls to
|
||||
** [sqlite3_serialize()] or [sqlite3_deserialize()], even operations that
|
||||
** occur on a different thread. Applications that need to
|
||||
** remember the string long-term should make their own copy. Applications that
|
||||
** are accessing the same database connection simultaneously on multiple
|
||||
** threads should mutex-protect calls to this API and should make their own
|
||||
** private copy of the result prior to releasing the mutex.
|
||||
*/
|
||||
const char *sqlite3_db_name(sqlite3 *db, int N);
|
||||
|
||||
/*
|
||||
** CAPI3REF: Return The Filename For A Database Connection
|
||||
** METHOD: sqlite3
|
||||
|
||||
@@ -356,6 +356,7 @@ struct sqlite3_api_routines {
|
||||
sqlite3_int64,sqlite3_int64,unsigned);
|
||||
unsigned char *(*serialize)(sqlite3*,const char *,sqlite3_int64*,
|
||||
unsigned int);
|
||||
const char *(*db_name)(sqlite3*,int);
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -674,10 +675,12 @@ typedef int (*sqlite3_loadext_entry)(
|
||||
#define sqlite3_vtab_in sqlite3_api->vtab_in
|
||||
#define sqlite3_vtab_in_first sqlite3_api->vtab_in_first
|
||||
#define sqlite3_vtab_in_next sqlite3_api->vtab_in_next
|
||||
/* Version 3.39.0 and later */
|
||||
#ifndef SQLITE_OMIT_DESERIALIZE
|
||||
#define sqlite3_deserialize sqlite3_api->deserialize
|
||||
#define sqlite3_serialize sqlite3_api->serialize
|
||||
#endif
|
||||
#define sqlite3_db_name sqlite3_api->db_name
|
||||
#endif /* !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) */
|
||||
|
||||
#if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION)
|
||||
|
||||
+24
-25
@@ -2847,7 +2847,7 @@ struct Expr {
|
||||
** TK_SELECT_COLUMN: column of the result vector */
|
||||
i16 iAgg; /* Which entry in pAggInfo->aCol[] or ->aFunc[] */
|
||||
union {
|
||||
int iJoin; /* If EP_FromJoin, the right table of the join */
|
||||
int iJoin; /* If EP_OuterON or EP_InnerON, the right table */
|
||||
int iOfst; /* else: start of token from start of statement */
|
||||
} w;
|
||||
AggInfo *pAggInfo; /* Used by TK_AGG_COLUMN and TK_AGG_FUNCTION */
|
||||
@@ -2868,29 +2868,29 @@ struct Expr {
|
||||
** EP_Agg == NC_HasAgg == SF_HasAgg
|
||||
** EP_Win == NC_HasWin
|
||||
*/
|
||||
#define EP_FromJoin 0x000001 /* Originates in ON/USING clause of outer join */
|
||||
#define EP_Distinct 0x000002 /* Aggregate function with DISTINCT keyword */
|
||||
#define EP_HasFunc 0x000004 /* Contains one or more functions of any kind */
|
||||
#define EP_FixedCol 0x000008 /* TK_Column with a known fixed value */
|
||||
#define EP_OuterON 0x000001 /* Originates in ON/USING clause of outer join */
|
||||
#define EP_InnerON 0x000002 /* Originates in ON/USING of an inner join */
|
||||
#define EP_Distinct 0x000004 /* Aggregate function with DISTINCT keyword */
|
||||
#define EP_HasFunc 0x000008 /* Contains one or more functions of any kind */
|
||||
#define EP_Agg 0x000010 /* Contains one or more aggregate functions */
|
||||
#define EP_VarSelect 0x000020 /* pSelect is correlated, not constant */
|
||||
#define EP_DblQuoted 0x000040 /* token.z was originally in "..." */
|
||||
#define EP_InfixFunc 0x000080 /* True for an infix function: LIKE, GLOB, etc */
|
||||
#define EP_Collate 0x000100 /* Tree contains a TK_COLLATE operator */
|
||||
#define EP_Commuted 0x000200 /* Comparison operator has been commuted */
|
||||
#define EP_IntValue 0x000400 /* Integer value contained in u.iValue */
|
||||
#define EP_xIsSelect 0x000800 /* x.pSelect is valid (otherwise x.pList is) */
|
||||
#define EP_Skip 0x001000 /* Operator does not contribute to affinity */
|
||||
#define EP_Reduced 0x002000 /* Expr struct EXPR_REDUCEDSIZE bytes only */
|
||||
#define EP_TokenOnly 0x004000 /* Expr struct EXPR_TOKENONLYSIZE bytes only */
|
||||
#define EP_FixedCol 0x000020 /* TK_Column with a known fixed value */
|
||||
#define EP_VarSelect 0x000040 /* pSelect is correlated, not constant */
|
||||
#define EP_DblQuoted 0x000080 /* token.z was originally in "..." */
|
||||
#define EP_InfixFunc 0x000100 /* True for an infix function: LIKE, GLOB, etc */
|
||||
#define EP_Collate 0x000200 /* Tree contains a TK_COLLATE operator */
|
||||
#define EP_Commuted 0x000400 /* Comparison operator has been commuted */
|
||||
#define EP_IntValue 0x000800 /* Integer value contained in u.iValue */
|
||||
#define EP_xIsSelect 0x001000 /* x.pSelect is valid (otherwise x.pList is) */
|
||||
#define EP_Skip 0x002000 /* Operator does not contribute to affinity */
|
||||
#define EP_Reduced 0x004000 /* Expr struct EXPR_REDUCEDSIZE bytes only */
|
||||
#define EP_Win 0x008000 /* Contains window functions */
|
||||
#define EP_MemToken 0x010000 /* Need to sqlite3DbFree() Expr.zToken */
|
||||
#define EP_IfNullRow 0x020000 /* The TK_IF_NULL_ROW opcode */
|
||||
#define EP_Unlikely 0x040000 /* unlikely() or likelihood() function */
|
||||
#define EP_ConstFunc 0x080000 /* A SQLITE_FUNC_CONSTANT or _SLOCHNG function */
|
||||
#define EP_CanBeNull 0x100000 /* Can be null despite NOT NULL constraint */
|
||||
#define EP_Subquery 0x200000 /* Tree contains a TK_SELECT operator */
|
||||
#define EP_InnerJoin 0x400000 /* Originates in ON/USING of an inner join */
|
||||
#define EP_TokenOnly 0x010000 /* Expr struct EXPR_TOKENONLYSIZE bytes only */
|
||||
#define EP_MemToken 0x020000 /* Need to sqlite3DbFree() Expr.zToken */
|
||||
#define EP_IfNullRow 0x040000 /* The TK_IF_NULL_ROW opcode */
|
||||
#define EP_Unlikely 0x080000 /* unlikely() or likelihood() function */
|
||||
#define EP_ConstFunc 0x100000 /* A SQLITE_FUNC_CONSTANT or _SLOCHNG function */
|
||||
#define EP_CanBeNull 0x200000 /* Can be null despite NOT NULL constraint */
|
||||
#define EP_Subquery 0x400000 /* Tree contains a TK_SELECT operator */
|
||||
#define EP_Leaf 0x800000 /* Expr.pLeft, .pRight, .u.pSelect all NULL */
|
||||
#define EP_WinFunc 0x1000000 /* TK_FUNCTION with Expr.y.pWin set */
|
||||
#define EP_Subrtn 0x2000000 /* Uses Expr.y.sub. TK_IN, _SELECT, or _EXISTS */
|
||||
@@ -2913,8 +2913,8 @@ struct Expr {
|
||||
#define ExprHasAllProperty(E,P) (((E)->flags&(P))==(P))
|
||||
#define ExprSetProperty(E,P) (E)->flags|=(P)
|
||||
#define ExprClearProperty(E,P) (E)->flags&=~(P)
|
||||
#define ExprAlwaysTrue(E) (((E)->flags&(EP_FromJoin|EP_IsTrue))==EP_IsTrue)
|
||||
#define ExprAlwaysFalse(E) (((E)->flags&(EP_FromJoin|EP_IsFalse))==EP_IsFalse)
|
||||
#define ExprAlwaysTrue(E) (((E)->flags&(EP_OuterON|EP_IsTrue))==EP_IsTrue)
|
||||
#define ExprAlwaysFalse(E) (((E)->flags&(EP_OuterON|EP_IsFalse))==EP_IsFalse)
|
||||
|
||||
/* Macros used to ensure that the correct members of unions are accessed
|
||||
** in Expr.
|
||||
@@ -5322,7 +5322,6 @@ const char *sqlite3JournalModename(int);
|
||||
#define IN_INDEX_NOOP_OK 0x0001 /* OK to return IN_INDEX_NOOP */
|
||||
#define IN_INDEX_MEMBERSHIP 0x0002 /* IN operator used for membership test */
|
||||
#define IN_INDEX_LOOP 0x0004 /* IN operator used as a loop */
|
||||
#define IN_INDEX_REUSE_CUR 0x0008 /* Reuse prior table cursor */
|
||||
int sqlite3FindInIndex(Parse *, Expr *, u32, int*, int*, int*);
|
||||
|
||||
int sqlite3JournalOpen(sqlite3_vfs *, const char *, sqlite3_file *, int, int);
|
||||
|
||||
+15
@@ -60,6 +60,11 @@ struct Thread {
|
||||
#define N_THREAD 26
|
||||
static Thread threadset[N_THREAD];
|
||||
|
||||
static void test_barrier(){
|
||||
sqlite3_mutex *pMutex = sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_APP1);
|
||||
sqlite3_mutex_enter(pMutex);
|
||||
sqlite3_mutex_leave(pMutex);
|
||||
}
|
||||
|
||||
/*
|
||||
** The main loop for a thread. Threads use busy waiting.
|
||||
@@ -76,16 +81,20 @@ static void *test_thread_main(void *pArg){
|
||||
p->db = 0;
|
||||
}
|
||||
p->pStmt = 0;
|
||||
test_barrier();
|
||||
p->completed = 1;
|
||||
while( p->opnum<=p->completed ) sched_yield();
|
||||
test_barrier();
|
||||
while( p->xOp ){
|
||||
if( p->zErr && p->zErr!=p->zStaticErr ){
|
||||
sqlite3_free(p->zErr);
|
||||
p->zErr = 0;
|
||||
}
|
||||
(*p->xOp)(p);
|
||||
test_barrier();
|
||||
p->completed++;
|
||||
while( p->opnum<=p->completed ) sched_yield();
|
||||
test_barrier();
|
||||
}
|
||||
if( p->pStmt ){
|
||||
sqlite3_finalize(p->pStmt);
|
||||
@@ -99,6 +108,7 @@ static void *test_thread_main(void *pArg){
|
||||
sqlite3_free(p->zErr);
|
||||
p->zErr = 0;
|
||||
}
|
||||
test_barrier();
|
||||
p->completed++;
|
||||
#ifndef SQLITE_OMIT_DEPRECATED
|
||||
sqlite3_thread_cleanup();
|
||||
@@ -166,7 +176,9 @@ static int SQLITE_TCLAPI tcl_thread_create(
|
||||
** Wait for a thread to reach its idle state.
|
||||
*/
|
||||
static void test_thread_wait(Thread *p){
|
||||
test_barrier();
|
||||
while( p->opnum>p->completed ) sched_yield();
|
||||
test_barrier();
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -456,6 +468,7 @@ static int SQLITE_TCLAPI tcl_thread_compile(
|
||||
threadset[i].xOp = do_compile;
|
||||
sqlite3_free(threadset[i].zArg);
|
||||
threadset[i].zArg = sqlite3_mprintf("%s", argv[2]);
|
||||
test_barrier();
|
||||
threadset[i].opnum++;
|
||||
return TCL_OK;
|
||||
}
|
||||
@@ -507,6 +520,7 @@ static int SQLITE_TCLAPI tcl_thread_step(
|
||||
}
|
||||
test_thread_wait(&threadset[i]);
|
||||
threadset[i].xOp = do_step;
|
||||
test_barrier();
|
||||
threadset[i].opnum++;
|
||||
return TCL_OK;
|
||||
}
|
||||
@@ -551,6 +565,7 @@ static int SQLITE_TCLAPI tcl_thread_finalize(
|
||||
threadset[i].xOp = do_finalize;
|
||||
sqlite3_free(threadset[i].zArg);
|
||||
threadset[i].zArg = 0;
|
||||
test_barrier();
|
||||
threadset[i].opnum++;
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
+5
-2
@@ -489,8 +489,11 @@ void sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u8 moreToFollow){
|
||||
sqlite3StrAccumInit(&x, 0, zFlgs, sizeof(zFlgs), 0);
|
||||
sqlite3_str_appendf(&x, " fg.af=%x.%c",
|
||||
pExpr->flags, pExpr->affExpr ? pExpr->affExpr : 'n');
|
||||
if( ExprHasProperty(pExpr, EP_FromJoin) ){
|
||||
sqlite3_str_appendf(&x, " iJoin=%d", pExpr->w.iJoin);
|
||||
if( ExprHasProperty(pExpr, EP_OuterON) ){
|
||||
sqlite3_str_appendf(&x, " outer.iJoin=%d", pExpr->w.iJoin);
|
||||
}
|
||||
if( ExprHasProperty(pExpr, EP_InnerON) ){
|
||||
sqlite3_str_appendf(&x, " inner.iJoin=%d", pExpr->w.iJoin);
|
||||
}
|
||||
if( ExprHasProperty(pExpr, EP_FromDDL) ){
|
||||
sqlite3_str_appendf(&x, " DDL");
|
||||
|
||||
+2
-4
@@ -1576,11 +1576,9 @@ char *sqlite3VdbeDisplayComment(
|
||||
}else if( c=='X' ){
|
||||
if( pOp->zComment && pOp->zComment[0] ){
|
||||
sqlite3_str_appendall(&x, pOp->zComment);
|
||||
}else{
|
||||
sqlite3_str_appendall(&x, zSynopsis+1);
|
||||
seenCom = 1;
|
||||
break;
|
||||
}
|
||||
seenCom = 1;
|
||||
break;
|
||||
}else{
|
||||
int v1 = translateP(c, pOp);
|
||||
int v2;
|
||||
|
||||
+17
-10
@@ -330,7 +330,7 @@ static WhereTerm *whereScanNext(WhereScan *pScan){
|
||||
&& (iColumn!=XN_EXPR
|
||||
|| sqlite3ExprCompareSkip(pTerm->pExpr->pLeft,
|
||||
pScan->pIdxExpr,iCur)==0)
|
||||
&& (pScan->iEquiv<=1 || !ExprHasProperty(pTerm->pExpr, EP_FromJoin))
|
||||
&& (pScan->iEquiv<=1 || !ExprHasProperty(pTerm->pExpr, EP_OuterON))
|
||||
){
|
||||
if( (pTerm->eOperator & WO_EQUIV)!=0
|
||||
&& pScan->nEquiv<ArraySize(pScan->aiCur)
|
||||
@@ -757,7 +757,7 @@ static int termCanDriveIndex(
|
||||
if( pTerm->leftCursor!=pSrc->iCursor ) return 0;
|
||||
if( (pTerm->eOperator & (WO_EQ|WO_IS))==0 ) return 0;
|
||||
if( (pSrc->fg.jointype & (JT_LEFT|JT_LTORJ))!=0
|
||||
&& !ExprHasProperty(pTerm->pExpr, EP_FromJoin)
|
||||
&& !ExprHasProperty(pTerm->pExpr, EP_OuterON)
|
||||
&& (pTerm->eOperator & WO_IS)
|
||||
){
|
||||
/* Cannot use an IS term from the WHERE clause as an index driver for
|
||||
@@ -1181,7 +1181,7 @@ static sqlite3_index_info *allocateIndexInfo(
|
||||
** RIGHT JOIN. See tag-20191211-001 for the
|
||||
** equivalent restriction for ordinary tables. */
|
||||
if( (pSrc->fg.jointype & (JT_LEFT|JT_LTORJ))!=0
|
||||
&& !ExprHasProperty(pTerm->pExpr, EP_FromJoin)
|
||||
&& !ExprHasProperty(pTerm->pExpr, EP_OuterON)
|
||||
){
|
||||
continue;
|
||||
}
|
||||
@@ -2059,7 +2059,7 @@ void sqlite3WhereTermPrint(WhereTerm *pTerm, int iTerm){
|
||||
memcpy(zType, "....", 5);
|
||||
if( pTerm->wtFlags & TERM_VIRTUAL ) zType[0] = 'V';
|
||||
if( pTerm->eOperator & WO_EQUIV ) zType[1] = 'E';
|
||||
if( ExprHasProperty(pTerm->pExpr, EP_FromJoin) ) zType[2] = 'L';
|
||||
if( ExprHasProperty(pTerm->pExpr, EP_OuterON) ) zType[2] = 'L';
|
||||
if( pTerm->wtFlags & TERM_CODED ) zType[3] = 'C';
|
||||
if( pTerm->eOperator & WO_SINGLE ){
|
||||
assert( (pTerm->eOperator & (WO_OR|WO_AND))==0 );
|
||||
@@ -2836,7 +2836,7 @@ static int whereLoopAddBtreeIndex(
|
||||
** RIGHT JOIN. Only constraints in the
|
||||
** ON clause are allowed. See tag-20191211-002 for the vtab equivalent. */
|
||||
if( (pSrc->fg.jointype & (JT_LEFT|JT_LTORJ))!=0
|
||||
&& !ExprHasProperty(pTerm->pExpr, EP_FromJoin|EP_InnerJoin)
|
||||
&& !ExprHasProperty(pTerm->pExpr, EP_OuterON|EP_InnerON)
|
||||
){
|
||||
continue;
|
||||
}
|
||||
@@ -3205,8 +3205,8 @@ static int whereUsablePartialIndex(
|
||||
for(i=0, pTerm=pWC->a; i<pWC->nTerm; i++, pTerm++){
|
||||
Expr *pExpr;
|
||||
pExpr = pTerm->pExpr;
|
||||
if( (!ExprHasProperty(pExpr, EP_FromJoin) || pExpr->w.iJoin==iTab)
|
||||
&& (isLeft==0 || ExprHasProperty(pExpr, EP_FromJoin))
|
||||
if( (!ExprHasProperty(pExpr, EP_OuterON) || pExpr->w.iJoin==iTab)
|
||||
&& (isLeft==0 || ExprHasProperty(pExpr, EP_OuterON))
|
||||
&& sqlite3ExprImpliesExpr(pParse, pExpr, pWhere, iTab)
|
||||
&& (pTerm->wtFlags & TERM_VNULL)==0
|
||||
){
|
||||
@@ -3482,7 +3482,14 @@ static int whereLoopAddBtree(
|
||||
}
|
||||
ApplyCostMultiplier(pNew->rRun, pTab->costMult);
|
||||
whereLoopOutputAdjust(pWC, pNew, rSize);
|
||||
rc = whereLoopInsert(pBuilder, pNew);
|
||||
if( (pSrc->fg.jointype & JT_RIGHT)!=0 && pProbe->aColExpr ){
|
||||
/* Do not do an SCAN of a index-on-expression in a RIGHT JOIN
|
||||
** because the cursor used to access the index might not be
|
||||
** positioned to the correct row during the right-join no-match
|
||||
** loop. */
|
||||
}else{
|
||||
rc = whereLoopInsert(pBuilder, pNew);
|
||||
}
|
||||
pNew->nOut = rSize;
|
||||
if( rc ) break;
|
||||
}
|
||||
@@ -5214,7 +5221,7 @@ static SQLITE_NOINLINE Bitmask whereOmitNoopJoin(
|
||||
pEnd = pWInfo->sWC.a + pWInfo->sWC.nTerm;
|
||||
for(pTerm=pWInfo->sWC.a; pTerm<pEnd; pTerm++){
|
||||
if( (pTerm->prereqAll & pLoop->maskSelf)!=0 ){
|
||||
if( !ExprHasProperty(pTerm->pExpr, EP_FromJoin)
|
||||
if( !ExprHasProperty(pTerm->pExpr, EP_OuterON)
|
||||
|| pTerm->pExpr->w.iJoin!=pItem->iCursor
|
||||
){
|
||||
break;
|
||||
@@ -6168,6 +6175,7 @@ void sqlite3WhereEnd(WhereInfo *pWInfo){
|
||||
}
|
||||
|
||||
assert( pWInfo->nLevel<=pTabList->nSrc );
|
||||
if( pWInfo->pExprMods ) whereUndoExprMods(pWInfo);
|
||||
for(i=0, pLevel=pWInfo->a; i<pWInfo->nLevel; i++, pLevel++){
|
||||
int k, last;
|
||||
VdbeOp *pOp, *pLastOp;
|
||||
@@ -6304,7 +6312,6 @@ void sqlite3WhereEnd(WhereInfo *pWInfo){
|
||||
|
||||
/* Final cleanup
|
||||
*/
|
||||
if( pWInfo->pExprMods ) whereUndoExprMods(pWInfo);
|
||||
pParse->nQueryLoop = pWInfo->savedNQueryLoop;
|
||||
whereInfoFree(db, pWInfo);
|
||||
return;
|
||||
|
||||
+42
-26
@@ -350,7 +350,7 @@ static void disableTerm(WhereLevel *pLevel, WhereTerm *pTerm){
|
||||
int nLoop = 0;
|
||||
assert( pTerm!=0 );
|
||||
while( (pTerm->wtFlags & TERM_CODED)==0
|
||||
&& (pLevel->iLeftJoin==0 || ExprHasProperty(pTerm->pExpr, EP_FromJoin))
|
||||
&& (pLevel->iLeftJoin==0 || ExprHasProperty(pTerm->pExpr, EP_OuterON))
|
||||
&& (pLevel->notReady & pTerm->prereqAll)==0
|
||||
){
|
||||
if( nLoop && (pTerm->wtFlags & TERM_LIKE)!=0 ){
|
||||
@@ -623,8 +623,7 @@ static int codeEqualityTerm(
|
||||
sqlite3ExprDelete(db, pX);
|
||||
}else{
|
||||
aiMap = (int*)sqlite3DbMallocZero(pParse->db, sizeof(int)*nEq);
|
||||
eType = sqlite3FindInIndex(pParse, pX, IN_INDEX_LOOP|IN_INDEX_REUSE_CUR, 0, aiMap,&iTab);
|
||||
iTab = pExpr->iTable;
|
||||
eType = sqlite3FindInIndex(pParse, pX, IN_INDEX_LOOP, 0, aiMap, &iTab);
|
||||
}
|
||||
pX = pExpr;
|
||||
}
|
||||
@@ -1072,7 +1071,7 @@ static void codeCursorHint(
|
||||
*/
|
||||
if( pTabItem->fg.jointype & JT_LEFT ){
|
||||
Expr *pExpr = pTerm->pExpr;
|
||||
if( !ExprHasProperty(pExpr, EP_FromJoin)
|
||||
if( !ExprHasProperty(pExpr, EP_OuterON)
|
||||
|| pExpr->w.iJoin!=pTabItem->iCursor
|
||||
){
|
||||
sWalker.eCode = 0;
|
||||
@@ -1081,7 +1080,7 @@ static void codeCursorHint(
|
||||
if( sWalker.eCode ) continue;
|
||||
}
|
||||
}else{
|
||||
if( ExprHasProperty(pTerm->pExpr, EP_FromJoin) ) continue;
|
||||
if( ExprHasProperty(pTerm->pExpr, EP_OuterON) ) continue;
|
||||
}
|
||||
|
||||
/* All terms in pWLoop->aLTerm[] except pEndRange are used to initialize
|
||||
@@ -2412,7 +2411,7 @@ Bitmask sqlite3WhereCodeOneLoopStart(
|
||||
Expr *pDelete; /* Local copy of OR clause term */
|
||||
int jmp1 = 0; /* Address of jump operation */
|
||||
testcase( (pTabItem[0].fg.jointype & JT_LEFT)!=0
|
||||
&& !ExprHasProperty(pOrExpr, EP_FromJoin)
|
||||
&& !ExprHasProperty(pOrExpr, EP_OuterON)
|
||||
); /* See TH3 vtab25.400 and ticket 614b25314c766238 */
|
||||
pDelete = pOrExpr = sqlite3ExprDup(db, pOrExpr, 0);
|
||||
if( db->mallocFailed ){
|
||||
@@ -2620,12 +2619,19 @@ Bitmask sqlite3WhereCodeOneLoopStart(
|
||||
}
|
||||
pE = pTerm->pExpr;
|
||||
assert( pE!=0 );
|
||||
if( (pTabItem->fg.jointype & (JT_LEFT|JT_LTORJ))
|
||||
&& !ExprHasProperty(pE,EP_FromJoin|EP_InnerJoin)
|
||||
){
|
||||
continue;
|
||||
if( pTabItem->fg.jointype & (JT_LEFT|JT_LTORJ|JT_RIGHT) ){
|
||||
if( !ExprHasProperty(pE,EP_OuterON|EP_InnerON) ){
|
||||
/* Defer processing WHERE clause constraints until after outer
|
||||
** join processing. tag-20220513a */
|
||||
continue;
|
||||
}else{
|
||||
Bitmask m = sqlite3WhereGetMask(&pWInfo->sMaskSet, pE->w.iJoin);
|
||||
if( m & pLevel->notReady ){
|
||||
/* An ON clause that is not ripe */
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( iLoop==1 && !sqlite3ExprCoveredByIndex(pE, pLevel->iTabCur, pIdx) ){
|
||||
iNext = 2;
|
||||
continue;
|
||||
@@ -2684,7 +2690,7 @@ Bitmask sqlite3WhereCodeOneLoopStart(
|
||||
if( (pTerm->eOperator & (WO_EQ|WO_IS))==0 ) continue;
|
||||
if( (pTerm->eOperator & WO_EQUIV)==0 ) continue;
|
||||
if( pTerm->leftCursor!=iCur ) continue;
|
||||
if( pTabItem->fg.jointype & (JT_LEFT|JT_LTORJ) ) continue;
|
||||
if( pTabItem->fg.jointype & (JT_LEFT|JT_LTORJ|JT_RIGHT) ) continue;
|
||||
pE = pTerm->pExpr;
|
||||
#ifdef WHERETRACE_ENABLED /* 0x800 */
|
||||
if( sqlite3WhereTrace & 0x800 ){
|
||||
@@ -2692,7 +2698,7 @@ Bitmask sqlite3WhereCodeOneLoopStart(
|
||||
sqlite3WhereTermPrint(pTerm, pWC->nTerm-j);
|
||||
}
|
||||
#endif
|
||||
assert( !ExprHasProperty(pE, EP_FromJoin) );
|
||||
assert( !ExprHasProperty(pE, EP_OuterON) );
|
||||
assert( (pTerm->prereqRight & pLevel->notReady)!=0 );
|
||||
assert( (pTerm->eOperator & (WO_OR|WO_AND))==0 );
|
||||
pAlt = sqlite3WhereFindTerm(pWC, iCur, pTerm->u.x.leftColumn, notReady,
|
||||
@@ -2763,18 +2769,8 @@ Bitmask sqlite3WhereCodeOneLoopStart(
|
||||
pLevel->addrFirst = sqlite3VdbeCurrentAddr(v);
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, 1, pLevel->iLeftJoin);
|
||||
VdbeComment((v, "record LEFT JOIN hit"));
|
||||
for(pTerm=pWC->a, j=0; j<pWC->nBase; j++, pTerm++){
|
||||
testcase( pTerm->wtFlags & TERM_VIRTUAL );
|
||||
testcase( pTerm->wtFlags & TERM_CODED );
|
||||
if( pTerm->wtFlags & (TERM_VIRTUAL|TERM_CODED) ) continue;
|
||||
if( (pTerm->prereqAll & pLevel->notReady)!=0 ){
|
||||
assert( pWInfo->untestedTerms );
|
||||
continue;
|
||||
}
|
||||
if( pTabItem->fg.jointype & JT_LTORJ ) continue;
|
||||
assert( pTerm->pExpr );
|
||||
sqlite3ExprIfFalse(pParse, pTerm->pExpr, addrCont, SQLITE_JUMPIFNULL);
|
||||
pTerm->wtFlags |= TERM_CODED;
|
||||
if( pLevel->pRJ==0 ){
|
||||
goto code_outer_join_constraints; /* WHERE clause constraints */
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2790,6 +2786,26 @@ Bitmask sqlite3WhereCodeOneLoopStart(
|
||||
pRJ->addrSubrtn = sqlite3VdbeCurrentAddr(v);
|
||||
assert( pParse->withinRJSubrtn < 255 );
|
||||
pParse->withinRJSubrtn++;
|
||||
|
||||
/* WHERE clause constraints must be deferred until after outer join
|
||||
** row elimination has completed, since WHERE clause constraints apply
|
||||
** to the results of the OUTER JOIN. The following loop generates the
|
||||
** appropriate WHERE clause constraint checks. tag-20220513a.
|
||||
*/
|
||||
code_outer_join_constraints:
|
||||
for(pTerm=pWC->a, j=0; j<pWC->nBase; j++, pTerm++){
|
||||
testcase( pTerm->wtFlags & TERM_VIRTUAL );
|
||||
testcase( pTerm->wtFlags & TERM_CODED );
|
||||
if( pTerm->wtFlags & (TERM_VIRTUAL|TERM_CODED) ) continue;
|
||||
if( (pTerm->prereqAll & pLevel->notReady)!=0 ){
|
||||
assert( pWInfo->untestedTerms );
|
||||
continue;
|
||||
}
|
||||
if( pTabItem->fg.jointype & JT_LTORJ ) continue;
|
||||
assert( pTerm->pExpr );
|
||||
sqlite3ExprIfFalse(pParse, pTerm->pExpr, addrCont, SQLITE_JUMPIFNULL);
|
||||
pTerm->wtFlags |= TERM_CODED;
|
||||
}
|
||||
}
|
||||
|
||||
#if WHERETRACE_ENABLED /* 0x20800 */
|
||||
@@ -2845,7 +2861,7 @@ SQLITE_NOINLINE void sqlite3WhereRightJoinLoop(
|
||||
WhereTerm *pTerm = &pWC->a[k];
|
||||
if( pTerm->wtFlags & TERM_VIRTUAL ) break;
|
||||
if( pTerm->prereqAll & ~mAll ) continue;
|
||||
if( ExprHasProperty(pTerm->pExpr, EP_FromJoin|EP_InnerJoin) ) continue;
|
||||
if( ExprHasProperty(pTerm->pExpr, EP_OuterON|EP_InnerON) ) continue;
|
||||
pSubWhere = sqlite3ExprAnd(pParse, pSubWhere,
|
||||
sqlite3ExprDup(pParse->db, pTerm->pExpr, 0));
|
||||
}
|
||||
|
||||
+10
-10
@@ -461,8 +461,8 @@ static int isAuxiliaryVtabOperator(
|
||||
** a join, then transfer the appropriate markings over to derived.
|
||||
*/
|
||||
static void transferJoinMarkings(Expr *pDerived, Expr *pBase){
|
||||
if( pDerived ){
|
||||
pDerived->flags |= pBase->flags & EP_FromJoin;
|
||||
if( pDerived && ExprHasProperty(pBase, EP_OuterON|EP_InnerON) ){
|
||||
pDerived->flags |= pBase->flags & (EP_OuterON|EP_InnerON);
|
||||
pDerived->w.iJoin = pBase->w.iJoin;
|
||||
}
|
||||
}
|
||||
@@ -917,7 +917,7 @@ static int termIsEquivalence(Parse *pParse, Expr *pExpr){
|
||||
CollSeq *pColl;
|
||||
if( !OptimizationEnabled(pParse->db, SQLITE_Transitive) ) return 0;
|
||||
if( pExpr->op!=TK_EQ && pExpr->op!=TK_IS ) return 0;
|
||||
if( ExprHasProperty(pExpr, EP_FromJoin) ) return 0;
|
||||
if( ExprHasProperty(pExpr, EP_OuterON) ) return 0;
|
||||
aff1 = sqlite3ExprAffinity(pExpr->pLeft);
|
||||
aff2 = sqlite3ExprAffinity(pExpr->pRight);
|
||||
if( aff1!=aff2
|
||||
@@ -1109,7 +1109,7 @@ static void exprAnalyze(
|
||||
}
|
||||
#endif
|
||||
|
||||
if( ExprHasProperty(pExpr, EP_FromJoin) ){
|
||||
if( ExprHasProperty(pExpr, EP_OuterON) ){
|
||||
Bitmask x = sqlite3WhereGetMask(pMaskSet, pExpr->w.iJoin);
|
||||
prereqAll |= x;
|
||||
extraRight = x-1; /* ON clause terms may not be used with an index
|
||||
@@ -1184,7 +1184,7 @@ static void exprAnalyze(
|
||||
pNew->eOperator = (operatorMask(pDup->op) + eExtraOp) & opMask;
|
||||
}else
|
||||
if( op==TK_ISNULL
|
||||
&& !ExprHasProperty(pExpr,EP_FromJoin)
|
||||
&& !ExprHasProperty(pExpr,EP_OuterON)
|
||||
&& 0==sqlite3ExprCanBeNull(pLeft)
|
||||
){
|
||||
assert( !ExprHasProperty(pExpr, EP_IntValue) );
|
||||
@@ -1255,7 +1255,7 @@ static void exprAnalyze(
|
||||
else if( pExpr->op==TK_NOTNULL ){
|
||||
if( pExpr->pLeft->op==TK_COLUMN
|
||||
&& pExpr->pLeft->iColumn>=0
|
||||
&& !ExprHasProperty(pExpr, EP_FromJoin)
|
||||
&& !ExprHasProperty(pExpr, EP_OuterON)
|
||||
){
|
||||
Expr *pNewExpr;
|
||||
Expr *pLeft = pExpr->pLeft;
|
||||
@@ -1459,8 +1459,8 @@ static void exprAnalyze(
|
||||
Expr *pNewExpr;
|
||||
pNewExpr = sqlite3PExpr(pParse, TK_MATCH,
|
||||
0, sqlite3ExprDup(db, pRight, 0));
|
||||
if( ExprHasProperty(pExpr, EP_FromJoin) && pNewExpr ){
|
||||
ExprSetProperty(pNewExpr, EP_FromJoin);
|
||||
if( ExprHasProperty(pExpr, EP_OuterON) && pNewExpr ){
|
||||
ExprSetProperty(pNewExpr, EP_OuterON);
|
||||
pNewExpr->w.iJoin = pExpr->w.iJoin;
|
||||
}
|
||||
idxNew = whereClauseInsert(pWC, pNewExpr, TERM_VIRTUAL|TERM_DYNAMIC);
|
||||
@@ -1827,9 +1827,9 @@ void sqlite3WhereTabFuncArgs(
|
||||
sqlite3ExprDup(pParse->db, pArgs->a[j].pExpr, 0), 0);
|
||||
pTerm = sqlite3PExpr(pParse, TK_EQ, pColRef, pRhs);
|
||||
if( pItem->fg.jointype & (JT_LEFT|JT_LTORJ) ){
|
||||
joinType = EP_FromJoin;
|
||||
joinType = EP_OuterON;
|
||||
}else{
|
||||
joinType = EP_InnerJoin;
|
||||
joinType = EP_InnerON;
|
||||
}
|
||||
sqlite3SetJoinExpr(pTerm, pItem->iCursor, joinType);
|
||||
whereClauseInsert(pWC, pTerm, TERM_DYNAMIC);
|
||||
|
||||
@@ -181,29 +181,53 @@ if {[working_64bit_int]} {
|
||||
}
|
||||
|
||||
test_expr expr-1.111 {i1=NULL, i2=8} {i1 IS i2} 0
|
||||
test_expr expr-1.111b {i1=NULL, i2=8} {i1 IS NOT DISTINCT FROM i2} 0
|
||||
test_expr expr-1.112 {i1=NULL, i2=NULL} {i1 IS i2} 1
|
||||
test_expr expr-1.112b {i1=NULL, i2=NULL} {i1 IS NOT DISTINCT FROM i2} 1
|
||||
test_expr expr-1.113 {i1=6, i2=NULL} {i1 IS i2} 0
|
||||
test_expr expr-1.113b {i1=6, i2=NULL} {i1 IS NOT DISTINCT FROM i2} 0
|
||||
test_expr expr-1.114 {i1=6, i2=6} {i1 IS i2} 1
|
||||
test_expr expr-1.114b {i1=6, i2=6} {i1 IS NOT DISTINCT FROM i2} 1
|
||||
test_expr expr-1.115 {i1=NULL, i2=8} \
|
||||
{CASE WHEN i1 IS i2 THEN 'yes' ELSE 'no' END} no
|
||||
test_expr expr-1.115b {i1=NULL, i2=8} \
|
||||
{CASE WHEN i1 IS NOT DISTINCT FROM i2 THEN 'yes' ELSE 'no' END} no
|
||||
test_expr expr-1.116 {i1=NULL, i2=NULL} \
|
||||
{CASE WHEN i1 IS i2 THEN 'yes' ELSE 'no' END} yes
|
||||
test_expr expr-1.116b {i1=NULL, i2=NULL} \
|
||||
{CASE WHEN i1 IS NOT DISTINCT FROM i2 THEN 'yes' ELSE 'no' END} yes
|
||||
test_expr expr-1.117 {i1=6, i2=NULL} \
|
||||
{CASE WHEN i1 IS i2 THEN 'yes' ELSE 'no' END} no
|
||||
test_expr expr-1.117b {i1=6, i2=NULL} \
|
||||
{CASE WHEN i1 IS NOT DISTINCT FROM i2 THEN 'yes' ELSE 'no' END} no
|
||||
test_expr expr-1.118 {i1=8, i2=8} \
|
||||
{CASE WHEN i1 IS i2 THEN 'yes' ELSE 'no' END} yes
|
||||
test_expr expr-1.118b {i1=8, i2=8} \
|
||||
{CASE WHEN i1 IS NOT DISTINCT FROM i2 THEN 'yes' ELSE 'no' END} yes
|
||||
test_expr expr-1.119 {i1=NULL, i2=8} {i1 IS NOT i2} 1
|
||||
test_expr expr-1.119b {i1=NULL, i2=8} {i1 IS DISTINCT FROM i2} 1
|
||||
test_expr expr-1.120 {i1=NULL, i2=NULL} {i1 IS NOT i2} 0
|
||||
test_expr expr-1.120b {i1=NULL, i2=NULL} {i1 IS DISTINCT FROM i2} 0
|
||||
test_expr expr-1.121 {i1=6, i2=NULL} {i1 IS NOT i2} 1
|
||||
test_expr expr-1.121b {i1=6, i2=NULL} {i1 IS DISTINCT FROM i2} 1
|
||||
test_expr expr-1.122 {i1=6, i2=6} {i1 IS NOT i2} 0
|
||||
test_expr expr-1.122b {i1=6, i2=6} {i1 IS DISTINCT FROM i2} 0
|
||||
test_expr expr-1.123 {i1=NULL, i2=8} \
|
||||
{CASE WHEN i1 IS NOT i2 THEN 'yes' ELSE 'no' END} yes
|
||||
test_expr expr-1.123b {i1=NULL, i2=8} \
|
||||
{CASE WHEN i1 IS DISTINCT FROM i2 THEN 'yes' ELSE 'no' END} yes
|
||||
test_expr expr-1.124 {i1=NULL, i2=NULL} \
|
||||
{CASE WHEN i1 IS NOT i2 THEN 'yes' ELSE 'no' END} no
|
||||
test_expr expr-1.124b {i1=NULL, i2=NULL} \
|
||||
{CASE WHEN i1 IS DISTINCT FROM i2 THEN 'yes' ELSE 'no' END} no
|
||||
test_expr expr-1.125 {i1=6, i2=NULL} \
|
||||
{CASE WHEN i1 IS NOT i2 THEN 'yes' ELSE 'no' END} yes
|
||||
test_expr expr-1.125b {i1=6, i2=NULL} \
|
||||
{CASE WHEN i1 IS DISTINCT FROM i2 THEN 'yes' ELSE 'no' END} yes
|
||||
test_expr expr-1.126 {i1=8, i2=8} \
|
||||
{CASE WHEN i1 IS NOT i2 THEN 'yes' ELSE 'no' END} no
|
||||
test_expr expr-1.126b {i1=8, i2=8} \
|
||||
{CASE WHEN i1 IS DISTINCT FROM i2 THEN 'yes' ELSE 'no' END} no
|
||||
|
||||
do_catchsql_test expr-1.127 {
|
||||
SELECT 1 IS #1;
|
||||
|
||||
@@ -798,4 +798,13 @@ do_execsql_test in-20.1 {
|
||||
SELECT (1 IN (2 IS TRUE));
|
||||
} {1}
|
||||
|
||||
# Forum post: https://sqlite.org/forum/forumpost/5782619992.
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test in-21.1 {
|
||||
CREATE TABLE t0(c0);
|
||||
SELECT COUNT(*) FROM t0 ORDER BY (t0.c0 IN ());
|
||||
} {0}
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
+115
@@ -260,4 +260,119 @@ do_execsql_test join8-7020 {
|
||||
ORDER BY coalesce(t0.a, t0.y+200, t4.d);
|
||||
} {/.*BLOOM FILTER ON t2.*BLOOM FILTER ON t3.*BLOOM FILTER ON t4.*/}
|
||||
|
||||
# 2022-05-12 Difference with PG found (by Dan) while exploring
|
||||
# https://sqlite.org/forum/forumpost/677a0ab93fcd9ccd
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test join8-8000 {
|
||||
CREATE TABLE t1(a INT, b INT);
|
||||
CREATE TABLE t2(c INT, d INT);
|
||||
CREATE TABLE t3(e INT, f INT);
|
||||
INSERT INTO t1 VALUES(1, 2);
|
||||
INSERT INTO t2 VALUES(3, 4);
|
||||
INSERT INTO t3 VALUES(5, 6);
|
||||
} {}
|
||||
do_execsql_test join8-8010 {
|
||||
SELECT *
|
||||
FROM t3 LEFT JOIN t2 ON true
|
||||
JOIN t1 ON (t3.e IS t2.c);
|
||||
} {}
|
||||
do_execsql_test join8-8020 {
|
||||
SELECT *
|
||||
FROM t3 LEFT JOIN t2 ON true
|
||||
JOIN t1 ON (t3.e IS NOT DISTINCT FROM t2.c);
|
||||
} {}
|
||||
|
||||
# 2022-05-13 The idea of reusing subquery cursors does not
|
||||
# work, if the cursors are used both for scanning and lookups.
|
||||
#
|
||||
reset_db
|
||||
db null -
|
||||
do_execsql_test join8-9000 {
|
||||
CREATE TABLE t1(a INTEGER PRIMARY KEY, b TEXT, c TEXT, d REAL);
|
||||
INSERT INTO t1 VALUES(1,'E','bb',NULL),(2,NULL,NULL,NULL);
|
||||
SELECT * FROM t1 NATURAL RIGHT JOIN t1 AS t2 WHERE (a,b) IN (SELECT a+0, b FROM t1);
|
||||
} {1 E bb -}
|
||||
|
||||
# 2022-05-14 https://sqlite.org/forum/forumpost/c06b10ad7e
|
||||
#
|
||||
reset_db
|
||||
db null -
|
||||
do_execsql_test join8-10000 {
|
||||
CREATE TABLE t1(c0 INT UNIQUE);
|
||||
CREATE TABLE t2(c0);
|
||||
CREATE TABLE t2i(c0 INT);
|
||||
CREATE TABLE t3(c0 INT);
|
||||
INSERT INTO t1 VALUES(1);
|
||||
INSERT INTO t2 VALUES(2);
|
||||
INSERT INTO t2i VALUES(2);
|
||||
INSERT INTO t3 VALUES(3);
|
||||
} {}
|
||||
do_execsql_test join8-10010 {
|
||||
SELECT DISTINCT t1.c0, t3.c0
|
||||
FROM t2 NATURAL JOIN t1 RIGHT JOIN t3 ON t1.c0;
|
||||
} {- 3}
|
||||
do_execsql_test join8-10020 {
|
||||
SELECT t1.c0, t3.c0
|
||||
FROM t2 NATURAL JOIN t1 RIGHT JOIN t3 ON t1.c0;
|
||||
} {- 3}
|
||||
do_execsql_test join8-10030 {
|
||||
SELECT DISTINCT t1.c0, t3.c0
|
||||
FROM t2 NATURAL CROSS JOIN t1 RIGHT JOIN t3 ON t1.c0;
|
||||
} {- 3}
|
||||
do_execsql_test join8-10040 {
|
||||
SELECT t1.c0, t3.c0
|
||||
FROM t1 NATURAL CROSS JOIN t2 RIGHT JOIN t3 ON t1.c0;
|
||||
} {- 3}
|
||||
do_execsql_test join8-10050 {
|
||||
SELECT DISTINCT t1.c0, t3.c0
|
||||
FROM t2i NATURAL JOIN t1 RIGHT JOIN t3 ON t1.c0;
|
||||
} {- 3}
|
||||
do_execsql_test join8-10060 {
|
||||
SELECT DISTINCT +t1.c0, t3.c0
|
||||
FROM t2 NATURAL JOIN t1 RIGHT JOIN t3 ON t1.c0;
|
||||
} {- 3}
|
||||
do_execsql_test join8-10070 {
|
||||
SELECT DISTINCT +t1.c0, t3.c0
|
||||
FROM t1 NATURAL CROSS JOIN t2 RIGHT JOIN t3 ON t1.c0;
|
||||
} {- 3}
|
||||
do_execsql_test join8-10080 {
|
||||
SELECT DISTINCT t1.c0, t3.c0
|
||||
FROM t2 NATURAL JOIN t1 RIGHT JOIN t3 ON t1.c0<>0;
|
||||
} {- 3}
|
||||
|
||||
# 2022-05-14
|
||||
# index-on-expr scan on a RIGHT JOIN
|
||||
# dbsqlfuzz 39ee60004ff027a9e2846cf76e02cd5ac0953739
|
||||
#
|
||||
reset_db
|
||||
db null -
|
||||
do_execsql_test join8-11000 {
|
||||
CREATE TABLE t1(a);
|
||||
CREATE TABLE t2(b);
|
||||
INSERT INTO t2 VALUES(0),(1),(2);
|
||||
SELECT * FROM t1 RIGHT JOIN t2 ON (a=b) WHERE 99+(b+1)!=99;
|
||||
} {- 0 - 1 - 2}
|
||||
do_execsql_test join8-11010 {
|
||||
CREATE INDEX t2b ON t2(b+1) WHERE b IS NOT NULL;
|
||||
SELECT * FROM t1 RIGHT JOIN t2 ON (a=b) WHERE 99+(b+1)!=99;
|
||||
} {- 0 - 1 - 2}
|
||||
do_execsql_test join8-11020 {
|
||||
DROP TABLE t1;
|
||||
DROP TABLE t2;
|
||||
CREATE TABLE t1(a);
|
||||
CREATE TABLE t2(b, c, d);
|
||||
INSERT INTO t2 VALUES(1, 3, 'not-4');
|
||||
SELECT b, d FROM t1 RIGHT JOIN t2 WHERE (b+0)=1 AND d!=4;
|
||||
} {1 not-4}
|
||||
do_execsql_test join8-11030 {
|
||||
CREATE INDEX i2 ON t2((b+0), d);
|
||||
SELECT b, d FROM t1 RIGHT JOIN t2 WHERE (b+0)=1 AND d!=4;
|
||||
} {1 not-4}
|
||||
do_execsql_test join8-11040 {
|
||||
DROP INDEX i2;
|
||||
CREATE INDEX i2 ON t2((b+0), d) WHERE d IS NOT NULL;
|
||||
SELECT b, d FROM t1 RIGHT JOIN t2 WHERE (b+0)=1 AND d!=4;
|
||||
} {1 not-4}
|
||||
|
||||
finish_test
|
||||
|
||||
+443
@@ -0,0 +1,443 @@
|
||||
# 2022-05-13
|
||||
#
|
||||
# The author disclaims copyright to this source code. In place of
|
||||
# a legal notice, here is a blessing:
|
||||
#
|
||||
# May you do good and not evil.
|
||||
# May you find forgiveness for yourself and forgive others.
|
||||
# May you share freely, never taking more than you give.
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
# This file implements tests for JOINs that use Bloom filters.
|
||||
#
|
||||
# The test case output is (mostly) all generated by PostgreSQL 14. This
|
||||
# test module was created as follows:
|
||||
#
|
||||
# 1. Run a TCL script (included at the bottom of this file) that
|
||||
# generates an input script for "psql" that will run man
|
||||
# diverse tests on joins.
|
||||
#
|
||||
# 2. Run the script from step (1) through psql and collect the
|
||||
# output.
|
||||
#
|
||||
# 3. Make a few minor global search-and-replace operations to convert
|
||||
# the psql output into a form suitable for this test module.
|
||||
#
|
||||
# 4. Add this header, and the script content at the footer.
|
||||
#
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
db nullvalue -
|
||||
db eval {
|
||||
CREATE TABLE t1(a INT);
|
||||
INSERT INTO t1 VALUES(1),(NULL);
|
||||
CREATE TABLE t2(b INT);
|
||||
INSERT INTO t2 VALUES(2),(NULL);
|
||||
}
|
||||
do_execsql_test joinE-1 {
|
||||
SELECT a, b
|
||||
FROM t1 INNER JOIN t2 ON true
|
||||
ORDER BY coalesce(a,b,3);
|
||||
} {
|
||||
1 2
|
||||
1 -
|
||||
- 2
|
||||
- -
|
||||
}
|
||||
do_execsql_test joinE-2 {
|
||||
SELECT a, b
|
||||
FROM t1 INNER JOIN t2 ON true WHERE a IS NULL
|
||||
ORDER BY coalesce(a,b,3);
|
||||
} {
|
||||
- 2
|
||||
- -
|
||||
}
|
||||
do_execsql_test joinE-3 {
|
||||
SELECT a, b
|
||||
FROM t1 INNER JOIN t2 ON a IS NULL
|
||||
ORDER BY coalesce(a,b,3);
|
||||
} {
|
||||
- 2
|
||||
- -
|
||||
}
|
||||
do_execsql_test joinE-4 {
|
||||
SELECT a, b
|
||||
FROM t1 INNER JOIN t2 ON true WHERE b IS NULL
|
||||
ORDER BY coalesce(a,b,3);
|
||||
} {
|
||||
1 -
|
||||
- -
|
||||
}
|
||||
do_execsql_test joinE-5 {
|
||||
SELECT a, b
|
||||
FROM t1 INNER JOIN t2 ON b IS NULL
|
||||
ORDER BY coalesce(a,b,3);
|
||||
} {
|
||||
1 -
|
||||
- -
|
||||
}
|
||||
do_execsql_test joinE-6 {
|
||||
SELECT a, b
|
||||
FROM t1 LEFT JOIN t2 ON true
|
||||
ORDER BY coalesce(a,b,3);
|
||||
} {
|
||||
1 2
|
||||
1 -
|
||||
- 2
|
||||
- -
|
||||
}
|
||||
do_execsql_test joinE-7 {
|
||||
SELECT a, b
|
||||
FROM t1 LEFT JOIN t2 ON true WHERE a IS NULL
|
||||
ORDER BY coalesce(a,b,3);
|
||||
} {
|
||||
- 2
|
||||
- -
|
||||
}
|
||||
do_execsql_test joinE-8 {
|
||||
SELECT a, b
|
||||
FROM t1 LEFT JOIN t2 ON a IS NULL
|
||||
ORDER BY coalesce(a,b,3);
|
||||
} {
|
||||
1 -
|
||||
- 2
|
||||
- -
|
||||
}
|
||||
do_execsql_test joinE-9 {
|
||||
SELECT a, b
|
||||
FROM t1 LEFT JOIN t2 ON true WHERE b IS NULL
|
||||
ORDER BY coalesce(a,b,3);
|
||||
} {
|
||||
1 -
|
||||
- -
|
||||
}
|
||||
do_execsql_test joinE-10 {
|
||||
SELECT a, b
|
||||
FROM t1 LEFT JOIN t2 ON b IS NULL
|
||||
ORDER BY coalesce(a,b,3);
|
||||
} {
|
||||
1 -
|
||||
- -
|
||||
}
|
||||
do_execsql_test joinE-11 {
|
||||
SELECT a, b
|
||||
FROM t1 RIGHT JOIN t2 ON true
|
||||
ORDER BY coalesce(a,b,3);
|
||||
} {
|
||||
1 2
|
||||
1 -
|
||||
- 2
|
||||
- -
|
||||
}
|
||||
do_execsql_test joinE-12 {
|
||||
SELECT a, b
|
||||
FROM t1 RIGHT JOIN t2 ON true WHERE a IS NULL
|
||||
ORDER BY coalesce(a,b,3);
|
||||
} {
|
||||
- 2
|
||||
- -
|
||||
}
|
||||
do_execsql_test joinE-13 {
|
||||
SELECT a, b
|
||||
FROM t1 RIGHT JOIN t2 ON a IS NULL
|
||||
ORDER BY coalesce(a,b,3);
|
||||
} {
|
||||
- 2
|
||||
- -
|
||||
}
|
||||
do_execsql_test joinE-14 {
|
||||
SELECT a, b
|
||||
FROM t1 RIGHT JOIN t2 ON true WHERE b IS NULL
|
||||
ORDER BY coalesce(a,b,3);
|
||||
} {
|
||||
1 -
|
||||
- -
|
||||
}
|
||||
do_execsql_test joinE-15 {
|
||||
SELECT a, b
|
||||
FROM t1 RIGHT JOIN t2 ON b IS NULL
|
||||
ORDER BY coalesce(a,b,3);
|
||||
} {
|
||||
1 -
|
||||
- 2
|
||||
- -
|
||||
}
|
||||
do_execsql_test joinE-16 {
|
||||
SELECT a, b
|
||||
FROM t1 FULL JOIN t2 ON true
|
||||
ORDER BY coalesce(a,b,3);
|
||||
} {
|
||||
1 2
|
||||
1 -
|
||||
- 2
|
||||
- -
|
||||
}
|
||||
do_execsql_test joinE-17 {
|
||||
SELECT a, b
|
||||
FROM t1 FULL JOIN t2 ON true WHERE a IS NULL
|
||||
ORDER BY coalesce(a,b,3);
|
||||
} {
|
||||
- 2
|
||||
- -
|
||||
}
|
||||
|
||||
# PG-14 is unable to perform this join. It says: FULL JOIN is only
|
||||
# supported with merge-joinable or hash-joinable join conditions
|
||||
#
|
||||
# do_execsql_test joinE-18 {
|
||||
# SELECT a, b
|
||||
# FROM t1 FULL JOIN t2 ON a IS NULL
|
||||
# ORDER BY coalesce(a,b,3);
|
||||
# } {
|
||||
# }
|
||||
|
||||
do_execsql_test joinE-19 {
|
||||
SELECT a, b
|
||||
FROM t1 FULL JOIN t2 ON true WHERE b IS NULL
|
||||
ORDER BY coalesce(a,b,3);
|
||||
} {
|
||||
1 -
|
||||
- -
|
||||
}
|
||||
|
||||
# PG-14 is unable to perform this join. It says: FULL JOIN is only
|
||||
# supported with merge-joinable or hash-joinable join conditions
|
||||
#
|
||||
# do_execsql_test joinE-20 {
|
||||
# SELECT a, b
|
||||
# FROM t1 FULL JOIN t2 ON b IS NULL
|
||||
# ORDER BY coalesce(a,b,3);
|
||||
# } {
|
||||
# }
|
||||
|
||||
db eval {
|
||||
DELETE FROM t1;
|
||||
INSERT INTO t1 VALUES(1);
|
||||
DELETE FROM t2;
|
||||
INSERT INTO t2 VALUES(NULL);
|
||||
}
|
||||
|
||||
do_execsql_test joinE-21 {
|
||||
SELECT a, b
|
||||
FROM t1 INNER JOIN t2 ON true
|
||||
ORDER BY coalesce(a,b,3);
|
||||
} {
|
||||
1 -
|
||||
}
|
||||
do_execsql_test joinE-22 {
|
||||
SELECT a, b
|
||||
FROM t1 INNER JOIN t2 ON true WHERE a IS NULL
|
||||
ORDER BY coalesce(a,b,3);
|
||||
} {
|
||||
}
|
||||
do_execsql_test joinE-23 {
|
||||
SELECT a, b
|
||||
FROM t1 INNER JOIN t2 ON a IS NULL
|
||||
ORDER BY coalesce(a,b,3);
|
||||
} {
|
||||
}
|
||||
do_execsql_test joinE-24 {
|
||||
SELECT a, b
|
||||
FROM t1 INNER JOIN t2 ON true WHERE b IS NULL
|
||||
ORDER BY coalesce(a,b,3);
|
||||
} {
|
||||
1 -
|
||||
}
|
||||
do_execsql_test joinE-25 {
|
||||
SELECT a, b
|
||||
FROM t1 INNER JOIN t2 ON b IS NULL
|
||||
ORDER BY coalesce(a,b,3);
|
||||
} {
|
||||
1 -
|
||||
}
|
||||
do_execsql_test joinE-26 {
|
||||
SELECT a, b
|
||||
FROM t1 LEFT JOIN t2 ON true
|
||||
ORDER BY coalesce(a,b,3);
|
||||
} {
|
||||
1 -
|
||||
}
|
||||
do_execsql_test joinE-27 {
|
||||
SELECT a, b
|
||||
FROM t1 LEFT JOIN t2 ON true WHERE a IS NULL
|
||||
ORDER BY coalesce(a,b,3);
|
||||
} {
|
||||
}
|
||||
do_execsql_test joinE-28 {
|
||||
SELECT a, b
|
||||
FROM t1 LEFT JOIN t2 ON a IS NULL
|
||||
ORDER BY coalesce(a,b,3);
|
||||
} {
|
||||
1 -
|
||||
}
|
||||
do_execsql_test joinE-29 {
|
||||
SELECT a, b
|
||||
FROM t1 LEFT JOIN t2 ON true WHERE b IS NULL
|
||||
ORDER BY coalesce(a,b,3);
|
||||
} {
|
||||
1 -
|
||||
}
|
||||
do_execsql_test joinE-30 {
|
||||
SELECT a, b
|
||||
FROM t1 LEFT JOIN t2 ON b IS NULL
|
||||
ORDER BY coalesce(a,b,3);
|
||||
} {
|
||||
1 -
|
||||
}
|
||||
do_execsql_test joinE-31 {
|
||||
SELECT a, b
|
||||
FROM t1 RIGHT JOIN t2 ON true
|
||||
ORDER BY coalesce(a,b,3);
|
||||
} {
|
||||
1 -
|
||||
}
|
||||
|
||||
do_execsql_test joinE-32 {
|
||||
SELECT a, b
|
||||
FROM t1 RIGHT JOIN t2 ON true WHERE a IS NULL
|
||||
ORDER BY coalesce(a,b,3);
|
||||
} {
|
||||
}
|
||||
|
||||
do_execsql_test joinE-33 {
|
||||
SELECT a, b
|
||||
FROM t1 RIGHT JOIN t2 ON a IS NULL
|
||||
ORDER BY coalesce(a,b,3);
|
||||
} {
|
||||
- -
|
||||
}
|
||||
do_execsql_test joinE-34 {
|
||||
SELECT a, b
|
||||
FROM t1 RIGHT JOIN t2 ON true WHERE b IS NULL
|
||||
ORDER BY coalesce(a,b,3);
|
||||
} {
|
||||
1 -
|
||||
}
|
||||
do_execsql_test joinE-35 {
|
||||
SELECT a, b
|
||||
FROM t1 RIGHT JOIN t2 ON b IS NULL
|
||||
ORDER BY coalesce(a,b,3);
|
||||
} {
|
||||
1 -
|
||||
}
|
||||
do_execsql_test joinE-36 {
|
||||
SELECT a, b
|
||||
FROM t1 FULL JOIN t2 ON true
|
||||
ORDER BY coalesce(a,b,3);
|
||||
} {
|
||||
1 -
|
||||
}
|
||||
do_execsql_test joinE-37 {
|
||||
SELECT a, b
|
||||
FROM t1 FULL JOIN t2 ON true WHERE a IS NULL
|
||||
ORDER BY coalesce(a,b,3);
|
||||
} {
|
||||
}
|
||||
|
||||
# PG-14 is unable
|
||||
#
|
||||
# do_execsql_test joinE-38 {
|
||||
# SELECT a, b
|
||||
# FROM t1 FULL JOIN t2 ON a IS NULL
|
||||
# ORDER BY coalesce(a,b,3);
|
||||
# } {
|
||||
# }
|
||||
|
||||
do_execsql_test joinE-39 {
|
||||
SELECT a, b
|
||||
FROM t1 FULL JOIN t2 ON true WHERE b IS NULL
|
||||
ORDER BY coalesce(a,b,3);
|
||||
} {
|
||||
1 -
|
||||
}
|
||||
|
||||
# PG-14 is unable
|
||||
# do_execsql_test joinE-40 {
|
||||
# SELECT a, b
|
||||
# FROM t1 FULL JOIN t2 ON b IS NULL
|
||||
# ORDER BY coalesce(a,b,3);
|
||||
# } {
|
||||
# }
|
||||
|
||||
finish_test
|
||||
|
||||
##############################################################################
|
||||
# This is the PG-14 test script generator
|
||||
#
|
||||
# puts "
|
||||
# \\pset border off
|
||||
# \\pset tuples_only on
|
||||
# \\pset null -
|
||||
#
|
||||
# DROP TABLE IF EXISTS t1;
|
||||
# DROP TABLE IF EXISTS t2;
|
||||
# CREATE TABLE t1(a INT);
|
||||
# INSERT INTO t1 VALUES(1),(NULL);
|
||||
# CREATE TABLE t2(b INT);
|
||||
# INSERT INTO t2 VALUES(2),(NULL);
|
||||
# "
|
||||
#
|
||||
# proc echo {prefix txt} {
|
||||
# regsub -all {\n} $txt \n$prefix txt
|
||||
# puts "$prefix$txt"
|
||||
# }
|
||||
#
|
||||
# set n 0
|
||||
# set k 0
|
||||
# foreach j1 {INNER LEFT RIGHT FULL} {
|
||||
# foreach on1 {
|
||||
# true
|
||||
# {true WHERE a IS NULL}
|
||||
# {a IS NULL}
|
||||
# {true WHERE b IS NULL}
|
||||
# {b IS NULL}
|
||||
# } {
|
||||
#
|
||||
# incr n
|
||||
# incr k
|
||||
# set q1 ""
|
||||
# append q1 "SELECT a, b\n"
|
||||
# append q1 " FROM t1 $j1 JOIN t2 ON $on1\n"
|
||||
# append q1 " ORDER BY coalesce(a,b,3);"
|
||||
#
|
||||
# echo "\\qecho " "do_execsql_test joinE-$n \{"
|
||||
# echo "\\qecho X " $q1
|
||||
# echo "\\qecho " "\} \{"
|
||||
# puts $q1
|
||||
# echo "\\qecho " "\}"
|
||||
#
|
||||
# }
|
||||
# }
|
||||
#
|
||||
# puts "
|
||||
# DELETE FROM t1;
|
||||
# INSERT INTO t1 VALUES(1);
|
||||
# DELETE FROM t2;
|
||||
# INSERT INTO t2 VALUES(NULL);
|
||||
# "
|
||||
#
|
||||
# foreach j1 {INNER LEFT RIGHT FULL} {
|
||||
# foreach on1 {
|
||||
# true
|
||||
# {true WHERE a IS NULL}
|
||||
# {a IS NULL}
|
||||
# {true WHERE b IS NULL}
|
||||
# {b IS NULL}
|
||||
# } {
|
||||
#
|
||||
# incr n
|
||||
# incr k
|
||||
# set q1 ""
|
||||
# append q1 "SELECT a, b\n"
|
||||
# append q1 " FROM t1 $j1 JOIN t2 ON $on1\n"
|
||||
# append q1 " ORDER BY coalesce(a,b,3);"
|
||||
#
|
||||
# echo "\\qecho " "do_execsql_test joinE-$n \{"
|
||||
# echo "\\qecho X " $q1
|
||||
# echo "\\qecho " "\} \{"
|
||||
# puts $q1
|
||||
# echo "\\qecho " "\}"
|
||||
#
|
||||
# }
|
||||
# }
|
||||
@@ -148,11 +148,13 @@ catch { array unset ::dbcache }
|
||||
# random integer between 0 and 1,000,000
|
||||
# 0 and 99.
|
||||
do_test 2.1 {
|
||||
catch { array unset ctx_used }
|
||||
for {set i 0} {$i < 100} {incr i} {
|
||||
while 1 {
|
||||
set ctx [expr abs(int(rand() *1000000))]
|
||||
if {[info exists ::dbcache($ctx)]==0} break
|
||||
if {[info exists ctx_used($ctx)]==0} break
|
||||
}
|
||||
set ctx_used($ctx) 1
|
||||
|
||||
set file test_remote.db$ctx
|
||||
forcedelete $file
|
||||
|
||||
Reference in New Issue
Block a user