Merge the latest trunk enhancements into the wal2 branch.
FossilOrigin-Name: c8ad869938b06378f49c02655c00ee4f3315e1275d15e69d4ff61d6f60230fe8
This commit is contained in:
commit
029eb1bf15
67
Makefile.in
67
Makefile.in
@ -631,7 +631,7 @@ FUZZCHECK_OPT += -DSQLITE_ENABLE_RTREE
|
||||
FUZZCHECK_OPT += -DSQLITE_ENABLE_GEOPOLY
|
||||
FUZZCHECK_OPT += -DSQLITE_ENABLE_DBSTAT_VTAB
|
||||
FUZZCHECK_OPT += -DSQLITE_ENABLE_BYTECODE_VTAB
|
||||
FUZZCHECK_SRC = $(TOP)/test/fuzzcheck.c $(TOP)/test/ossfuzz.c
|
||||
FUZZCHECK_SRC = $(TOP)/test/fuzzcheck.c $(TOP)/test/ossfuzz.c $(TOP)/test/fuzzinvariants.c
|
||||
DBFUZZ_OPT =
|
||||
|
||||
# This is the default Makefile target. The objects listed here
|
||||
@ -1523,48 +1523,66 @@ fiddle_dir_abs = $(TOP)/$(fiddle_dir)
|
||||
# ^^^ some emcc opts require absolute paths
|
||||
fiddle_html = $(fiddle_dir)/fiddle.html
|
||||
fiddle_module_js = $(fiddle_dir)/fiddle-module.js
|
||||
fiddle_generated = $(fiddle_module_js) \
|
||||
$(fiddle_dir)/fiddle-module.wasm
|
||||
sqlite3_wasm_js = $(fiddle_dir)/sqlite3.js
|
||||
sqlite3_wasm = $(fiddle_dir)/sqlite3.wasm
|
||||
sqlite3_wasm_generated = $(sqlite3_wasm) $(sqlite3_wasm_js)
|
||||
clean-wasm:
|
||||
rm -f $(fiddle_generated) $(sqlite3_wasm_generated)
|
||||
clean: clean-wasm
|
||||
#emcc_opt = -O0
|
||||
#emcc_opt = -O1
|
||||
#emcc_opt = -O2
|
||||
#emcc_opt = -O3
|
||||
emcc_opt = -Oz
|
||||
emcc_flags = $(emcc_opt) -sALLOW_TABLE_GROWTH -I. $(SHELL_OPT)
|
||||
emcc_flags = $(emcc_opt) -sALLOW_TABLE_GROWTH -sSTRICT_JS \
|
||||
-sENVIRONMENT=web -sMODULARIZE \
|
||||
-sEXPORTED_RUNTIME_METHODS=@$(fiddle_dir_abs)/EXPORTED_RUNTIME_METHODS \
|
||||
-sDYNAMIC_EXECUTION=0 \
|
||||
-I. $(SHELL_OPT)
|
||||
$(fiddle_module_js): Makefile sqlite3.c shell.c \
|
||||
$(fiddle_dir)/EXPORTED_RUNTIME_METHODS \
|
||||
$(fiddle_dir)/EXPORTED_FUNCTIONS.fiddle
|
||||
emcc -o $@ $(emcc_flags) \
|
||||
-sENVIRONMENT=web \
|
||||
-sMODULARIZE \
|
||||
-sEXPORT_NAME=initFiddleModule \
|
||||
-sEXPORTED_RUNTIME_METHODS=@$(fiddle_dir_abs)/EXPORTED_RUNTIME_METHODS \
|
||||
-sEXPORTED_FUNCTIONS=@$(fiddle_dir_abs)/EXPORTED_FUNCTIONS.fiddle \
|
||||
sqlite3.c shell.c
|
||||
gzip < $@ > $@.gz
|
||||
gzip < $(fiddle_dir)/fiddle-module.wasm > $(fiddle_dir)/fiddle-module.wasm.gz
|
||||
$(sqlite3_wasm_js): Makefile sqlite3.c \
|
||||
$(fiddle_dir)/sqlite3-api.js \
|
||||
$(fiddle_dir)/EXPORTED_RUNTIME_METHODS \
|
||||
$(fiddle_dir)/EXPORTED_FUNCTIONS.sqlite3-api
|
||||
emcc -o $@ $(emcc_flags) \
|
||||
-sENVIRONMENT=web \
|
||||
-sMODULARIZE \
|
||||
-sEXPORT_NAME=initSqlite3Module \
|
||||
-sEXPORTED_RUNTIME_METHODS=@$(fiddle_dir_abs)/EXPORTED_RUNTIME_METHODS \
|
||||
-sEXPORTED_FUNCTIONS=@$(fiddle_dir_abs)/EXPORTED_FUNCTIONS.sqlite3-api \
|
||||
--post-js=$(fiddle_dir)/sqlite3-api.js \
|
||||
--no-entry \
|
||||
sqlite3.c
|
||||
fiddle: $(fiddle_module_js)
|
||||
gzip < $@ > $@.gz
|
||||
gzip < $(sqlite3_wasm) > $(sqlite3_wasm).gz
|
||||
gzip < $(fiddle_dir)/sqlite3-api.js > $(fiddle_dir)/sqlite3-api.js.gz
|
||||
$(fiddle_dir)/fiddle.js.gz: $(fiddle_dir)/fiddle.js
|
||||
gzip < $< > $@
|
||||
$(fiddle_dir)/sqlite3-api.js.gz: $(fiddle_dir)/sqlite3-api.js
|
||||
gzip < $< > $@
|
||||
|
||||
fiddle_generated = $(fiddle_module_js) $(fiddle_module_js).gz \
|
||||
$(fiddle_dir)/fiddle-module.wasm \
|
||||
$(fiddle_dir)/fiddle-module.wasm.gz \
|
||||
$(fiddle_dir)/fiddle.js.gz
|
||||
sqlite3_wasm_generated = \
|
||||
$(sqlite3_wasm) $(sqlite3_wasm).gz \
|
||||
$(sqlite3_wasm_js) $(sqlite3_wasm_js).gz \
|
||||
$(fiddle_dir)/sqlite3.js.gz \
|
||||
$(fiddle_dir)/sqlite3-api.js.gz
|
||||
|
||||
clean-wasm:
|
||||
rm -f $(fiddle_generated) $(sqlite3_wasm_generated)
|
||||
clean: clean-wasm
|
||||
fiddle: $(fiddle_module_js) $(fiddle_dir)/fiddle.js.gz
|
||||
sqlite3-wasm: $(sqlite3_wasm_js)
|
||||
wasm: fiddle sqlite3-wasm
|
||||
########################################################################
|
||||
# Explanation of the emcc build flags:
|
||||
# Explanation of the emcc build flags follows. Full docs for these can
|
||||
# be found at:
|
||||
#
|
||||
# https://github.com/emscripten-core/emscripten/blob/main/src/settings.js
|
||||
#
|
||||
# -sENVIRONMENT=web: elides bootstrap code related to non-web JS
|
||||
# environments like node.js. Removing this makes the output a tiny
|
||||
@ -1588,6 +1606,23 @@ wasm: fiddle sqlite3-wasm
|
||||
# developers. e.g., _sqlite3_open_v2 and _sqlite3_finalize. Must be
|
||||
# an absolute path!
|
||||
#
|
||||
# -sSTRICT_JS ensures that the emitted JS code includes the 'use
|
||||
# strict' option. Note that -sSTRICT is more broadly-scoped and
|
||||
# results in build errors.
|
||||
#
|
||||
# -sALLOW_TABLE_GROWTH is required for (at a minimum) the UDF-binding
|
||||
# feature.
|
||||
#
|
||||
# -sDYNAMIC_EXECUTION=0 disables eval() and the Function constructor.
|
||||
# If the build runs without these, it's preferable to use this flag
|
||||
# because certain execution environments disallow those constructs.
|
||||
# This flag is not strictly necessary, however.
|
||||
#
|
||||
# -sWASM_BIGINT is UNTESTED but "should" allow the int64-using C APIs
|
||||
# to work with JS/wasm, insofar as the JS environment supports the
|
||||
# BigInt type. That support requires an extremely recent browser:
|
||||
# Safari didn't get that support until late 2020.
|
||||
#
|
||||
# --no-entry: for compiling library code with no main(). If this is
|
||||
# not supplied and the code has a main(), it is called as part of the
|
||||
# module init process. Note that main() is #if'd out of shell.c
|
||||
|
@ -1706,7 +1706,7 @@ FUZZCHECK_OPTS = $(FUZZCHECK_OPTS) -DSQLITE_ENABLE_GEOPOLY
|
||||
FUZZCHECK_OPTS = $(FUZZCHECK_OPTS) -DSQLITE_ENABLE_DBSTAT_VTAB
|
||||
FUZZCHECK_OPTS = $(FUZZCHECK_OPTS) -DSQLITE_ENABLE_BYTECODE_VTAB
|
||||
|
||||
FUZZCHECK_SRC = $(TOP)\test\fuzzcheck.c $(TOP)\test\ossfuzz.c
|
||||
FUZZCHECK_SRC = $(TOP)\test\fuzzcheck.c $(TOP)\test\ossfuzz.c $(TOP)\test\fuzzinvariants.c
|
||||
OSSSHELL_SRC = $(TOP)\test\ossshell.c $(TOP)\test\ossfuzz.c
|
||||
DBFUZZ_COMPILE_OPTS = -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION
|
||||
KV_COMPILE_OPTS = -DSQLITE_THREADSAFE=0 -DSQLITE_DIRECT_OVERFLOW_READ
|
||||
|
@ -8,7 +8,8 @@ clean:
|
||||
|
||||
fiddle_files = emscripten.css fiddle.html \
|
||||
fiddle.js fiddle-module.js \
|
||||
fiddle-module.wasm fiddle-worker.js
|
||||
fiddle-module.wasm fiddle-worker.js \
|
||||
$(wildcard *.wasm.gz) $(wildcard *.js.gz)
|
||||
|
||||
# fiddle_remote is the remote destination for the fiddle app. It
|
||||
# must be a [user@]HOST:/path for rsync.
|
||||
|
@ -234,8 +234,9 @@
|
||||
fiddleModule.FS.createDataFile("/", fn, buffer, true, true);
|
||||
const oldName = Sqlite3Shell.dbFilename();
|
||||
Sqlite3Shell.exec('.open "/'+fn+'"');
|
||||
if(oldName !== fn){
|
||||
fiddleModule.FS.unlink(oldName);
|
||||
if(oldName && oldName !== fn){
|
||||
try{fiddleModule.FS.unlink(oldName);}
|
||||
catch(e){/*ignored*/}
|
||||
}
|
||||
stdout("Replaced DB with",fn+".");
|
||||
return;
|
||||
|
@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
<title>sqlite3 fiddle</title>
|
||||
<title>SQLite3 Fiddle</title>
|
||||
<link rel="shortcut icon" href="data:image/x-icon;," type="image/x-icon">
|
||||
<!-- to add a togglable terminal-style view, uncomment the following
|
||||
two lines and ensure that these files are on the web server. -->
|
||||
@ -12,50 +12,97 @@
|
||||
<link rel="stylesheet" href="emscripten.css"/>
|
||||
<style>
|
||||
/* The following styles are for app-level use. */
|
||||
:root {
|
||||
--sqlite-blue: #044a64;
|
||||
--textarea-color1: #044a64;
|
||||
--textarea-color2: white;
|
||||
}
|
||||
textarea {
|
||||
font-family: monospace;
|
||||
flex: 1 1 auto;
|
||||
background-color: var(--textarea-color1);
|
||||
color: var(--textarea-color2);
|
||||
}
|
||||
textarea#input {
|
||||
color: var(--textarea-color1);
|
||||
background-color: var(--textarea-color2);
|
||||
}
|
||||
header {
|
||||
font-size: 130%;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
background-color: var(--sqlite-blue);
|
||||
color: white;
|
||||
font-size: 120%;
|
||||
font-weight: bold;
|
||||
border-radius: 0.25em;
|
||||
padding: 0.2em 0.5em;
|
||||
}
|
||||
header > .powered-by {
|
||||
font-size: 80%;
|
||||
}
|
||||
header a, header a:visited, header a:hover {
|
||||
color: inherit;
|
||||
}
|
||||
#main-wrapper {
|
||||
display: flex;
|
||||
flex-direction: column-reverse;
|
||||
flex: 20 1 auto;
|
||||
flex: 1 1 auto;
|
||||
margin: 0.5em 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
#main-wrapper.side-by-side {
|
||||
flex-direction: row-reverse;
|
||||
flex-direction: row;
|
||||
}
|
||||
#main-wrapper.side-by-side > fieldset {
|
||||
margin-left: 0.25em;
|
||||
margin-right: 0.25em;
|
||||
}
|
||||
#main-wrapper:not(.side-by-side) > fieldset {
|
||||
margin-bottom: 0.25em;
|
||||
}
|
||||
#main-wrapper.swapio {
|
||||
flex-direction: column;
|
||||
}
|
||||
#main-wrapper.side-by-side.swapio {
|
||||
flex-direction: row;
|
||||
flex-direction: row-reverse;
|
||||
}
|
||||
.ta-wrapper{
|
||||
.zone-wrapper{
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
margin: 0 0.25em;
|
||||
margin: 0;
|
||||
flex: 1 1 0%;
|
||||
border-radius: 0.5em;
|
||||
min-width: inherit/*important: resolves inability to scroll fieldset child element!*/;
|
||||
padding: 0.35em 0 0 0;
|
||||
}
|
||||
.zone-wrapper textarea {
|
||||
border-radius: 0.5em;
|
||||
flex: 1 1 auto;
|
||||
/*min/max width resolve an inexplicable margin on the RHS. The -1em
|
||||
is for the padding, else we overlap the parent boundaries.*/
|
||||
/*min-width: calc(100% - 1em);
|
||||
max-width: calc(100% - 1em);
|
||||
padding: 0 0.5em;*/
|
||||
}
|
||||
.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;
|
||||
|
||||
.zone-wrapper.input { flex: 10 1 auto; }
|
||||
.zone-wrapper.output { flex: 20 1 auto; }
|
||||
.zone-wrapper > div {
|
||||
display:flex;
|
||||
flex: 1 1 0%;
|
||||
}
|
||||
.zone-wrapper.output {}
|
||||
.button-bar {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
flex: 0 1 auto;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
align-content: space-between;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
.button-bar button {
|
||||
margin: 0.25em 1em;
|
||||
.button-bar > * {
|
||||
margin: 0.05em 0.5em 0.05em 0;
|
||||
flex: 0 1 auto;
|
||||
align-self: auto;
|
||||
}
|
||||
label[for] {
|
||||
cursor: pointer;
|
||||
@ -70,97 +117,68 @@
|
||||
pointer-events: none !important;
|
||||
display: none !important;
|
||||
}
|
||||
/* Safari supports neither styling of nor event handling on a
|
||||
fieldset legend, so we emulate a fieldset-like widget. */
|
||||
.fieldset {
|
||||
fieldset {
|
||||
border-radius: 0.5em;
|
||||
border: 1px inset;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 0.25em;
|
||||
}
|
||||
.fieldset > .legend {
|
||||
position: relative;
|
||||
top: -1.5ex;
|
||||
padding: 0 0.5em;
|
||||
font-size: 85%;
|
||||
margin-left: 0.5em;
|
||||
flex: 0 1 auto;
|
||||
align-self: self-start;
|
||||
cursor: pointer;
|
||||
fieldset.options {
|
||||
font-size: 80%;
|
||||
margin-top: 0.5em;
|
||||
}
|
||||
.fieldset.options > div {
|
||||
fieldset:not(.options) > legend {
|
||||
font-size: 80%;
|
||||
}
|
||||
fieldset.options > div {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
font-size: 70%;
|
||||
margin: 0 0.5em 0.5em 0.5em;
|
||||
}
|
||||
.fieldset > .legend > span {
|
||||
position: relative;
|
||||
fieldset button {
|
||||
font-size: inherit;
|
||||
}
|
||||
.fieldset > .legend::before {
|
||||
/* Hide the parent element's top border where this
|
||||
element intersects it. */
|
||||
content: ' ';
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: white
|
||||
/* REALLY want to 'inherit' the color from the fieldset's
|
||||
parent, but inherit leads to a transparent bg, which is
|
||||
exactly what we're trying to avoid here. */;
|
||||
opacity: 1;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
}
|
||||
.fieldset > .legend::after {
|
||||
fieldset.collapsible > legend > .fieldset-toggle::after {
|
||||
content: " [hide]";
|
||||
position: relative;
|
||||
}
|
||||
.fieldset.collapsed > .legend::after {
|
||||
fieldset.collapsible.collapsed > legend > .fieldset-toggle::after {
|
||||
content: " [show]";
|
||||
position: relative;
|
||||
}
|
||||
span.labeled-input {
|
||||
padding: 0.25em;
|
||||
margin: 0.25em 0.5em;
|
||||
margin: 0.05em 0.25em;
|
||||
border-radius: 0.25em;
|
||||
white-space: nowrap;
|
||||
background: #0002;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
#notes-caveats {
|
||||
border-top: 1px dotted;
|
||||
padding-top: 0.25em;
|
||||
margin-top: 0.5em;
|
||||
span.labeled-input > *:nth-child(2) {
|
||||
margin-left: 0.3em;
|
||||
}
|
||||
.center { text-align: center; }
|
||||
|
||||
body.terminal-mode {
|
||||
max-height: calc(100% - 2em);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
}
|
||||
#view-terminal {
|
||||
}
|
||||
#view-terminal {}
|
||||
.app-view {
|
||||
flex: 20 1 auto;
|
||||
}
|
||||
#titlebar {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
#view-split {
|
||||
display: flex;
|
||||
flex-direction: column-reverse;
|
||||
}
|
||||
#view-split > .fieldset.options {
|
||||
margin-top: 0.5em;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<header id='titlebar'><span>sqlite3 fiddle</span></header>
|
||||
<header id='titlebar'>
|
||||
<span>SQLite3 Fiddle</span>
|
||||
<span class='powered-by'>Powered by
|
||||
<a href='https://sqlite.org'>SQLite3</a></span>
|
||||
</header>
|
||||
<!-- emscripten bits -->
|
||||
<figure id="module-spinner">
|
||||
<div class="spinner"></div>
|
||||
@ -177,12 +195,13 @@
|
||||
</div><!-- /emscripten bits -->
|
||||
|
||||
<div id='view-terminal' class='app-view hidden initially-hidden'>
|
||||
This is a placeholder for a terminal-like view.
|
||||
This is a placeholder for a terminal-like view which is not in
|
||||
the default build.
|
||||
</div>
|
||||
|
||||
<div id='view-split' class='app-view initially-hidden'>
|
||||
<div class='fieldset options collapsible'>
|
||||
<span class='legend'><span>Options</span></span>
|
||||
<fieldset class='options collapsible'>
|
||||
<legend><button class='fieldset-toggle'>Options</button></legend>
|
||||
<div class=''>
|
||||
<span class='labeled-input'>
|
||||
<input type='checkbox' id='opt-cb-sbs'
|
||||
@ -209,8 +228,8 @@
|
||||
<label for='opt-cb-autoclear'>Auto-clear output</label>
|
||||
</span>
|
||||
<span class='labeled-input'>
|
||||
<input type='file' id='load-db'/>
|
||||
<label>Load DB</label>
|
||||
<input type='file' id='load-db' class='hidden'/>
|
||||
<button id='btn-load-db'>Load DB...</button>
|
||||
</span>
|
||||
<span class='labeled-input'>
|
||||
<button id='btn-export'>Download DB</button>
|
||||
@ -218,40 +237,40 @@
|
||||
<span class='labeled-input'>
|
||||
<button id='btn-reset'>Reset DB</button>
|
||||
</span>
|
||||
<span class='labeled-input'>
|
||||
<select id='select-examples'></select>
|
||||
</span>
|
||||
</div>
|
||||
</div><!-- .fieldset -->
|
||||
</fieldset><!-- .options -->
|
||||
<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. If only a subset
|
||||
-- is currently selected, only that part is executed.
|
||||
.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'>
|
||||
<fieldset class='zone-wrapper input'>
|
||||
<legend><div class='button-bar'>
|
||||
<button id='btn-shell-exec'>Run</button>
|
||||
<button id='btn-clear'>Clear Input</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 data-cmd='.help'>Help</button-->
|
||||
<select id='select-examples'></select>
|
||||
</div></legend>
|
||||
<div><textarea id="input"
|
||||
placeholder="Shell input. Ctrl-enter/shift-enter runs it.">
|
||||
-- ==================================================
|
||||
-- Use ctrl-enter or shift-enter to execute sqlite3
|
||||
-- shell commands and SQL.
|
||||
-- If a subset of the text is currently selected,
|
||||
-- only that part is executed.
|
||||
-- ==================================================
|
||||
.nullvalue NULL
|
||||
.headers on
|
||||
</textarea></div>
|
||||
</fieldset>
|
||||
<fieldset class='zone-wrapper output'>
|
||||
<legend><div class='button-bar'>
|
||||
<button id='btn-clear-output'>Clear Output</button>
|
||||
<button id='btn-interrupt' class='hidden' disabled>Interrupt</button>
|
||||
<!-- interruption cannot work in the current configuration
|
||||
because we cannot send an interrupt message when work
|
||||
is currently underway. At that point the Worker is
|
||||
tied up and will not receive the message. -->
|
||||
</div>
|
||||
</div>
|
||||
</div></legend>
|
||||
<div><textarea id="output" readonly
|
||||
placeholder="Shell output."></textarea></div>
|
||||
</fieldset>
|
||||
</div>
|
||||
</div> <!-- #view-split -->
|
||||
<!-- Maintenance notes:
|
||||
|
@ -216,7 +216,7 @@
|
||||
That slows it down but is useful for testing. */
|
||||
echoToConsole: false,
|
||||
/* If true, display input/output areas side-by-side. */
|
||||
sideBySide: false,
|
||||
sideBySide: true,
|
||||
/* If true, swap positions of the input/output areas. */
|
||||
swapInOut: false
|
||||
},
|
||||
@ -525,19 +525,35 @@
|
||||
|
||||
/** Initiate a download of the db. */
|
||||
const btnExport = E('#btn-export');
|
||||
const eDisableDuringExport = [
|
||||
/* UI elements to disable while export is running. Normally
|
||||
the export is fast enough that this won't matter, but we
|
||||
really don't want to be reading (from outside of sqlite)
|
||||
the db when the user taps btnShellExec. */
|
||||
btnShellExec, btnExport
|
||||
];
|
||||
const eLoadDb = E('#load-db');
|
||||
const btnLoadDb = E('#btn-load-db');
|
||||
btnLoadDb.addEventListener('click', ()=>eLoadDb.click());
|
||||
/**
|
||||
Enables (if passed true) or disables all UI elements which
|
||||
"might," if timed "just right," interfere with an
|
||||
in-progress db import/export/exec operation.
|
||||
*/
|
||||
const enableMutatingElements = function f(enable){
|
||||
if(!f._elems){
|
||||
f._elems = [
|
||||
/* UI elements to disable while import/export are
|
||||
running. Normally the export is fast enough
|
||||
that this won't matter, but we really don't
|
||||
want to be reading (from outside of sqlite) the
|
||||
db when the user taps btnShellExec. */
|
||||
btnShellExec, btnExport, eLoadDb
|
||||
];
|
||||
}
|
||||
f._elems.forEach( enable
|
||||
? (e)=>e.removeAttribute('disabled')
|
||||
: (e)=>e.setAttribute('disabled','disabled') );
|
||||
};
|
||||
btnExport.addEventListener('click',function(){
|
||||
eDisableDuringExport.forEach(e=>e.setAttribute('disabled','disabled'));
|
||||
enableMutatingElements(false);
|
||||
SF.wMsg('db-export');
|
||||
});
|
||||
SF.addMsgHandler('db-export', function(ev){
|
||||
eDisableDuringExport.forEach(e=>e.removeAttribute('disabled'));
|
||||
enableMutatingElements(true);
|
||||
ev = ev.data;
|
||||
if(ev.error){
|
||||
SF.echo("Export failed:",ev.error);
|
||||
@ -560,11 +576,11 @@
|
||||
/**
|
||||
Handle load/import of an external db file.
|
||||
*/
|
||||
E('#load-db').addEventListener('change',function(){
|
||||
eLoadDb.addEventListener('change',function(){
|
||||
const f = this.files[0];
|
||||
const r = new FileReader();
|
||||
const status = {loaded: 0, total: 0};
|
||||
this.setAttribute('disabled','disabled');
|
||||
enableMutatingElements(false);
|
||||
r.addEventListener('loadstart', function(){
|
||||
SF.echo("Loading",f.name,"...");
|
||||
});
|
||||
@ -573,7 +589,7 @@
|
||||
});
|
||||
const that = this;
|
||||
r.addEventListener('load', function(){
|
||||
that.removeAttribute('disabled');
|
||||
enableMutatingElements(true);
|
||||
SF.echo("Loaded",f.name+". Opening db...");
|
||||
SF.wMsg('open',{
|
||||
filename: f.name,
|
||||
@ -581,25 +597,25 @@
|
||||
});
|
||||
});
|
||||
r.addEventListener('error',function(){
|
||||
that.removeAttribute('disabled');
|
||||
enableMutatingElements(true);
|
||||
SF.echo("Loading",f.name,"failed for unknown reasons.");
|
||||
});
|
||||
r.addEventListener('abort',function(){
|
||||
that.removeAttribute('disabled');
|
||||
enableMutatingElements(true);
|
||||
SF.echo("Cancelled loading of",f.name+".");
|
||||
});
|
||||
r.readAsArrayBuffer(f);
|
||||
});
|
||||
|
||||
EAll('.fieldset.collapsible').forEach(function(fs){
|
||||
const legend = E(fs,'span.legend'),
|
||||
EAll('fieldset.collapsible').forEach(function(fs){
|
||||
const btnToggle = E(fs,'legend > .fieldset-toggle'),
|
||||
content = EAll(fs,':scope > div');
|
||||
legend.addEventListener('click', function(){
|
||||
btnToggle.addEventListener('click', function(){
|
||||
fs.classList.toggle('collapsed');
|
||||
content.forEach((d)=>d.classList.toggle('hidden'));
|
||||
}, false);
|
||||
});
|
||||
|
||||
|
||||
/**
|
||||
Given a DOM element, this routine measures its "effective
|
||||
height", which is the bounding top/bottom range of this element
|
||||
@ -706,6 +722,14 @@
|
||||
(function(){
|
||||
const xElem = E('#select-examples');
|
||||
const examples = [
|
||||
{name: "Help", sql:
|
||||
`-- ================================================
|
||||
-- Use ctrl-enter or shift-enter to execute sqlite3
|
||||
-- shell commands and SQL.
|
||||
-- If a subset of the text is currently selected,
|
||||
-- only that part is executed.
|
||||
-- ================================================
|
||||
.help`},
|
||||
{name: "Timer on", sql: ".timer on"},
|
||||
{name: "Setup table T", sql:`.nullvalue NULL
|
||||
CREATE TABLE t(a,b);
|
||||
@ -779,5 +803,7 @@ SELECT group_concat(rtrim(t),x'0a') as Mandelbrot FROM a;`}
|
||||
'any number of changes or outright removal at any time.\n');
|
||||
delete ForceResizeKludge.$disabled;
|
||||
ForceResizeKludge();
|
||||
|
||||
btnShellExec.click();
|
||||
}/*onSFLoaded()*/;
|
||||
})();
|
||||
|
@ -85,7 +85,8 @@
|
||||
if(!Module.postRun) Module.postRun = [];
|
||||
/* ^^^^ the name Module is, in this setup, scope-local in the generated
|
||||
file sqlite3.js, with which this file gets combined at build-time. */
|
||||
Module.postRun.push(function(namespace){
|
||||
Module.postRun.push(function(namespace/*the module object, the target for
|
||||
installed features*/){
|
||||
'use strict';
|
||||
/* For reference: sql.js does essentially everything we want and
|
||||
it solves much of the wasm-related voodoo, but we'll need a
|
||||
@ -221,11 +222,10 @@ Module.postRun.push(function(namespace){
|
||||
["sqlite3_prepare_v2", "number", ["number", "string", "number", "number", "number"]],
|
||||
["sqlite3_prepare_v2_sqlptr", "sqlite3_prepare_v2",
|
||||
/* Impl which requires that the 2nd argument be a pointer to
|
||||
the SQL, instead of a string. This is used for cases where
|
||||
we require a non-NULL value for the final argument. We may
|
||||
or may not need this, depending on how our higher-level
|
||||
API shapes up, but this code's spiritual guide (sql.js)
|
||||
uses it we we'll include it. */
|
||||
the SQL string, instead of being converted to a
|
||||
string. This is used for cases where we require a non-NULL
|
||||
value for the final argument (exec()'ing multiple
|
||||
statements from one input string). */
|
||||
"number", ["number", "number", "number", "number", "number"]],
|
||||
["sqlite3_reset", "number", ["number"]],
|
||||
["sqlite3_result_blob",null,["number", "number", "number", "number"]],
|
||||
@ -270,33 +270,56 @@ Module.postRun.push(function(namespace){
|
||||
|
||||
- ()
|
||||
- (undefined) (same effect as ())
|
||||
- (Uint8Array holding an sqlite3 db image)
|
||||
- (filename[,buffer])
|
||||
- (buffer)
|
||||
|
||||
It always generates a random filename and sets is to
|
||||
the `filename` property of this object.
|
||||
Where a buffer indicates a Uint8Array holding an sqlite3 db
|
||||
image.
|
||||
|
||||
Developer's note: the reason it does not (any longer) support
|
||||
":memory:" as a name is because we can apparently only export
|
||||
images of DBs which are stored in the pseudo-filesystem
|
||||
provided by the JS APIs. Since exporting and importing images
|
||||
is an important usability feature for this class, ":memory:"
|
||||
DBs are not supported (until/unless we can find a way to export
|
||||
those as well). The naming semantics will certainly evolve as
|
||||
this API does.
|
||||
If the filename is provided, only the last component of the
|
||||
path is used - any path prefix is stripped and certain
|
||||
"special" characters are replaced with `_`. If no name is
|
||||
provided, a random name is generated. The resulting filename is
|
||||
the one used for accessing the db file within root directory of
|
||||
the emscripten-supplied virtual filesystem, and is set (with no
|
||||
path part) as the DB object's `filename` property.
|
||||
|
||||
Note that the special sqlite3 db names ":memory:" and ""
|
||||
(temporary db) have no special meanings here. We can apparently
|
||||
only export images of DBs which are stored in the
|
||||
pseudo-filesystem provided by the JS APIs. Since exporting and
|
||||
importing images is an important usability feature for this
|
||||
class, ":memory:" DBs are not supported (until/unless we can
|
||||
find a way to export those as well). The naming semantics will
|
||||
certainly evolve as this API does.
|
||||
*/
|
||||
const DB = function(arg){
|
||||
const fn = "db-"+((Math.random() * 10000000) | 0)+
|
||||
"-"+((Math.random() * 10000000) | 0)+".sqlite3";
|
||||
let buffer;
|
||||
if(name instanceof Uint8Array){
|
||||
let buffer, fn;
|
||||
if(arg instanceof Uint8Array){
|
||||
buffer = arg;
|
||||
arg = undefined;
|
||||
}else if(arguments.length && undefined!==arg){
|
||||
toss("Invalid arguments to DB constructor.",
|
||||
"Expecting no args, undefined, or a",
|
||||
"sqlite3 file as a Uint8Array.");
|
||||
}else if(arguments.length){ /*(filename[,buffer])*/
|
||||
if('string'===typeof arg){
|
||||
const p = arg.split('/').pop().replace(':','_');
|
||||
if(p) fn = p;
|
||||
if(arguments.length>1){
|
||||
buffer = arguments[1];
|
||||
}
|
||||
}else if(undefined!==arg){
|
||||
toss("Invalid arguments to DB constructor.",
|
||||
"Expecting (), (undefined), (name,buffer),",
|
||||
"or (buffer), where buffer an sqlite3 db ",
|
||||
"as a Uint8Array.");
|
||||
}
|
||||
}
|
||||
if(!fn){
|
||||
fn = "db-"+((Math.random() * 10000000) | 0)+
|
||||
"-"+((Math.random() * 10000000) | 0)+".sqlite3";
|
||||
}
|
||||
if(buffer){
|
||||
if(!(buffer instanceof Uint8Array)){
|
||||
toss("Expecting Uint8Array image of db contents.");
|
||||
}
|
||||
FS.createDataFile("/", fn, buffer, true, true);
|
||||
}
|
||||
setValue(pPtrArg, 0, "i32");
|
||||
@ -381,9 +404,9 @@ Module.postRun.push(function(namespace){
|
||||
default: toss("Invalid argument count for exec().");
|
||||
};
|
||||
if('string'!==typeof out.sql) toss("Missing SQL argument.");
|
||||
if(out.opt.callback){
|
||||
if(out.opt.callback || out.opt.resultRows){
|
||||
switch((undefined===out.opt.rowMode)
|
||||
? 'stmt' : out.opt.rowMode) {
|
||||
? 'stmt' : out.opt.rowMode) {
|
||||
case 'object': out.cbArg = (stmt)=>stmt.get({}); break;
|
||||
case 'array': out.cbArg = (stmt)=>stmt.get([]); break;
|
||||
case 'stmt': out.cbArg = (stmt)=>stmt; break;
|
||||
@ -417,9 +440,12 @@ Module.postRun.push(function(namespace){
|
||||
/**
|
||||
Finalizes all open statements and closes this database
|
||||
connection. This is a no-op if the db has already been
|
||||
closed.
|
||||
closed. If the db is open and alsoUnlink is truthy then the
|
||||
this.filename entry in the pseudo-filesystem will also be
|
||||
removed (and any error in that attempt is silently
|
||||
ignored).
|
||||
*/
|
||||
close: function(){
|
||||
close: function(alsoUnlink){
|
||||
if(this._pDb){
|
||||
let s;
|
||||
const that = this;
|
||||
@ -430,9 +456,15 @@ Module.postRun.push(function(namespace){
|
||||
Object.values(this._udfs).forEach(SQM.removeFunction);
|
||||
delete this._udfs;
|
||||
delete this._statements;
|
||||
delete this.filename;
|
||||
api.sqlite3_close_v2(this._pDb);
|
||||
delete this._pDb;
|
||||
if(this.filename){
|
||||
if(alsoUnlink){
|
||||
try{SQM.FS.unlink('/'+this.filename);}
|
||||
catch(e){/*ignored*/}
|
||||
}
|
||||
delete this.filename;
|
||||
}
|
||||
}
|
||||
},
|
||||
/**
|
||||
@ -461,16 +493,38 @@ Module.postRun.push(function(namespace){
|
||||
return stmt;
|
||||
},
|
||||
/**
|
||||
This function works like execMulti(), and takes the same
|
||||
arguments, but is more efficient (performs much less work)
|
||||
when the input SQL is only a single statement. If passed a
|
||||
multi-statement SQL, it only processes the first one.
|
||||
This function works like execMulti(), and takes most of the
|
||||
same arguments, but is more efficient (performs much less
|
||||
work) when the input SQL is only a single statement. If
|
||||
passed a multi-statement SQL, it only processes the first
|
||||
one.
|
||||
|
||||
This function supports one additional option not used by
|
||||
execMulti():
|
||||
This function supports the following additional options not
|
||||
supported by execMulti():
|
||||
|
||||
- .multi: if true, this function acts as a proxy for
|
||||
execMulti().
|
||||
execMulti() and behaves identically to that function.
|
||||
|
||||
- .resultRows: if this is an array, each row of the result
|
||||
set (if any) is appended to it in the format specified
|
||||
for the `rowMode` property, with the exception that the
|
||||
only legal values for `rowMode` in this case are 'array'
|
||||
or 'object', neither of which is the default. It is legal
|
||||
to use both `resultRows` and `callback`, but `resultRows`
|
||||
is likely much simpler to use for small data sets and can
|
||||
be used over a WebWorker-style message interface.
|
||||
|
||||
- .columnNames: if this is an array and the query has
|
||||
result columns, the array is passed to
|
||||
Stmt.getColumnNames() to append the column names to it
|
||||
(regardless of whether the query produces any result
|
||||
rows). If the query has no result columns, this value is
|
||||
unchanged.
|
||||
|
||||
The following options to execMulti() are _not_ supported by
|
||||
this method (they are simply ignored):
|
||||
|
||||
- .saveSql
|
||||
*/
|
||||
exec: function(/*(sql [,optionsObj]) or (optionsObj)*/){
|
||||
affirmDbOpen(this);
|
||||
@ -480,15 +534,29 @@ Module.postRun.push(function(namespace){
|
||||
return this.execMulti(arg, undefined, BindTypes);
|
||||
}
|
||||
const opt = arg.opt;
|
||||
let stmt;
|
||||
let stmt, rowTarget;
|
||||
try {
|
||||
if(Array.isArray(opt.resultRows)){
|
||||
if(opt.rowMode!=='array' && opt.rowMode!=='object'){
|
||||
toss("Invalid rowMode for resultRows array: must",
|
||||
"be one of 'array' or 'object'.");
|
||||
}
|
||||
rowTarget = opt.resultRows;
|
||||
}
|
||||
stmt = this.prepare(arg.sql);
|
||||
if(stmt.columnCount && Array.isArray(opt.columnNames)){
|
||||
stmt.getColumnNames(opt.columnNames);
|
||||
}
|
||||
if(opt.bind) stmt.bind(opt.bind);
|
||||
if(opt.callback){
|
||||
if(opt.callback || rowTarget){
|
||||
while(stmt.step()){
|
||||
stmt._isLocked = true;
|
||||
opt.callback(arg.cbArg(stmt), stmt);
|
||||
stmt._isLocked = false;
|
||||
const row = arg.cbArg(stmt);
|
||||
if(rowTarget) rowTarget.push(row);
|
||||
if(opt.callback){
|
||||
stmt._isLocked = true;
|
||||
opt.callback(row, stmt);
|
||||
stmt._isLocked = false;
|
||||
}
|
||||
}
|
||||
}else{
|
||||
stmt.step();
|
||||
@ -503,10 +571,11 @@ Module.postRun.push(function(namespace){
|
||||
|
||||
}/*exec()*/,
|
||||
/**
|
||||
Executes one or more SQL statements. Its arguments
|
||||
must be either (sql,optionsObject) or (optionsObject).
|
||||
In the latter case, optionsObject.sql must contain the
|
||||
SQL to execute. Returns this object. Throws on error.
|
||||
Executes one or more SQL statements in the form of a single
|
||||
string. Its arguments must be either (sql,optionsObject) or
|
||||
(optionsObject). In the latter case, optionsObject.sql
|
||||
must contain the SQL to execute. Returns this
|
||||
object. Throws on error.
|
||||
|
||||
If no SQL is provided, or a non-string is provided, an
|
||||
exception is triggered. Empty SQL, on the other hand, is
|
||||
@ -547,15 +616,20 @@ Module.postRun.push(function(namespace){
|
||||
don't have the string until after that). Empty SQL
|
||||
statements are elided.
|
||||
|
||||
See also the exec() method, which is a close cousin of this
|
||||
one.
|
||||
|
||||
ACHTUNG #1: The callback MUST NOT modify the Stmt
|
||||
object. Calling any of the Stmt.get() variants,
|
||||
Stmt.getColumnName(), or simililar, is legal, but calling
|
||||
Stmt.getColumnName(), or similar, is legal, but calling
|
||||
step() or finalize() is not. Routines which are illegal
|
||||
in this context will trigger an exception.
|
||||
|
||||
ACHTUNG #2: The semantics of the `bind` and `callback`
|
||||
options may well change or those options may be removed
|
||||
altogether for this function (but retained for exec()).
|
||||
Generally speaking, neither bind parameters nor a callback
|
||||
are generically useful when executing multi-statement SQL.
|
||||
*/
|
||||
execMulti: function(/*(sql [,obj]) || (obj)*/){
|
||||
affirmDbOpen(this);
|
||||
@ -803,8 +877,13 @@ Module.postRun.push(function(namespace){
|
||||
/**
|
||||
Exports a copy of this db's file as a Uint8Array and
|
||||
returns it. It is technically not legal to call this while
|
||||
any prepared statement are currently active. Throws if this
|
||||
db is not open.
|
||||
any prepared statement are currently active because,
|
||||
depending on the platform, it might not be legal to read
|
||||
the db while a statement is locking it. Throws if this db
|
||||
is not open or has any opened statements.
|
||||
|
||||
The resulting buffer can be passed to this class's
|
||||
constructor to restore the DB.
|
||||
|
||||
Maintenance reminder: the corresponding sql.js impl of this
|
||||
feature closes the current db, finalizing any active
|
||||
@ -822,8 +901,7 @@ Module.postRun.push(function(namespace){
|
||||
toss("Cannot export with prepared statements active!",
|
||||
"finalize() all statements and try again.");
|
||||
}
|
||||
const img = FS.readFile(this.filename, {encoding:"binary"});
|
||||
return img;
|
||||
return FS.readFile(this.filename, {encoding:"binary"});
|
||||
}
|
||||
}/*DB.prototype*/;
|
||||
|
||||
@ -1248,6 +1326,13 @@ Module.postRun.push(function(namespace){
|
||||
const ptr = api.sqlite3_column_blob(this._pStmt, ndx);
|
||||
const rc = new Uint8Array(n);
|
||||
for(let i = 0; i < n; ++i) rc[i] = HEAP8[ptr + i];
|
||||
if(n && this.db._blobXfer instanceof Array){
|
||||
/* This is an optimization soley for the
|
||||
Worker-based API. These values will be
|
||||
transfered to the main thread directly
|
||||
instead of being copied. */
|
||||
this.db._blobXfer.push(rc.buffer);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
default: toss("Don't know how to translate",
|
||||
@ -1327,8 +1412,8 @@ Module.postRun.push(function(namespace){
|
||||
DB,
|
||||
Stmt,
|
||||
/**
|
||||
Reports whether a given compile-time option, named by the
|
||||
given argument. It has several distinct uses:
|
||||
Reports info about compile-time options. It has several
|
||||
distinct uses:
|
||||
|
||||
If optName is an array then it is expected to be a list of
|
||||
compilation options and this function returns an object
|
||||
@ -1387,10 +1472,310 @@ Module.postRun.push(function(namespace){
|
||||
'string'===typeof optName
|
||||
) ? !!api.sqlite3_compileoption_used(optName) : false;
|
||||
}
|
||||
};
|
||||
}/*SQLite3 object*/;
|
||||
|
||||
namespace.sqlite3 = {
|
||||
api: api,
|
||||
SQLite3
|
||||
};
|
||||
|
||||
if(self === self.window){
|
||||
/* This is running in the main window thread, so we're done. */
|
||||
setTimeout(()=>postMessage({type:'sqlite3-api',data:'loaded'}), 0);
|
||||
return;
|
||||
}
|
||||
/******************************************************************
|
||||
End of main window thread. What follows is only intended for use
|
||||
in Worker threads.
|
||||
******************************************************************/
|
||||
|
||||
/*
|
||||
UNDER CONSTRUCTION
|
||||
|
||||
We need an API which can proxy the DB API via a Worker message
|
||||
interface. The primary quirky factor in such an API is that we
|
||||
cannot pass callback functions between the window thread and a
|
||||
worker thread, so we have to receive all db results via
|
||||
asynchronous message-passing.
|
||||
|
||||
Certain important considerations here include:
|
||||
|
||||
- Support only one db connection or multiple? The former is far
|
||||
easier, but there's always going to be a user out there who
|
||||
wants to juggle six database handles at once. Do we add that
|
||||
complexity or tell such users to write their own code using
|
||||
the provided lower-level APIs?
|
||||
|
||||
- Fetching multiple results: do we pass them on as a series of
|
||||
messages, with start/end messages on either end, or do we
|
||||
collect all results and bundle them back in a single message?
|
||||
The former is, generically speaking, more memory-efficient but
|
||||
the latter far easier to implement in this environment. The
|
||||
latter is untennable for large data sets. Despite a web page
|
||||
hypothetically being a relatively limited environment, there
|
||||
will always be those users who feel that they should/need to
|
||||
be able to work with multi-hundred-meg (or larger) blobs, and
|
||||
passing around arrays of those may quickly exhaust the JS
|
||||
engine's memory.
|
||||
|
||||
TODOs include, but are not limited to:
|
||||
|
||||
- The ability to manage multiple DB handles. This can
|
||||
potentially be done via a simple mapping of DB.filename or
|
||||
DB._pDb (`sqlite3*` handle) to DB objects. The open()
|
||||
interface would need to provide an ID (probably DB._pDb) back
|
||||
to the user which can optionally be passed as an argument to
|
||||
the other APIs (they'd default to the first-opened DB, for
|
||||
ease of use). Client-side usability of this feature would
|
||||
benefit from making another wrapper class (or a singleton)
|
||||
available to the main thread, with that object proxying all(?)
|
||||
communication with the worker.
|
||||
|
||||
- Revisit how virtual files are managed. We currently delete DBs
|
||||
from the virtual filesystem when we close them, for the sake
|
||||
of saving memory (the VFS lives in RAM). Supporting multiple
|
||||
DBs may require that we give up that habit. Similarly, fully
|
||||
supporting ATTACH, where a user can upload multiple DBs and
|
||||
ATTACH them, also requires the that we manage the VFS entries
|
||||
better. As of this writing, ATTACH will fail fatally in the
|
||||
fiddle app (but not the lower-level APIs) because it runs in
|
||||
safe mode, where ATTACH is disabled.
|
||||
*/
|
||||
|
||||
/**
|
||||
Helper for managing Worker-level state.
|
||||
*/
|
||||
const wState = {
|
||||
db: undefined,
|
||||
open: function(arg){
|
||||
if(!arg && this.db) return this.db;
|
||||
else if(this.db) this.db.close();
|
||||
return this.db = (Array.isArray(arg) ? new DB(...arg) : new DB(arg));
|
||||
},
|
||||
close: function(alsoUnlink){
|
||||
if(this.db){
|
||||
this.db.close(alsoUnlink);
|
||||
this.db = undefined;
|
||||
}
|
||||
},
|
||||
affirmOpen: function(){
|
||||
return this.db || toss("DB is not opened.");
|
||||
},
|
||||
post: function(type,data,xferList){
|
||||
if(xferList){
|
||||
self.postMessage({type, data},xferList);
|
||||
xferList.length = 0;
|
||||
}else{
|
||||
self.postMessage({type, data});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
A level of "organizational abstraction" for the Worker
|
||||
API. Each method in this object must map directly to a Worker
|
||||
message type key. The onmessage() dispatcher attempts to
|
||||
dispatch all inbound messages to a method of this object,
|
||||
passing it the event.data part of the inbound event object. All
|
||||
methods must return a plain Object containing any response
|
||||
state, which the dispatcher may amend. All methods must throw
|
||||
on error.
|
||||
*/
|
||||
const wMsgHandler = {
|
||||
xfer: [/*Temp holder for "transferable" postMessage() state.*/],
|
||||
/**
|
||||
Proxy for DB.exec() which expects a single argument of type
|
||||
string (SQL to execute) or an options object in the form
|
||||
expected by exec(). The notable differences from exec()
|
||||
include:
|
||||
|
||||
- The default value for options.rowMode is 'array' because
|
||||
the normal default cannot cross the window/Worker boundary.
|
||||
|
||||
- A function-type options.callback property cannot cross
|
||||
the window/Worker boundary, so is not useful here. If
|
||||
options.callback is a string then it is assumed to be a
|
||||
message type key, in which case a callback function will be
|
||||
applied which posts each row result via:
|
||||
|
||||
postMessage({type: thatKeyType, data: theRow})
|
||||
|
||||
And, at the end of the result set (whether or not any
|
||||
result rows were produced), it will post an identical
|
||||
message with data:null to alert the caller than the result
|
||||
set is completed.
|
||||
|
||||
The callback proxy must not recurse into this interface, or
|
||||
results are undefined. (It hypothetically cannot recurse
|
||||
because an exec() call will be tying up the Worker thread,
|
||||
causing any recursion attempt to wait until the first
|
||||
exec() is completed.)
|
||||
|
||||
The response is the input options object (or a synthesized
|
||||
one if passed only a string), noting that
|
||||
options.resultRows and options.columnNames may be populated
|
||||
by the call to exec().
|
||||
|
||||
This opens/creates the Worker's db if needed.
|
||||
*/
|
||||
exec: function(ev){
|
||||
const opt = (
|
||||
'string'===typeof ev.data
|
||||
) ? {sql: ev.data} : (ev.data || {});
|
||||
if(!opt.rowMode){
|
||||
/* Since the default rowMode of 'stmt' is not useful
|
||||
for the Worker interface, we'll default to
|
||||
something else. */
|
||||
opt.rowMode = 'array';
|
||||
}else if('stmt'===opt.rowMode){
|
||||
toss("Invalid rowMode for exec(): stmt mode",
|
||||
"does not work in the Worker API.");
|
||||
}
|
||||
const db = wState.open();
|
||||
if(opt.callback || opt.resultRows instanceof Array){
|
||||
// Part of a copy-avoidance optimization for blobs
|
||||
db._blobXfer = this.xfer;
|
||||
}
|
||||
const callbackMsgType = opt.callback;
|
||||
if('string' === typeof callbackMsgType){
|
||||
const that = this;
|
||||
opt.callback =
|
||||
(row)=>wState.post(callbackMsgType,row,this.xfer);
|
||||
}
|
||||
try {
|
||||
db.exec(opt);
|
||||
if(opt.callback instanceof Function){
|
||||
opt.callback = callbackMsgType;
|
||||
wState.post(callbackMsgType, null);
|
||||
}
|
||||
}finally{
|
||||
delete db._blobXfer;
|
||||
if('string'===typeof callbackMsgType){
|
||||
opt.callback = callbackMsgType;
|
||||
}
|
||||
}
|
||||
return opt;
|
||||
}/*exec()*/,
|
||||
/**
|
||||
Proxy for DB.exportBinaryImage(). Throws if the db has not
|
||||
been opened. Response is an object:
|
||||
|
||||
{
|
||||
buffer: Uint8Array (db file contents),
|
||||
filename: the current db filename,
|
||||
mimetype: string
|
||||
}
|
||||
*/
|
||||
export: function(ev){
|
||||
const db = wState.affirmOpen();
|
||||
const response = {
|
||||
buffer: db.exportBinaryImage(),
|
||||
filename: db.filename,
|
||||
mimetype: 'application/x-sqlite3'
|
||||
};
|
||||
this.xfer.push(response.buffer.buffer);
|
||||
return response;
|
||||
}/*export()*/,
|
||||
/**
|
||||
Proxy for the DB constructor. Expects to be passed a single
|
||||
object or a falsy value to use defaults. The object may
|
||||
have a filename property to name the db file (see the DB
|
||||
constructor for peculiarities and transformations) and/or a
|
||||
buffer property (a Uint8Array holding a complete database
|
||||
file's contents). The response is an object:
|
||||
|
||||
{
|
||||
filename: db filename (possibly differing from the input)
|
||||
}
|
||||
|
||||
If the Worker's db is currently opened, this call closes it
|
||||
before proceeding.
|
||||
*/
|
||||
open: function(ev){
|
||||
wState.close(/*true???*/);
|
||||
const args = [], data = (ev.data || {});
|
||||
if(data.filename) args.push(data.filename);
|
||||
if(data.buffer){
|
||||
args.push(data.buffer);
|
||||
this.xfer.push(data.buffer.buffer);
|
||||
}
|
||||
const db = wState.open(args);
|
||||
return {filename: db.filename};
|
||||
},
|
||||
/**
|
||||
Proxy for DB.close(). If ev.data may either be a boolean or
|
||||
an object with an `unlink` property. If that value is
|
||||
truthy then the db file (if the db is currently open) will
|
||||
be unlinked from the virtual filesystem, else it will be
|
||||
kept intact. The response object is:
|
||||
|
||||
{filename: db filename _if_ the db is is opened when this
|
||||
is called, else the undefined value
|
||||
}
|
||||
*/
|
||||
close: function(ev){
|
||||
const response = {
|
||||
filename: wState.db && wState.db.filename
|
||||
};
|
||||
if(wState.db){
|
||||
wState.close(!!(ev.data && 'object'===typeof ev.data)
|
||||
? ev.data.unlink : ev.data);
|
||||
}
|
||||
return response;
|
||||
}
|
||||
}/*wMsgHandler*/;
|
||||
|
||||
/**
|
||||
UNDER CONSTRUCTION!
|
||||
|
||||
A subset of the DB API is accessible via Worker messages in the form:
|
||||
|
||||
{ type: apiCommand,
|
||||
data: apiArguments }
|
||||
|
||||
As a rule, these commands respond with a postMessage() of their
|
||||
own in the same form, but will, if needed, transform the `data`
|
||||
member to an object and may add state to it. The responses
|
||||
always have an object-format `data` part. If the inbound `data`
|
||||
is an object which has a `messageId` property, that property is
|
||||
always mirrored in the result object, for use in client-side
|
||||
dispatching of these asynchronous results. Exceptions thrown
|
||||
during processing result in an `error`-type event with a
|
||||
payload in the form:
|
||||
|
||||
{
|
||||
message: error string,
|
||||
errorClass: class name of the error type,
|
||||
input: ev.data,
|
||||
[messageId: if set in the inbound message]
|
||||
}
|
||||
|
||||
The individual APIs are documented in the wMsgHandler object.
|
||||
*/
|
||||
self.onmessage = function(ev){
|
||||
ev = ev.data;
|
||||
let response, evType = ev.type;
|
||||
try {
|
||||
if(wMsgHandler.hasOwnProperty(evType) &&
|
||||
wMsgHandler[evType] instanceof Function){
|
||||
response = wMsgHandler[evType](ev);
|
||||
}else{
|
||||
toss("Unknown db worker message type:",ev.type);
|
||||
}
|
||||
}catch(err){
|
||||
evType = 'error';
|
||||
response = {
|
||||
message: err.message,
|
||||
errorClass: err.name,
|
||||
input: ev
|
||||
};
|
||||
}
|
||||
if(!response.messageId && ev.data
|
||||
&& 'object'===typeof ev.data && ev.data.messageId){
|
||||
response.messageId = ev.data.messageId;
|
||||
}
|
||||
wState.post(evType, response, wMsgHandler.xfer);
|
||||
};
|
||||
|
||||
setTimeout(()=>postMessage({type:'sqlite3-api',data:'loaded'}), 0);
|
||||
});
|
||||
|
44
ext/fiddle/sqlite3-worker.js
Normal file
44
ext/fiddle/sqlite3-worker.js
Normal file
@ -0,0 +1,44 @@
|
||||
/*
|
||||
2022-05-23
|
||||
|
||||
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 is a JS Worker file for the main sqlite3 api. It loads
|
||||
sqlite3.js, initializes the module, and postMessage()'s a message
|
||||
after the module is initialized:
|
||||
|
||||
{type: 'sqlite3-api', data: 'ready'}
|
||||
|
||||
This seemingly superfluous level of indirection is necessary when
|
||||
loading sqlite3.js via a Worker. Loading sqlite3.js from the main
|
||||
window thread elides the Worker-specific API. Instantiating a worker
|
||||
with new Worker("sqlite.js") will not (cannot) call
|
||||
initSqlite3Module() to initialize the module due to a
|
||||
timing/order-of-operations conflict (and that symbol is not exported
|
||||
in a way that a Worker loading it that way can see it). Thus JS
|
||||
code wanting to load the sqlite3 Worker-specific API needs to pass
|
||||
_this_ file (or equivalent) to the Worker constructor and then
|
||||
listen for an event in the form shown above in order to know when
|
||||
the module has completed initialization. sqlite3.js will fire a
|
||||
similar event, with data:'loaded' as the final step in its loading
|
||||
process. Whether or not we _really_ need both 'loaded' and 'ready'
|
||||
events is unclear, but they are currently separate events primarily
|
||||
for the sake of clarity in the timing of when it's okay to use the
|
||||
loaded module. At the time the 'loaded' event is fired, it's
|
||||
possible (but unknown and unknowable) that the emscripten-generated
|
||||
module-setup infrastructure still has work to do. Thus it is
|
||||
hypothesized that client code is better off waiting for the 'ready'
|
||||
even before using the API.
|
||||
*/
|
||||
"use strict";
|
||||
importScripts('sqlite3.js');
|
||||
initSqlite3Module().then(function(){
|
||||
setTimeout(()=>self.postMessage({type:'sqlite3-api',data:'ready'}), 0);
|
||||
});
|
@ -10,7 +10,8 @@
|
||||
|
||||
***********************************************************************
|
||||
|
||||
A basic test script for sqlite3-api.js.
|
||||
A basic test script for sqlite3-api.js. This file must be run in
|
||||
main JS thread and sqlite3.js must have been loaded before it.
|
||||
*/
|
||||
(function(){
|
||||
const T = self.SqliteTestUtil;
|
||||
@ -70,23 +71,27 @@ INSERT INTO t(a,b) VALUES(1,2),(3,4),(?,?);`,
|
||||
T.assert(2 === list.length);
|
||||
//log("Exec'd SQL:", list);
|
||||
let counter = 0, colNames = [];
|
||||
list.length = 0;
|
||||
db.exec("SELECT a a, b b FROM t",{
|
||||
rowMode: 'object',
|
||||
resultRows: list,
|
||||
columnNames: colNames,
|
||||
callback: function(row,stmt){
|
||||
if(!counter) stmt.getColumnNames(colNames);
|
||||
++counter;
|
||||
T.assert(row.a%2 && row.a<6);
|
||||
}
|
||||
});
|
||||
assert(2 === colNames.length);
|
||||
assert('a' === colNames[0]);
|
||||
T.assert(3 === counter);
|
||||
T.assert(2 === colNames.length)
|
||||
.assert('a' === colNames[0])
|
||||
.assert(3 === counter)
|
||||
.assert(3 === list.length);
|
||||
list.length = 0;
|
||||
db.exec("SELECT a a, b b FROM t",{
|
||||
rowMode: 'array',
|
||||
callback: function(row,stmt){
|
||||
++counter;
|
||||
assert(Array.isArray(row));
|
||||
T.assert(0===row[1]%2 && row[1]<7);
|
||||
T.assert(Array.isArray(row))
|
||||
.assert(0===row[1]%2 && row[1]<7);
|
||||
}
|
||||
});
|
||||
T.assert(6 === counter);
|
||||
@ -114,11 +119,32 @@ INSERT INTO t(a,b) VALUES(1,2),(3,4),(?,?);`,
|
||||
assert(3===db.selectValue("select bar(1,2)")).
|
||||
assert(-1===db.selectValue("select bar(1,2,-4)"));
|
||||
|
||||
const eqApprox = function(v1,v2,factor=0.05){
|
||||
return v1>=(v2-factor) && v1<=(v2+factor);
|
||||
};
|
||||
|
||||
T.assert('hi' === db.selectValue("select ?",'hi')).
|
||||
assert(null===db.selectValue("select null")).
|
||||
assert(null === db.selectValue("select ?",null)).
|
||||
assert(null === db.selectValue("select ?",[null])).
|
||||
assert(null === db.selectValue("select $a",{$a:null}));
|
||||
assert(null === db.selectValue("select $a",{$a:null})).
|
||||
assert(eqApprox(3.1,db.selectValue("select 3.0 + 0.1")))
|
||||
;
|
||||
};
|
||||
|
||||
const testAttach = function(db){
|
||||
log("Testing ATTACH...");
|
||||
db.exec({
|
||||
sql:[
|
||||
"attach 'foo.db' as foo",
|
||||
"create table foo.bar(a)",
|
||||
"insert into foo.bar(a) values(1),(2),(3)"
|
||||
].join(';'),
|
||||
multi: true
|
||||
});
|
||||
T.assert(2===db.selectValue('select a from foo.bar where a>1 order by a'));
|
||||
db.exec("detach foo");
|
||||
T.mustThrow(()=>db.exec("select * from foo.bar"));
|
||||
};
|
||||
|
||||
const runTests = function(Module){
|
||||
@ -136,8 +162,12 @@ INSERT INTO t(a,b) VALUES(1,2),(3,4),(?,?);`,
|
||||
try {
|
||||
log("DB:",db.filename);
|
||||
[
|
||||
test1, testUDF
|
||||
].forEach((f)=>f(db, sqlite3));
|
||||
test1, testUDF, testAttach
|
||||
].forEach((f)=>{
|
||||
const t = T.counter;
|
||||
f(db, sqlite3);
|
||||
log("Test count:",T.counter - t);
|
||||
});
|
||||
}finally{
|
||||
db.close();
|
||||
}
|
||||
|
32
ext/fiddle/testing2.html
Normal file
32
ext/fiddle/testing2.html
Normal file
@ -0,0 +1,32 @@
|
||||
<!doctype html>
|
||||
<html lang="en-us">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
<link rel="shortcut icon" href="data:image/x-icon;," type="image/x-icon">
|
||||
<link rel="stylesheet" href="emscripten.css"/>
|
||||
<link rel="stylesheet" href="testing.css"/>
|
||||
<title>sqlite3-worker.js tests</title>
|
||||
<style></style>
|
||||
</head>
|
||||
<body>
|
||||
<header id='titlebar'><span>sqlite3-worker.js tests</span></header>
|
||||
<!-- emscripten bits -->
|
||||
<figure id="module-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="module-status">Downloading...</div>
|
||||
<div class="emscripten">
|
||||
<progress value="0" max="100" id="module-progress" hidden='1'></progress>
|
||||
</div><!-- /emscripten bits -->
|
||||
<div>Everything on this page happens in the dev console.</div>
|
||||
<script src="testing-common.js"></script>
|
||||
<script src="testing2.js"></script>
|
||||
</body>
|
||||
</html>
|
235
ext/fiddle/testing2.js
Normal file
235
ext/fiddle/testing2.js
Normal file
@ -0,0 +1,235 @@
|
||||
/*
|
||||
2022-05-22
|
||||
|
||||
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.
|
||||
|
||||
***********************************************************************
|
||||
|
||||
A basic test script for sqlite3-worker.js.
|
||||
*/
|
||||
(function(){
|
||||
const T = self.SqliteTestUtil;
|
||||
const SW = new Worker("sqlite3-worker.js");
|
||||
/** Posts a worker message as {type:type, data:data}. */
|
||||
const wMsg = function(type,data){
|
||||
SW.postMessage({type, data});
|
||||
return SW;
|
||||
};
|
||||
const log = console.log.bind(console);
|
||||
const warn = console.warn.bind(console);
|
||||
const error = console.error.bind(console);
|
||||
|
||||
SW.onerror = function(event){
|
||||
error("onerror",event);
|
||||
};
|
||||
|
||||
/**
|
||||
A queue for callbacks which are to be run in response to async
|
||||
DB commands. See the notes in runTests() for why we need
|
||||
this. The event-handling plumbing of this file requires that
|
||||
any DB command which includes a `messageId` property also have
|
||||
a queued callback entry, as the existence of that property in
|
||||
response payloads is how it knows whether or not to shift an
|
||||
entry off of the queue.
|
||||
*/
|
||||
const MsgHandlerQueue = {
|
||||
queue: [],
|
||||
id: 0,
|
||||
push: function(type,callback){
|
||||
this.queue.push(callback);
|
||||
return type + '-' + (++this.id);
|
||||
},
|
||||
shift: function(){
|
||||
return this.queue.shift();
|
||||
}
|
||||
};
|
||||
|
||||
const testCount = ()=>log("Total test count:",T.counter);
|
||||
|
||||
const runOneTest = function(eventType, eventData, callback){
|
||||
T.assert(eventData && 'object'===typeof eventData);
|
||||
/* ^^^ that is for the testing and messageId-related code, not
|
||||
a hard requirement of all of the Worker-exposed APIs. */
|
||||
eventData.messageId = MsgHandlerQueue.push(eventType,function(ev){
|
||||
log("runOneTest",eventType,"result",ev.data);
|
||||
if(callback instanceof Function){
|
||||
callback(ev);
|
||||
testCount();
|
||||
}
|
||||
});
|
||||
wMsg(eventType, eventData);
|
||||
};
|
||||
|
||||
/** Methods which map directly to onmessage() event.type keys.
|
||||
They get passed the inbound event.data. */
|
||||
const dbMsgHandler = {
|
||||
open: function(ev){
|
||||
log("open result",ev.data);
|
||||
},
|
||||
exec: function(ev){
|
||||
log("exec result",ev.data);
|
||||
},
|
||||
export: function(ev){
|
||||
log("exec result",ev.data);
|
||||
},
|
||||
error: function(ev){
|
||||
error("ERROR from the worker:",ev.data);
|
||||
},
|
||||
resultRowTest1: function f(ev){
|
||||
if(undefined === f.counter) f.counter = 0;
|
||||
if(ev.data) ++f.counter;
|
||||
//log("exec() result row:",ev.data);
|
||||
T.assert(null===ev.data || 'number' === typeof ev.data.b);
|
||||
}
|
||||
};
|
||||
|
||||
const runTests = function(){
|
||||
const mustNotReach = ()=>{
|
||||
throw new Error("This is not supposed to be reached.");
|
||||
};
|
||||
/**
|
||||
"The problem" now is that the test results are async. We
|
||||
know, however, that the messages posted to the worker will
|
||||
be processed in the order they are passed to it, so we can
|
||||
create a queue of callbacks to handle them. The problem
|
||||
with that approach is that it's not error-handling
|
||||
friendly, in that an error can cause us to bypass a result
|
||||
handler queue entry. We have to perform some extra
|
||||
acrobatics to account for that.
|
||||
*/
|
||||
runOneTest('open', {filename:'testing2.sqlite3'}, function(ev){
|
||||
//log("open result",ev);
|
||||
T.assert('testing2.sqlite3'===ev.data.filename)
|
||||
.assert(ev.data.messageId);
|
||||
});
|
||||
runOneTest('exec',{
|
||||
sql: ["create table t(a,b)",
|
||||
"insert into t(a,b) values(1,2),(3,4),(5,6)"
|
||||
].join(';'),
|
||||
multi: true,
|
||||
resultRows: [], columnNames: []
|
||||
}, function(ev){
|
||||
ev = ev.data;
|
||||
T.assert(0===ev.resultRows.length)
|
||||
.assert(0===ev.columnNames.length);
|
||||
});
|
||||
runOneTest('exec',{
|
||||
sql: 'select a a, b b from t order by a',
|
||||
resultRows: [], columnNames: [],
|
||||
}, function(ev){
|
||||
ev = ev.data;
|
||||
T.assert(3===ev.resultRows.length)
|
||||
.assert(1===ev.resultRows[0][0])
|
||||
.assert(6===ev.resultRows[2][1])
|
||||
.assert(2===ev.columnNames.length)
|
||||
.assert('b'===ev.columnNames[1]);
|
||||
});
|
||||
runOneTest('exec',{
|
||||
sql: 'select a a, b b from t order by a',
|
||||
resultRows: [], columnNames: [],
|
||||
rowMode: 'object'
|
||||
}, function(ev){
|
||||
ev = ev.data;
|
||||
T.assert(3===ev.resultRows.length)
|
||||
.assert(1===ev.resultRows[0].a)
|
||||
.assert(6===ev.resultRows[2].b)
|
||||
});
|
||||
runOneTest('exec',{sql:'intentional_error'}, mustNotReach);
|
||||
// Ensure that the message-handler queue survives ^^^ that error...
|
||||
runOneTest('exec',{
|
||||
sql:'select 1',
|
||||
resultRows: [],
|
||||
//rowMode: 'array', // array is the default in the Worker interface
|
||||
}, function(ev){
|
||||
ev = ev.data;
|
||||
T.assert(1 === ev.resultRows.length)
|
||||
.assert(1 === ev.resultRows[0][0]);
|
||||
});
|
||||
runOneTest('exec',{
|
||||
sql: 'select a a, b b from t order by a',
|
||||
callback: 'resultRowTest1',
|
||||
rowMode: 'object'
|
||||
}, function(ev){
|
||||
T.assert(3===dbMsgHandler.resultRowTest1.counter);
|
||||
dbMsgHandler.resultRowTest1.counter = 0;
|
||||
});
|
||||
runOneTest('exec',{sql: 'delete from t where a>3'});
|
||||
runOneTest('exec',{
|
||||
sql: 'select count(a) from t',
|
||||
resultRows: []
|
||||
},function(ev){
|
||||
ev = ev.data;
|
||||
T.assert(1===ev.resultRows.length)
|
||||
.assert(2===ev.resultRows[0][0]);
|
||||
});
|
||||
runOneTest('export',{}, function(ev){
|
||||
ev = ev.data;
|
||||
T.assert('string' === typeof ev.filename)
|
||||
.assert(ev.buffer instanceof Uint8Array)
|
||||
.assert(ev.buffer.length > 1024)
|
||||
.assert('application/x-sqlite3' === ev.mimetype);
|
||||
});
|
||||
|
||||
/***** close() tests must come last. *****/
|
||||
runOneTest('close',{unlink:true},function(ev){
|
||||
ev = ev.data;
|
||||
T.assert('string' === typeof ev.filename);
|
||||
});
|
||||
runOneTest('close',{unlink:true},function(ev){
|
||||
ev = ev.data;
|
||||
T.assert(undefined === ev.filename);
|
||||
});
|
||||
};
|
||||
|
||||
SW.onmessage = function(ev){
|
||||
if(!ev.data || 'object'!==typeof ev.data){
|
||||
warn("Unknown sqlite3-worker message type:",ev);
|
||||
return;
|
||||
}
|
||||
ev = ev.data/*expecting a nested object*/;
|
||||
//log("main window onmessage:",ev);
|
||||
if(ev.data && ev.data.messageId){
|
||||
/* We're expecting a queued-up callback handler. */
|
||||
const f = MsgHandlerQueue.shift();
|
||||
if('error'===ev.type){
|
||||
dbMsgHandler.error(ev);
|
||||
return;
|
||||
}
|
||||
T.assert(f instanceof Function);
|
||||
f(ev);
|
||||
return;
|
||||
}
|
||||
switch(ev.type){
|
||||
case 'sqlite3-api':
|
||||
switch(ev.data){
|
||||
case 'loaded':
|
||||
log("Message:",ev); return;
|
||||
case 'ready':
|
||||
log("Message:",ev);
|
||||
self.sqlite3TestModule.setStatus(null);
|
||||
setTimeout(runTests, 0);
|
||||
return;
|
||||
default:
|
||||
warn("Unknown sqlite3-api message type:",ev);
|
||||
return;
|
||||
}
|
||||
default:
|
||||
if(dbMsgHandler.hasOwnProperty(ev.type)){
|
||||
try{dbMsgHandler[ev.type](ev);}
|
||||
catch(err){
|
||||
error("Exception while handling db result message",
|
||||
ev,":",err);
|
||||
}
|
||||
return;
|
||||
}
|
||||
warn("Unknown sqlite3-api message type:",ev);
|
||||
}
|
||||
};
|
||||
|
||||
log("Init complete, but async init bits may still be running.");
|
||||
})();
|
@ -3899,6 +3899,8 @@ static int fts3IncrmergePush(
|
||||
pBlk->n += sqlite3Fts3PutVarint(&pBlk->a[pBlk->n], nPrefix);
|
||||
}
|
||||
pBlk->n += sqlite3Fts3PutVarint(&pBlk->a[pBlk->n], nSuffix);
|
||||
assert( nPrefix+nSuffix<=nTerm );
|
||||
assert( nPrefix>=0 );
|
||||
memcpy(&pBlk->a[pBlk->n], &zTerm[nPrefix], nSuffix);
|
||||
pBlk->n += nSuffix;
|
||||
|
||||
@ -4021,6 +4023,7 @@ static int fts3IncrmergeAppend(
|
||||
pLeaf = &pWriter->aNodeWriter[0];
|
||||
nPrefix = fts3PrefixCompress(pLeaf->key.a, pLeaf->key.n, zTerm, nTerm);
|
||||
nSuffix = nTerm - nPrefix;
|
||||
if(nSuffix<=0 ) return FTS_CORRUPT_VTAB;
|
||||
|
||||
nSpace = sqlite3Fts3VarintLen(nPrefix);
|
||||
nSpace += sqlite3Fts3VarintLen(nSuffix) + nSuffix;
|
||||
|
7
main.mk
7
main.mk
@ -547,6 +547,9 @@ FUZZCHECK_OPT += -DSQLITE_ENABLE_RTREE
|
||||
FUZZCHECK_OPT += -DSQLITE_ENABLE_GEOPOLY
|
||||
FUZZCHECK_OPT += -DSQLITE_ENABLE_DBSTAT_VTAB
|
||||
FUZZCHECK_OPT += -DSQLITE_ENABLE_BYTECODE_VTAB
|
||||
FUZZSRC += $(TOP)/test/fuzzcheck.c
|
||||
FUZZSRC += $(TOP)/test/ossfuzz.c
|
||||
FUZZSRC += $(TOP)/test/fuzzinvariants.c
|
||||
DBFUZZ_OPT =
|
||||
KV_OPT = -DSQLITE_THREADSAFE=0 -DSQLITE_DIRECT_OVERFLOW_READ
|
||||
ST_OPT = -DSQLITE_THREADSAFE=0
|
||||
@ -605,10 +608,10 @@ dbfuzz2$(EXE): $(TOP)/test/dbfuzz2.c sqlite3.c sqlite3.h
|
||||
$(TCCX) -I. -g -O0 -DSTANDALONE -o dbfuzz2$(EXE) \
|
||||
$(DBFUZZ2_OPTS) $(TOP)/test/dbfuzz2.c sqlite3.c $(TLIBS) $(THREADLIB)
|
||||
|
||||
fuzzcheck$(EXE): $(TOP)/test/fuzzcheck.c sqlite3.c sqlite3.h $(TOP)/test/ossfuzz.c
|
||||
fuzzcheck$(EXE): $(FUZZSRC) sqlite3.c sqlite3.h
|
||||
$(TCCX) -o fuzzcheck$(EXE) -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION \
|
||||
-DSQLITE_ENABLE_MEMSYS5 $(FUZZCHECK_OPT) -DSQLITE_OSS_FUZZ \
|
||||
$(TOP)/test/fuzzcheck.c $(TOP)/test/ossfuzz.c sqlite3.c $(TLIBS) $(THREADLIB)
|
||||
$(FUZZSRC) sqlite3.c $(TLIBS) $(THREADLIB)
|
||||
|
||||
ossshell$(EXE): $(TOP)/test/ossfuzz.c $(TOP)/test/ossshell.c sqlite3.c sqlite3.h
|
||||
$(TCCX) -o ossshell$(EXE) -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION \
|
||||
|
97
manifest
97
manifest
@ -1,11 +1,11 @@
|
||||
C Merge\sthe\slatest\strunk\senhancements\sinto\sthe\swal2\sbranch.
|
||||
D 2022-05-28T14:44:19.777
|
||||
D 2022-06-16T13:44:49.125
|
||||
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
|
||||
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
|
||||
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
|
||||
F Makefile.in 6e2350cc3bed7a467fbdaa3ce42e665627aa91c9344c464cd21e750b75280181
|
||||
F Makefile.in 915ed9176f23f867575c1bd4e2d4b8a60e4e61965538b52418508f1c226558c4
|
||||
F Makefile.linux-gcc f609543700659711fbd230eced1f01353117621dccae7b9fb70daa64236c5241
|
||||
F Makefile.msc f969c28231012e50ee07c5062ee1dd5bbfc00883d52573af44b4fd477ec63843
|
||||
F Makefile.msc 028810319c9b921358303610bc64b0b7689b23a5810435a56bc268d45bdbee3f
|
||||
F README.md 8b8df9ca852aeac4864eb1e400002633ee6db84065bd01b78c33817f97d31f5e
|
||||
F VERSION fa8e7d2d1cc962f9e14c6d410387cf75860ee139462763fda887c1be4261f824
|
||||
F aclocal.m4 a5c22d164aff7ed549d53a90fa56d56955281f50
|
||||
@ -59,17 +59,20 @@ F ext/expert/test_expert.c d56c194b769bdc90cf829a14c9ecbc1edca9c850b837a4d0b13be
|
||||
F ext/fiddle/EXPORTED_FUNCTIONS.fiddle 7fb73f7150ab79d83bb45a67d257553c905c78cd3d693101699243f36c5ae6c3
|
||||
F ext/fiddle/EXPORTED_FUNCTIONS.sqlite3-api 540b9dec63a3a62a256e2f030827848a92e9b9d9b6fa5c0188295a4a1c5382cd
|
||||
F ext/fiddle/EXPORTED_RUNTIME_METHODS b831017ba67ba993b34a27400cef2f6095bd6789c0fc4eba7e7a251c207be31c
|
||||
F ext/fiddle/Makefile de65d04bfb312e94dbd7a0e7d99fb126f0abc1db62f920159c4124b5a42347d8
|
||||
F ext/fiddle/Makefile e25d34a0e1324f771d64c09c592601b97219282011587e6ce410fa8acdedb913
|
||||
F ext/fiddle/SqliteTestUtil.js 559731c3e8e0de330ec7d292e6c1846566408caee6637acc8a119ac338a8781c
|
||||
F ext/fiddle/emscripten.css 3d253a6fdb8983a2ac983855bfbdd4b6fa1ff267c28d69513dd6ef1f289ada3f
|
||||
F ext/fiddle/fiddle-worker.js 3a19253dc026d1ad9064ee853f3c4da3385223ce4434dab1838837525d817371
|
||||
F ext/fiddle/fiddle.html 724f1cd4126616bc87f5871f78d3f7aaaf41e45c9728724627baab87e6af35f0
|
||||
F ext/fiddle/fiddle.js 5b456ed7085830cda2fc75a0801476174a978521949335f24bc4154d076dcd4d
|
||||
F ext/fiddle/fiddle-worker.js 88bc2193a6cb6a3f04d8911bed50a4401fe6f277de7a71ba833865ab64a1b4ae
|
||||
F ext/fiddle/fiddle.html 550c5aafce40bd218de9bf26192749f69f9b10bc379423ecd2e162bcef885c08
|
||||
F ext/fiddle/fiddle.js 812f9954cc7c4b191884ad171f36fcf2d0112d0a7ecfdf6087896833a0c079a8
|
||||
F ext/fiddle/index.md d9c1c308d8074341bc3b11d1d39073cd77754cb3ca9aeb949f23fdd8323d81cf
|
||||
F ext/fiddle/sqlite3-api.js 8500698d2163f4a25f8e5e6810ad826487342579d6a321d82b244dbc8e6f6db6
|
||||
F ext/fiddle/sqlite3-api.js ccf4bd0c1c5bbb3be3469573423d6c53991941bec497eac63e9f17ea13bf8952
|
||||
F ext/fiddle/sqlite3-worker.js a9c2b614beca187dbdd8c053ec2770cc61ec1ac9c0ec6398ceb49a79f705a421
|
||||
F ext/fiddle/testing.css 750572dded671d2cf142bbcb27af5542522ac08db128245d0b9fe410aa1d7f2a
|
||||
F ext/fiddle/testing1.html ea1f3be727f78e420007f823912c1a03b337ecbb8e79449abc2244ad4fe15d9a
|
||||
F ext/fiddle/testing1.js 94a7597955c8fdbd15839a70d9b8279bc690205dda65ff175f688f13bf315745
|
||||
F ext/fiddle/testing1.js e2fa02ac8adbd21c69bc50cfcb79bfc26af0d30a8d6b95ac473a17e0dc9de733
|
||||
F ext/fiddle/testing2.html 9063b2430ade2fe9da4e711addd1b51a2741cf0c7ebf6926472a5e5dd63c0bc4
|
||||
F ext/fiddle/testing2.js 7b45b4e7fddbd51dbaf89b6722c02758051b34bac5a98c11b569a7e7572f88ee
|
||||
F ext/fts1/README.txt 20ac73b006a70bcfd80069bdaf59214b6cf1db5e
|
||||
F ext/fts1/ft_hash.c 3927bd880e65329bdc6f506555b228b28924921b
|
||||
F ext/fts1/ft_hash.h 06df7bba40dadd19597aa400a875dbc2fed705ea
|
||||
@ -118,7 +121,7 @@ F ext/fts3/fts3_tokenizer.h 64c6ef6c5272c51ebe60fc607a896e84288fcbc3
|
||||
F ext/fts3/fts3_tokenizer1.c 5c98225a53705e5ee34824087478cf477bdb7004
|
||||
F ext/fts3/fts3_unicode.c de426ff05c1c2e7bce161cf6b706638419c3a1d9c2667de9cb9dc0458c18e226
|
||||
F ext/fts3/fts3_unicode2.c 416eb7e1e81142703520d284b768ca2751d40e31fa912cae24ba74860532bf0f
|
||||
F ext/fts3/fts3_write.c 3109c1a232da86474e196cc7db754445a354409f141e08cb11c846cdb17bdf31
|
||||
F ext/fts3/fts3_write.c 85279b980f99253c296006503a13f92957ec49b716123083f021acc74545ecfc
|
||||
F ext/fts3/fts3speed.tcl b54caf6a18d38174f1a6e84219950d85e98bb1e9
|
||||
F ext/fts3/tool/fts3cov.sh c331d006359456cf6f8f953e37f2b9c7d568f3863f00bb5f7eb87fea4ac01b73
|
||||
F ext/fts3/tool/fts3view.c 413c346399159df81f86c4928b7c4a455caab73bfbc8cd68f950f632e5751674
|
||||
@ -490,7 +493,7 @@ F ext/userauth/userauth.c 7f00cded7dcaa5d47f54539b290a43d2e59f4b1eb5f447545fa865
|
||||
F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x
|
||||
F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8
|
||||
F magic.txt 8273bf49ba3b0c8559cb2774495390c31fd61c60
|
||||
F main.mk 50dac6249cce84aca4a365127a771b0a32247b151d64752997cc679660e0c3c4
|
||||
F main.mk d486b4a787c6836371d1f769e7f29074c519d7ec998bc09c036018a0adae6207
|
||||
F mkso.sh fd21c06b063bb16a5d25deea1752c2da6ac3ed83
|
||||
F mptest/config01.test 3c6adcbc50b991866855f1977ff172eb6d901271
|
||||
F mptest/config02.test 4415dfe36c48785f751e16e32c20b077c28ae504
|
||||
@ -509,7 +512,7 @@ F src/auth.c f4fa91b6a90bbc8e0d0f738aa284551739c9543a367071f55574681e0f24f8cf
|
||||
F src/backup.c a2891172438e385fdbe97c11c9745676bec54f518d4447090af97189fd8e52d7
|
||||
F src/bitvec.c 7c849aac407230278445cb069bebc5f89bf2ddd87c5ed9459b070a9175707b3d
|
||||
F src/btmutex.c 8acc2f464ee76324bf13310df5692a262b801808984c1b79defb2503bbafadb6
|
||||
F src/btree.c 01a6bd849a63b1ae8ac01aa21e58637a3c7db29bdc687269e476f3d20f39ef2e
|
||||
F src/btree.c db537493a3aaa2ee44631bb9ddad305b457561531114b5b084d7286ad0ea5a12
|
||||
F src/btree.h 74d64b8f28cfa4a894d14d4ed64fa432cd697b98b61708d4351482ae15913e22
|
||||
F src/btreeInt.h 8ce1332edd89dfd2461d561ac10a0ab5601c8e06200cb5230596c3caaf54482e
|
||||
F src/build.c 23f874642825d7eaaeeb7a3281b2b1a75e1d4c4dd9ae4dceddcd908266634214
|
||||
@ -520,10 +523,10 @@ F src/date.c 15082566229d4b1e5f24fdb490bf9bcc68824b911d70e3573ef075a1b9e2d26f
|
||||
F src/dbpage.c 90661a87e1db8bfbc8d2ebbdcd3749651ddb287c555c07a28fb17c7c591ffb68
|
||||
F src/dbstat.c 861e08690fcb0f2ee1165eff0060ea8d4f3e2ea10f80dab7d32ad70443a6ff2d
|
||||
F src/delete.c a8e844af211a48b13b5b358be77a12c860c6a557c21990ad51a548e2536500ce
|
||||
F src/expr.c 19507ae3244402860cac2944be3b92bf9a8b50212fbfabaf7e9817127fec7c00
|
||||
F src/expr.c 4907afcb86d72b5525d8767515ce425ec53c7a2d3664441b46cef5b376ee0cba
|
||||
F src/fault.c 460f3e55994363812d9d60844b2a6de88826e007
|
||||
F src/fkey.c d965ede15d8360c09ed59348940649ee647b192e784466837d7aefa836d1d91e
|
||||
F src/func.c 41bf487f04d54e694baf84baacff7de621847fd7e89a35b776d5fb9ade772ff7
|
||||
F src/func.c 8f72e88cccdee22185133c10f96ccd61dc34c5ea4b1fa9a73c237ef59b2e64f1
|
||||
F src/global.c e83ee571b79ee3adc32e380cf554cf1254bc43763d23786c71721fbcdfbbb965
|
||||
F src/hash.c 8d7dda241d0ebdafb6ffdeda3149a412d7df75102cecfc1021c98d6219823b19
|
||||
F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51
|
||||
@ -556,7 +559,7 @@ F src/os_setup.h 0dbaea40a7d36bf311613d31342e0b99e2536586
|
||||
F src/os_unix.c 2df2b33db88f00af13805d4573ee126bc5973f9e3b91d03c575fa7ba64e7dc41
|
||||
F src/os_win.c a8ea80037e81127ca01959daa87387cc135f325c88dc745376c4f760de852a10
|
||||
F src/os_win.h 7b073010f1451abe501be30d12f6bc599824944a
|
||||
F src/pager.c edd204a77e24583e4be80f8e0c596a5a2d941cafc618ac4c17f724893cfc704f
|
||||
F src/pager.c adb8600c3ff85ad95973379f7132c01d6f2cc5265df959ac3d589f075d004dc3
|
||||
F src/pager.h c49ff262186a78bc5f27e3891edefb900afa769b9e2eaeca0322c7f3553536d4
|
||||
F src/parse.y 8e67d820030d2655b9942ffe61c1e7e6b96cea2f2f72183533299393907d0564
|
||||
F src/pcache.c 084e638432c610f95aea72b8509f0845d2791293f39d1b82f0c0a7e089c3bb6b
|
||||
@ -569,16 +572,16 @@ F src/printf.c 6166a30417b05c5b2f82e1f183f75faa2926ad60531c0b688a57dbc951441a20
|
||||
F src/random.c 097dc8b31b8fba5a9aca1697aeb9fd82078ec91be734c16bffda620ced7ab83c
|
||||
F src/resolve.c a4eb3c617027fd049b07432f3b942ea7151fa793a332a11a7d0f58c9539e104f
|
||||
F src/rowset.c ba9515a922af32abe1f7d39406b9d35730ed65efab9443dc5702693b60854c92
|
||||
F src/select.c 7a4c5023d6c3bcd243546dbe9bbf5b280a60ca565658d037b8c0ec8dd77e1136
|
||||
F src/shell.c.in b76e681f9e441928d574f21f9473ef615158bbeab1ae49f05ecab9d81730a51d
|
||||
F src/select.c ee3113de67330163a35307eacb4188b6778fcae1e2d2f738a9dda2daa0346e24
|
||||
F src/shell.c.in 08e59f1cb9d9b1180aba52861aaada0c95f6ddd210488719684e160a0724c806
|
||||
F src/sqlite.h.in 172528c287399a34f188154017b7268bf82c6d5b780902e361958d2318c4e37c
|
||||
F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
|
||||
F src/sqlite3ext.h a988810c9b21c0dc36dc7a62735012339dc76fc7ab448fb0792721d30eacb69d
|
||||
F src/sqliteInt.h 3064533677f135771e71843b5221482df18d6589afe65e6a7ef828ccb8879a5f
|
||||
F src/sqliteInt.h 8353e96646372efdb0795a13cd9949831b4992c928de8f5c43b2524e8a4c6e7b
|
||||
F src/sqliteLimit.h d7323ffea5208c6af2734574bae933ca8ed2ab728083caa117c9738581a31657
|
||||
F src/status.c 4a3da6d77eeb3531cb0dbdf7047772a2a1b99f98c69e90ce009c75fe6328b2c0
|
||||
F src/table.c 0f141b58a16de7e2fbe81c308379e7279f4c6b50eb08efeec5892794a0ba30d1
|
||||
F src/tclsqlite.c 1f6673991147bc2cecc08a40d22f9803b84c805b24b499fe727f392256f73474
|
||||
F src/tclsqlite.c 4e64ba300a5a26e0f1170e09032429faeb65e45e8f3d1a7833e8edb69fc2979e
|
||||
F src/test1.c 1356984e97bff07e4a8cc3863e892f05b3348678a74783bb6f350b76316736f1
|
||||
F src/test2.c 3efb99ab7f1fc8d154933e02ae1378bac9637da5
|
||||
F src/test3.c 61798bb0d38b915067a8c8e03f5a534b431181f802659a6616f9b4ff7d872644
|
||||
@ -634,20 +637,20 @@ 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 73facf395c8841653b9a54e789d8c80e15bc3d0d1cb9d16104c2d889c15e33cd
|
||||
F src/treeview.c c48bbb4b04a951dcecf95b464d0fe94930339af56688a77f18ee50a526bc1706
|
||||
F src/trigger.c 61bea163b1fa3039bc572ed8312461b978e5c527e5301f302b078f4c1ccdec6a
|
||||
F src/update.c 2cfaded82ca80ff56afb8c3ae5e88284e0824bfd86119827cc22481959f96f92
|
||||
F src/update.c c52a7991bece0453d22c77c08469512ee2f1391c12503fd347d1c939220c5877
|
||||
F src/upsert.c 8789047a8f0a601ea42fa0256d1ba3190c13746b6ba940fe2d25643a7e991937
|
||||
F src/utf.c ee39565f0843775cc2c81135751ddd93eceb91a673ea2c57f61c76f288b041a0
|
||||
F src/util.c 602fe229f32a96ceccae4f40824129669582096f7c355f53dbac156c9fecef23
|
||||
F src/vacuum.c bb346170b0b54c6683bba4a5983aea40485597fdf605c87ec8bc2e199fe88cd8
|
||||
F src/vdbe.c e2ce1c8c86bba823698b1c176283c8b06fbe19c2a1e7238822cefb4f6bce7e3e
|
||||
F src/vdbe.c 4842b98a3043d8fbb0e06de81492caac096a0625301e0f7a14270ad67b28df60
|
||||
F src/vdbe.h 07641758ca8b4f4c6d81ea667ea167c541e6ece21f5574da11e3d21ec37e2662
|
||||
F src/vdbeInt.h ef43f7fdc5fde29fc3fd29c506c12830f366178fdb4edbbf0cbc3dfbd1278b5f
|
||||
F src/vdbeInt.h 2cad0aeeb106371ed0e0946bab89f60627087068847afc2451c05056961c18da
|
||||
F src/vdbeapi.c 354c893f1500cf524cc45c32879b9c68893a28b77e3442c24668d6afe4236217
|
||||
F src/vdbeaux.c 75c4f75ed7e1d12eb3d80093a160ec998c839f3008a1c3c967fc5acf522d0e3c
|
||||
F src/vdbeblob.c 5e61ce31aca17db8fb60395407457a8c1c7fb471dde405e0cd675974611dcfcd
|
||||
F src/vdbemem.c 7189090b72baa025f945a1ac8c61ee420c645254476e8a191d555db76dfea5d4
|
||||
F src/vdbemem.c 3db315458f8dc158aff58719795441437dd6c0fd302e9d9379a8f2a61e185ad6
|
||||
F src/vdbesort.c 43756031ca7430f7aec3ef904824a7883c4ede783e51f280d99b9b65c0796e35
|
||||
F src/vdbetrace.c fe0bc29ebd4e02c8bc5c1945f1d2e6be5927ec12c06d89b03ef2a4def34bf823
|
||||
F src/vdbevtab.c f99b275366c5fc5e2d99f734729880994ab9500bdafde7fae3b02d562b9d323c
|
||||
@ -656,10 +659,10 @@ F src/vxworks.h d2988f4e5a61a4dfe82c6524dd3d6e4f2ce3cdb9
|
||||
F src/wal.c a9cc35881efbb97e568b654dffa2716cf4c5d3fd15489980ab96e48fc5bc60a7
|
||||
F src/wal.h d01234e828943e002040c22a7e017642962f9fd9b2dc142fa599769ae4e459e9
|
||||
F src/walker.c f890a3298418d7cba3b69b8803594fdc484ea241206a8dfa99db6dd36f8cbb3b
|
||||
F src/where.c c4b64c6fa224e5b89ed547ec0ebdfd243c081509b195e71581164a2fbb8d4a80
|
||||
F src/whereInt.h 8da918f392bf202ccc0ee61291455b33ad171d209445f1ff3eaf62e0b6f6b363
|
||||
F src/wherecode.c 2a8a73bcf1886632f2b2247c79395f94852a4b74484d8aa70a005892ce73d339
|
||||
F src/whereexpr.c 7c5ee52e1df81d6a43f39e6b6f35d540fd37254e2b6e953a4e2715c3abf26f46
|
||||
F src/where.c 267caa227dd38ede46959468118ef4067316dae589d889c200911ff77df53ef1
|
||||
F src/whereInt.h b48ca529ffe293c18cbfa8326af18a09e39910de66fb3e96ef788c7cbf8ef3a7
|
||||
F src/wherecode.c 0b09abfcb88c61c6a6984a3e065786631ff35495e9bdf865e6b74ab0a1299c5b
|
||||
F src/whereexpr.c 20255cf03e0b765b742301197d165511ff99e95da0d7ee9c8a2ebc1e888dd049
|
||||
F src/window.c fff1b51757438c664e471d5184634e48dcdf8ea34b640f3b1b0810b1e06de18c
|
||||
F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2
|
||||
F test/affinity2.test ce1aafc86e110685b324e9a763eab4f2a73f737842ec3b687bd965867de90627
|
||||
@ -788,6 +791,7 @@ F test/carray01.test d55d57bf66b1af1c7ac55fae66ff4910884a8f5d21a90a18797ce386212
|
||||
F test/cast.test 336fa21989b5170ebcaf90c24266be22dd97b3e23d1fad5ecf6ad4efb04c4423
|
||||
F test/cffault.test 9d6b20606afe712374952eec4f8fd74b1a8097ef
|
||||
F test/changes.test 9dd8e597d84072122fc8a4fcdea837f4a54a461e6e536053ea984303e8ca937b
|
||||
F test/changes2.test d222c0cbf5ab0ac4d7c180594e486c1bf20b2098d33e56ce33b8e12eba6823b9
|
||||
F test/check.test 56e4ed457e9f8683b9fc56f5b964f461f6e8a8dd5a13f3d495408215d66419ed
|
||||
F test/checkfault.test da6cb3d50247169efcb20bdf57863a3ccfa1d27d9e55cd324f0680096970f014
|
||||
F test/chunksize.test 427d87791743486cbf0c3b8c625002f3255cb3a89c6eba655a98923b1387b760
|
||||
@ -1002,12 +1006,12 @@ F test/fts3b.test c15c4a9d04e210d0be67e54ce6a87b927168fbf9c1e3faec8c1a732c366fd4
|
||||
F test/fts3c.test fc723a9cf10b397fdfc2b32e73c53c8b1ec02958
|
||||
F test/fts3comp1.test a0f5b16a2df44dd0b15751787130af2183167c0c
|
||||
F test/fts3conf.test c84bbaec81281c1788aa545ac6e78a6bd6cde2bdbbce2da261690e3659f5a76b
|
||||
F test/fts3corrupt.test 43c6c89b994e90997590ece4dfa9c9325c9b61cddd7c97e158498da8b1de79f8
|
||||
F test/fts3corrupt.test 6732477c5ace050c5758a40a8b5706c8c0cccd416b9c558e0e15224805a40e57
|
||||
F test/fts3corrupt2.test e318f0676e5e78d5a4b702637e2bb25265954c08a1b1e4aaf93c7880bb0c67d0
|
||||
F test/fts3corrupt3.test 0d5b69a0998b4adf868cc301fc78f3d0707745f1d984ce044c205cdb764b491f
|
||||
F test/fts3corrupt4.test 799ff994b964fed7201be6b6b62c7ff2ef7bb3da6c02b9eaf0d96a5a4d9b6ca3
|
||||
F test/fts3corrupt5.test 0549f85ec4bd22e992f645f13c59b99d652f2f5e643dac75568bfd23a6db7ed5
|
||||
F test/fts3corrupt6.test 657b4b8e5791d8d4adc93c90588fb25f1c7346544dd877c6c298a0746749146d
|
||||
F test/fts3corrupt6.test f417c910254f32c0bc9ead7affa991a1d5aec35b3b32a183ffb05eea78289525
|
||||
F test/fts3cov.test 7eacdbefd756cfa4dc2241974e3db2834e9b372ca215880e00032222f32194cf
|
||||
F test/fts3d.test 2bd8c97bcb9975f2334147173b4872505b6a41359a4f9068960a36afe07a679f
|
||||
F test/fts3defer.test f4c20e4c7153d20a98ee49ee5f3faef624fefc9a067f8d8d629db380c4d9f1de
|
||||
@ -1083,8 +1087,8 @@ F test/fuzz3.test 9c813e6613b837cb7a277b0383cd66bfa07042b4cf0317157c35852f30043c
|
||||
F test/fuzz4.test c229bcdb45518a89e1d208a21343e061503460ac69fae1539320a89f572eb634
|
||||
F test/fuzz_common.tcl b7197de6ed1ee8250a4f82d67876f4561b42ee8cbbfc6160dcb66331bad3f830
|
||||
F test/fuzz_malloc.test f348276e732e814802e39f042b1f6da6362a610af73a528d8f76898fde6b22f2
|
||||
F test/fuzzcheck.c e34696a5db46738118b2efd14fb71f8458ecf0f482df8bbae18fa1d64db9ab7b
|
||||
F test/fuzzdata1.db d36e88741b4f23bcbaaf55b006290669d03c6c891cf13c7b3a53bc1b097b693f
|
||||
F test/fuzzcheck.c 609152902fb51e718554719f44d13677f68c53d98d15fb359fbefdd134be153b
|
||||
F test/fuzzdata1.db 3e86d9cf5aea68ddb8e27c02d7dfdaa226347426c7eb814918e4d95475bf8517
|
||||
F test/fuzzdata2.db 128b3feeb78918d075c9b14b48610145a0dd4c8d6f1ca7c2870c7e425f5bf31f
|
||||
F test/fuzzdata3.db c6586d3e3cef0fbc18108f9bb649aa77bfc38aba
|
||||
F test/fuzzdata4.db b502c7d5498261715812dd8b3c2005bad08b3a26e6489414bd13926cd3e42ed2
|
||||
@ -1095,6 +1099,7 @@ F test/fuzzdata8.db ca9a97f401b06b0d5376139ec7e1f9e773e13345a9a2d9ccc0032cdbfede
|
||||
F test/fuzzer1.test 3d4c4b7e547aba5e5511a2991e3e3d07166cfbb8
|
||||
F test/fuzzer2.test a85ef814ce071293bce1ad8dffa217cbbaad4c14
|
||||
F test/fuzzerfault.test f64c4aef4c9e9edf1d6dc0d3f1e65dcc81e67c996403c88d14f09b74807a42bc
|
||||
F test/fuzzinvariants.c cd5a4c9b891a5c843e0f97a513c095490e974f1ea51392b13c0a7d77fff8e46d
|
||||
F test/gcfault.test dd28c228a38976d6336a3fc42d7e5f1ad060cb8c
|
||||
F test/gencol1.test cc0dbb0ee116e5602e18ea7d47f2a0f76b26e09a823b7c36ef254370c2b0f3c1
|
||||
F test/genesis.tcl 1e2e2e8e5cc4058549a154ff1892fe5c9de19f98
|
||||
@ -1114,7 +1119,7 @@ F test/in5.test b32ce7f4a93f44c5dee94af16886d922cc16ebe33c8e1765c73d4049d0f4b40f
|
||||
F test/in6.test f5f40d6816a8bb7c784424b58a10ac38efb76ab29127a2c17399e0cbeeda0e4b
|
||||
F test/incrblob.test c9b96afc292aeff43d6687bcb09b0280aa599822
|
||||
F test/incrblob2.test a494c9e848560039a23974b9119cfc2cf3ad3bd15cc2694ee6367ae537ef8f1f
|
||||
F test/incrblob3.test d47be78a46da142dd60b93772db6e03936a8a36a3b6129dff8d11923dfdc6d63
|
||||
F test/incrblob3.test 67621a04b3084113bf38ce03797d70eca012d9d8f948193b8f655df577b0da6f
|
||||
F test/incrblob4.test 21a52a6843a56cdcce968c6a86b72a7066d0e6ba
|
||||
F test/incrblob_err.test 89372a28f1d98254f03fed705f9efcd34ef61a674df16d2dbb4726944a2de5e9
|
||||
F test/incrblobfault.test de274b1e329169c2c3438f9528994807ea8201ebf38ae9f157d34bf3ec0cc549
|
||||
@ -1128,10 +1133,10 @@ F test/index2.test f835d5e13ca163bd78c4459ca15fd2e4ed487407
|
||||
F test/index3.test 51685f39345462b84fcf77eb8537af847fdf438cc96b05c45d6aaca4e473ade0
|
||||
F test/index4.test ab92e736d5946840236cd61ac3191f91a7856bf6
|
||||
F test/index5.test 8621491915800ec274609e42e02a97d67e9b13e7
|
||||
F test/index6.test 6e5b6943f6a97a34898e48c4d0d4990caf55c12c00465a43a9c33d2fd9a3a820
|
||||
F test/index6.test b376a648e85aa71c50074382784e6cb0c126ec46e43d1ad15af9a4d234c52e65
|
||||
F test/index7.test b238344318e0b4e42126717f6554f0e7dfd0b39cecad4b736039b43e1e3b6eb3
|
||||
F test/index8.test caa097735c91dbc23d8a402f5e63a2a03c83840ba3928733ed7f9a03f8a912a3
|
||||
F test/index9.test 0aa3e509dddf81f93380396e40e9bb386904c1054924ba8fa9bcdfe85a8e7721
|
||||
F test/index9.test 2ac891806a4136ef3e91280477e23114e67575207dc331e6797fa0ed9379f997
|
||||
F test/indexedby.test f21eca4f7a6ffe14c8500a7ad6cd53166666c99e5ccd311842a28bc94a195fe0
|
||||
F test/indexexpr1.test 3360c2a29a8844e7c4b13293567025281257f9e13a31854cfff6959cede11502
|
||||
F test/indexexpr2.test 2c7abe3c48f8aaa5a448615ab4d13df3662185d28419c00999670834a3f0b484
|
||||
@ -1158,20 +1163,21 @@ F test/ioerr4.test f130fe9e71008577b342b8874d52984bd04ede2c
|
||||
F test/ioerr5.test 2edfa4fb0f896f733071303b42224df8bedd9da4
|
||||
F test/ioerr6.test a395a6ab144b26a9e3e21059a1ab6a7149cca65b
|
||||
F test/istrue.test e7f285bb70282625c258e866ce6337d4c762922f5a300e1b50f958aef6e7d9c9
|
||||
F test/join.test edeaff6edc1c1a2bcfebee343744e04d000f861c3d67cb653114f88565f8c955
|
||||
F test/join.test 5c7f917aa219a125d1df517d3df384c79f74860fdfb2e48c0a599f1e2b3e9ed8
|
||||
F test/join2.test 466b07233820f5deee66a6c3bf6e4500c8bbf7b83649e67606f5f649c07928c0
|
||||
F test/join3.test 6f0c774ff1ba0489e6c88a3e77b9d3528fb4fda0
|
||||
F test/join4.test 1a352e4e267114444c29266ce79e941af5885916
|
||||
F test/join5.test d22b6cba8fb59ab3f1c82701434c360705eb12d4ce200c449f37b018fc47681a
|
||||
F test/join6.test f809c025fa253f9e150c0e9afd4cef8813257bceeb6f46e04041228c9403cc2c
|
||||
F test/join7.test 8e72de4b45e5e930d18c305c7efe86015fb2552731e4e03ea226353036b0dab0
|
||||
F test/join8.test 616eb7c2e4f2a54f2d730b914884d2205c8ada4757e89d08089255964a28e78e
|
||||
F test/join7.test 2268dcbb54b724391dda3748ea95c60d960607ffeed67885675998e7117697f6
|
||||
F test/join8.test e3c8ca1419e3519596a7ef129246ed118d2508b1ebb69ea83b51ea0eda32037c
|
||||
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/joinD.test 1a430af8dac5b68663f13df534ffe98775e582bac2305b80f1e8eb4ab778672a
|
||||
F test/joinE.test d5d182f3812771e2c0d97c9dcf5dbe4c41c8e21c82560e59358731c4a3981d6b
|
||||
F test/joinF.test 53dd66158806823ea680dd7543b5406af151b5aafa5cd06a7f3231cd94938127
|
||||
F test/journal1.test c7b768041b7f494471531e17abc2f4f5ebf9e5096984f43ed17c4eb80ba34497
|
||||
F test/journal2.test 9dac6b4ba0ca79c3b21446bbae993a462c2397c4
|
||||
F test/journal3.test 7c3cf23ffc77db06601c1fcfc9743de8441cb77db9d1aa931863d94f5ffa140e
|
||||
@ -1352,9 +1358,9 @@ F test/round1.test 768018b04522ca420b1aba8a24bd76091d269f3bce3902af3ec6ebcee41ab
|
||||
F test/rowallock.test 3f88ec6819489d0b2341c7a7528ae17c053ab7cc
|
||||
F test/rowhash.test 0bc1d31415e4575d10cacf31e1a66b5cc0f8be81
|
||||
F test/rowid.test e29025be95baf6b32f0d5edef59a7633028325896a98f1caa8019559ca910350
|
||||
F test/rowvalue.test 228b312f8526ed000ecda559a6a9adf30aa2d79dcb12afa5c04eebaafcf55eae
|
||||
F test/rowvalue.test ff1ffa31cebe12feb6f989e09263f3b1e8c560db94b40fe006126a8435fd6832
|
||||
F test/rowvalue2.test 060d238b7e5639a7c5630cb5e63e311b44efef2b
|
||||
F test/rowvalue3.test 3068f508753af69884b12125995f023da0dbb256
|
||||
F test/rowvalue3.test 1347e25ca11c547c5a6ff0cc5626f95aa9740e9275bfaec096029f57cb2130ce
|
||||
F test/rowvalue4.test 441e7e366ac6d939a3a95a574031c56ec2a854077a91d66eee5ff1d86cb5be58
|
||||
F test/rowvalue5.test 00740304ea6a53a8704640c7405690f0045d5d2a6b4b04dde7bccc14c3068ea7
|
||||
F test/rowvalue6.test d19b54feb604d5601f8614b15e214e0774c01087
|
||||
@ -1476,7 +1482,7 @@ F test/subquery.test 3a1a5b600b8d4f504d2a2c61f33db820983dba94a0ef3e4aedca8f0165e
|
||||
F test/subquery2.test 90cf944b9de8204569cf656028391e4af1ccc8c0cc02d4ef38ee3be8de1ffb12
|
||||
F test/subselect.test 0966aa8e720224dbd6a5e769a3ec2a723e332303
|
||||
F test/substr.test a673e3763e247e9b5e497a6cacbaf3da2bd8ec8921c0677145c109f2e633f36b
|
||||
F test/subtype1.test 7fe09496352f97053af1437150751be2d0a0cae8
|
||||
F test/subtype1.test 45c85632abd38f7ea9b33f17448d966d67550f552e0822bab74576814d0d1718
|
||||
F test/superlock.test ec94f0556b6488d97f71c79f9061ae08d9ab8f12
|
||||
F test/swarmvtab.test 250231404fcac88f61a6c147bb0e3a118ed879278cd3ccb0ae2d3a729e1e8e26
|
||||
F test/swarmvtab2.test c948cb2fdfc5b01d85e8f6d6504854202dc1a0782ab2a0ed61538f27cbd0aa5c
|
||||
@ -1854,6 +1860,7 @@ F test/window9.test 349c71eab4288a1ffc19e2f65872ec2c37e6cf8a1dda2ad300364b7450ae
|
||||
F test/windowA.test 6d63dc1260daa17141a55007600581778523a8b420629f1282d2acfc36af23be
|
||||
F test/windowB.test f2fb42b864b0cf431c956407583e9478a74c3642bdf8737fdcb6ff4a40298b07
|
||||
F test/windowC.test 6fd75f5bb2f1343d34e470e36e68f0ff638d8a42f6aa7d99471261b31a0d42f2
|
||||
F test/windowD.test 65cf5a765fb8072450e8a0de2979ce7f09a38d87724fe1280c6444073e3da49b
|
||||
F test/windowerr.tcl f5acd6fbc210d7b5546c0e879d157888455cd4a17a1d3f28f07c1c8a387019e0
|
||||
F test/windowerr.test a8b752402109c15aa1c5efe1b93ccb0ce1ef84fa964ae1cd6684dd0b3cc1819b
|
||||
F test/windowfault.test 15094c1529424e62f798bc679e3fe9dfab6e8ba2f7dfe8c923b6248c31660a7c
|
||||
@ -1983,8 +1990,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 33d77fea4084c5aba9203dfeddb820424f102dcb8347dc59e32b922bdb241382 7e87892c249f023ee9ed1d5f75a9ad8db10fb38f14dd9e6954b12b9b28400b07
|
||||
R c9081974dfbb7d140ac326c1bbce54a8
|
||||
P 934656f13dabc41ccf307b10dca7377c758b8a3b93eca57c072745c2786d6b3c 3a461f61b47e6ba6d5dcc2b7470ebde512b57bc68086f65050e07b06f42b7351
|
||||
R 25e193ae4a494f5ff48654bafa28631f
|
||||
U drh
|
||||
Z bbe2f38f083ae616992549cc7e11561e
|
||||
Z b958844303a6732e4928e10fcec459d3
|
||||
# Remove this line to create a well-formed Fossil manifest.
|
||||
|
@ -1 +1 @@
|
||||
934656f13dabc41ccf307b10dca7377c758b8a3b93eca57c072745c2786d6b3c
|
||||
c8ad869938b06378f49c02655c00ee4f3315e1275d15e69d4ff61d6f60230fe8
|
@ -3933,12 +3933,17 @@ static int incrVacuumStep(BtShared *pBt, Pgno nFin, Pgno iLastPg, int bCommit){
|
||||
}
|
||||
do {
|
||||
MemPage *pFreePg;
|
||||
Pgno dbSize = btreePagecount(pBt);
|
||||
rc = allocateBtreePage(pBt, &pFreePg, &iFreePg, iNear, eMode);
|
||||
if( rc!=SQLITE_OK ){
|
||||
releasePage(pLastPg);
|
||||
return rc;
|
||||
}
|
||||
releasePage(pFreePg);
|
||||
if( iFreePg>dbSize ){
|
||||
releasePage(pLastPg);
|
||||
return SQLITE_CORRUPT_BKPT;
|
||||
}
|
||||
}while( bCommit && iFreePg>nFin );
|
||||
assert( iFreePg<iLastPg );
|
||||
|
||||
@ -9165,7 +9170,7 @@ int sqlite3BtreeInsert(
|
||||
TRACE(("INSERT: table=%d nkey=%lld ndata=%d page=%d %s\n",
|
||||
pCur->pgnoRoot, pX->nKey, pX->nData, pPage->pgno,
|
||||
loc==0 ? "overwrite" : "new entry"));
|
||||
assert( pPage->isInit );
|
||||
assert( pPage->isInit || CORRUPT_DB );
|
||||
newCell = pBt->pTmpSpace;
|
||||
assert( newCell!=0 );
|
||||
if( flags & BTREE_PREFORMAT ){
|
||||
|
46
src/expr.c
46
src/expr.c
@ -3955,7 +3955,17 @@ static int exprCodeInlineFunction(
|
||||
caseExpr.x.pList = pFarg;
|
||||
return sqlite3ExprCodeTarget(pParse, &caseExpr, target);
|
||||
}
|
||||
|
||||
#ifdef SQLITE_ENABLE_OFFSET_SQL_FUNC
|
||||
case INLINEFUNC_sqlite_offset: {
|
||||
Expr *pArg = pFarg->a[0].pExpr;
|
||||
if( pArg->op==TK_COLUMN && pArg->iTable>=0 ){
|
||||
sqlite3VdbeAddOp3(v, OP_Offset, pArg->iTable, pArg->iColumn, target);
|
||||
}else{
|
||||
sqlite3VdbeAddOp2(v, OP_Null, 0, target);
|
||||
}
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
default: {
|
||||
/* The UNLIKELY() function is a no-op. The result is the value
|
||||
** of the first argument.
|
||||
@ -4494,20 +4504,8 @@ expr_code_doover:
|
||||
if( !pColl ) pColl = db->pDfltColl;
|
||||
sqlite3VdbeAddOp4(v, OP_CollSeq, 0, 0, 0, (char *)pColl, P4_COLLSEQ);
|
||||
}
|
||||
#ifdef SQLITE_ENABLE_OFFSET_SQL_FUNC
|
||||
if( (pDef->funcFlags & SQLITE_FUNC_OFFSET)!=0 && ALWAYS(pFarg!=0) ){
|
||||
Expr *pArg = pFarg->a[0].pExpr;
|
||||
if( pArg->op==TK_COLUMN ){
|
||||
sqlite3VdbeAddOp3(v, OP_Offset, pArg->iTable, pArg->iColumn, target);
|
||||
}else{
|
||||
sqlite3VdbeAddOp2(v, OP_Null, 0, target);
|
||||
}
|
||||
}else
|
||||
#endif
|
||||
{
|
||||
sqlite3VdbeAddFunctionCall(pParse, constMask, r1, target, nFarg,
|
||||
pDef, pExpr->op2);
|
||||
}
|
||||
sqlite3VdbeAddFunctionCall(pParse, constMask, r1, target, nFarg,
|
||||
pDef, pExpr->op2);
|
||||
if( nFarg ){
|
||||
if( constMask==0 ){
|
||||
sqlite3ReleaseTempRange(pParse, r1, nFarg);
|
||||
@ -4579,8 +4577,24 @@ expr_code_doover:
|
||||
exprCodeBetween(pParse, pExpr, target, 0, 0);
|
||||
return target;
|
||||
}
|
||||
case TK_COLLATE: {
|
||||
if( !ExprHasProperty(pExpr, EP_Collate)
|
||||
&& ALWAYS(pExpr->pLeft)
|
||||
&& pExpr->pLeft->op==TK_FUNCTION
|
||||
){
|
||||
inReg = sqlite3ExprCodeTarget(pParse, pExpr->pLeft, target);
|
||||
if( inReg!=target ){
|
||||
sqlite3VdbeAddOp2(v, OP_SCopy, inReg, target);
|
||||
inReg = target;
|
||||
}
|
||||
sqlite3VdbeAddOp1(v, OP_ClrSubtype, inReg);
|
||||
return inReg;
|
||||
}else{
|
||||
pExpr = pExpr->pLeft;
|
||||
goto expr_code_doover; /* 2018-04-28: Prevent deep recursion. */
|
||||
}
|
||||
}
|
||||
case TK_SPAN:
|
||||
case TK_COLLATE:
|
||||
case TK_UPLUS: {
|
||||
pExpr = pExpr->pLeft;
|
||||
goto expr_code_doover; /* 2018-04-28: Prevent deep recursion. OSSFuzz. */
|
||||
|
@ -2241,8 +2241,7 @@ void sqlite3RegisterBuiltinFunctions(void){
|
||||
INLINE_FUNC(likelihood, 2, INLINEFUNC_unlikely, SQLITE_FUNC_UNLIKELY),
|
||||
INLINE_FUNC(likely, 1, INLINEFUNC_unlikely, SQLITE_FUNC_UNLIKELY),
|
||||
#ifdef SQLITE_ENABLE_OFFSET_SQL_FUNC
|
||||
{1, SQLITE_FUNC_BUILTIN|SQLITE_UTF8|SQLITE_FUNC_OFFSET|SQLITE_FUNC_TYPEOF,
|
||||
0, 0, noopFunc, 0, 0, 0, "sqlite_offset", {0} },
|
||||
INLINE_FUNC(sqlite_offset, 1, INLINEFUNC_sqlite_offset, 0 ),
|
||||
#endif
|
||||
FUNCTION(ltrim, 1, 1, 0, trimFunc ),
|
||||
FUNCTION(ltrim, 2, 1, 0, trimFunc ),
|
||||
|
@ -2613,6 +2613,7 @@ static int pager_truncate(Pager *pPager, Pgno nPage){
|
||||
memset(pTmp, 0, szPage);
|
||||
testcase( (newSize-szPage) == currentSize );
|
||||
testcase( (newSize-szPage) > currentSize );
|
||||
sqlite3OsFileControlHint(pPager->fd, SQLITE_FCNTL_SIZE_HINT, &newSize);
|
||||
rc = sqlite3OsWrite(pPager->fd, pTmp, szPage, newSize-szPage);
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
|
68
src/select.c
68
src/select.c
@ -429,15 +429,21 @@ void sqlite3SetJoinExpr(Expr *p, int iTable, u32 joinFlag){
|
||||
** an ordinary term that omits the EP_OuterON mark.
|
||||
**
|
||||
** This happens when a LEFT JOIN is simplified into an ordinary JOIN.
|
||||
**
|
||||
** If nullable is true, that means that Expr p might evaluate to NULL even
|
||||
** if it is a reference to a NOT NULL column. This can happen, for example,
|
||||
** if the table that p references is on the left side of a RIGHT JOIN.
|
||||
** If nullable is true, then take care to not remove the EP_CanBeNull bit.
|
||||
** See forum thread https://sqlite.org/forum/forumpost/b40696f50145d21c
|
||||
*/
|
||||
static void unsetJoinExpr(Expr *p, int iTable){
|
||||
static void unsetJoinExpr(Expr *p, int iTable, int nullable){
|
||||
while( p ){
|
||||
if( ExprHasProperty(p, EP_OuterON)
|
||||
&& (iTable<0 || p->w.iJoin==iTable) ){
|
||||
ExprClearProperty(p, EP_OuterON);
|
||||
ExprSetProperty(p, EP_InnerON);
|
||||
}
|
||||
if( p->op==TK_COLUMN && p->iTable==iTable ){
|
||||
if( p->op==TK_COLUMN && p->iTable==iTable && !nullable ){
|
||||
ExprClearProperty(p, EP_CanBeNull);
|
||||
}
|
||||
if( p->op==TK_FUNCTION ){
|
||||
@ -445,11 +451,11 @@ static void unsetJoinExpr(Expr *p, int iTable){
|
||||
if( p->x.pList ){
|
||||
int i;
|
||||
for(i=0; i<p->x.pList->nExpr; i++){
|
||||
unsetJoinExpr(p->x.pList->a[i].pExpr, iTable);
|
||||
unsetJoinExpr(p->x.pList->a[i].pExpr, iTable, nullable);
|
||||
}
|
||||
}
|
||||
}
|
||||
unsetJoinExpr(p->pLeft, iTable);
|
||||
unsetJoinExpr(p->pLeft, iTable, nullable);
|
||||
p = p->pRight;
|
||||
}
|
||||
}
|
||||
@ -610,6 +616,7 @@ static int sqlite3ProcessJoin(Parse *pParse, Select *p){
|
||||
sqlite3SetJoinExpr(pRight->u3.pOn, pRight->iCursor, joinType);
|
||||
p->pWhere = sqlite3ExprAnd(pParse, p->pWhere, pRight->u3.pOn);
|
||||
pRight->u3.pOn = 0;
|
||||
pRight->fg.isOn = 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
@ -3763,9 +3770,10 @@ static Expr *substExpr(
|
||||
Expr *pExpr /* Expr in which substitution occurs */
|
||||
){
|
||||
if( pExpr==0 ) return 0;
|
||||
if( ExprHasProperty(pExpr, EP_OuterON)
|
||||
if( ExprHasProperty(pExpr, EP_OuterON|EP_InnerON)
|
||||
&& pExpr->w.iJoin==pSubst->iTable
|
||||
){
|
||||
testcase( ExprHasProperty(pExpr, EP_InnerON) );
|
||||
pExpr->w.iJoin = pSubst->iNewTable;
|
||||
}
|
||||
if( pExpr->op==TK_COLUMN
|
||||
@ -3810,6 +3818,11 @@ static Expr *substExpr(
|
||||
}
|
||||
sqlite3ExprDelete(db, pExpr);
|
||||
pExpr = pNew;
|
||||
if( pExpr->op==TK_TRUEFALSE ){
|
||||
pExpr->u.iValue = sqlite3ExprTruthValue(pExpr);
|
||||
pExpr->op = TK_INTEGER;
|
||||
ExprSetProperty(pExpr, EP_IntValue);
|
||||
}
|
||||
|
||||
/* Ensure that the expression now has an implicit collation sequence,
|
||||
** just as it did when it was a column of a view or sub-query. */
|
||||
@ -4163,6 +4176,11 @@ static void renumberCursors(
|
||||
**
|
||||
** (28) The subquery is not a MATERIALIZED CTE.
|
||||
**
|
||||
** (29) Either the subquery is not the right-hand operand of a join with an
|
||||
** ON or USING clause nor the right-hand operand of a NATURAL JOIN, or
|
||||
** the right-most table within the FROM clause of the subquery
|
||||
** is not part of an outer join.
|
||||
**
|
||||
**
|
||||
** In this routine, the "p" parameter is a pointer to the outer query.
|
||||
** The subquery is p->pSrc->a[iFrom]. isAgg is true if the outer query
|
||||
@ -4290,6 +4308,35 @@ static int flattenSubquery(
|
||||
return 0; /* (28) */
|
||||
}
|
||||
|
||||
/* Restriction (29):
|
||||
**
|
||||
** We do not want two constraints on the same term of the flattened
|
||||
** query where one constraint has EP_InnerON and the other is EP_OuterON.
|
||||
** To prevent this, one or the other of the following conditions must be
|
||||
** false:
|
||||
**
|
||||
** (29a) The right-most entry in the FROM clause of the subquery
|
||||
** must not be part of an outer join.
|
||||
**
|
||||
** (29b) The subquery itself must not be the right operand of a
|
||||
** NATURAL join or a join that as an ON or USING clause.
|
||||
**
|
||||
** These conditions are sufficient to keep an EP_OuterON from being
|
||||
** flattened into an EP_InnerON. Restrictions (3a) and (27) prevent
|
||||
** an EP_InnerON from being flattened into an EP_OuterON.
|
||||
*/
|
||||
if( pSubSrc->nSrc>=2
|
||||
&& (pSubSrc->a[pSubSrc->nSrc-1].fg.jointype & JT_OUTER)!=0
|
||||
){
|
||||
if( (pSubitem->fg.jointype & JT_NATURAL)!=0
|
||||
|| pSubitem->fg.isUsing
|
||||
|| NEVER(pSubitem->u3.pOn!=0) /* ON clause already shifted into WHERE */
|
||||
|| pSubitem->fg.isOn
|
||||
){
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Restriction (17): If the sub-query is a compound SELECT, then it must
|
||||
** use only the UNION ALL operator. And none of the simple select queries
|
||||
** that make up the compound SELECT are allowed to be aggregate or distinct
|
||||
@ -4681,7 +4728,11 @@ static void constInsert(
|
||||
static void findConstInWhere(WhereConst *pConst, Expr *pExpr){
|
||||
Expr *pRight, *pLeft;
|
||||
if( NEVER(pExpr==0) ) return;
|
||||
if( ExprHasProperty(pExpr, EP_OuterON) ) return;
|
||||
if( ExprHasProperty(pExpr, EP_OuterON|EP_InnerON) ){
|
||||
testcase( ExprHasProperty(pExpr, EP_OuterON) );
|
||||
testcase( ExprHasProperty(pExpr, EP_InnerON) );
|
||||
return;
|
||||
}
|
||||
if( pExpr->op==TK_AND ){
|
||||
findConstInWhere(pConst, pExpr->pRight);
|
||||
findConstInWhere(pConst, pExpr->pLeft);
|
||||
@ -5022,7 +5073,7 @@ static int pushDownWhereTerms(
|
||||
while( pSubq ){
|
||||
SubstContext x;
|
||||
pNew = sqlite3ExprDup(pParse->db, pWhere, 0);
|
||||
unsetJoinExpr(pNew, -1);
|
||||
unsetJoinExpr(pNew, -1, 1);
|
||||
x.pParse = pParse;
|
||||
x.iTable = pSrc->iCursor;
|
||||
x.iNewTable = pSrc->iCursor;
|
||||
@ -6706,7 +6757,8 @@ int sqlite3Select(
|
||||
SELECTTRACE(0x100,pParse,p,
|
||||
("LEFT-JOIN simplifies to JOIN on term %d\n",i));
|
||||
pItem->fg.jointype &= ~(JT_LEFT|JT_OUTER);
|
||||
unsetJoinExpr(p->pWhere, pItem->iCursor);
|
||||
unsetJoinExpr(p->pWhere, pItem->iCursor,
|
||||
pTabList->a[0].fg.jointype & JT_LTORJ);
|
||||
}
|
||||
|
||||
/* No futher action if this term of the FROM clause is no a subquery */
|
||||
|
@ -1838,7 +1838,11 @@ static int safeModeAuth(
|
||||
UNUSED_PARAMETER(zA4);
|
||||
switch( op ){
|
||||
case SQLITE_ATTACH: {
|
||||
#ifndef SQLITE_SHELL_WASM_MODE
|
||||
/* In WASM builds the filesystem is a virtual sandbox, so
|
||||
** there's no harm in using ATTACH. */
|
||||
failIfSafeMode(p, "cannot run ATTACH in safe mode");
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
case SQLITE_FUNCTION: {
|
||||
@ -4293,7 +4297,9 @@ static const char *(azHelp[]) = {
|
||||
".connection [close] [#] Open or close an auxiliary database connection",
|
||||
".databases List names and files of attached databases",
|
||||
".dbconfig ?op? ?val? List or change sqlite3_db_config() options",
|
||||
#if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_ENABLE_DBPAGE_VTAB)
|
||||
".dbinfo ?DB? Show status information about the database",
|
||||
#endif
|
||||
".dump ?OBJECTS? Render database content as SQL",
|
||||
" Options:",
|
||||
" --data-only Output only INSERT statements",
|
||||
@ -5831,6 +5837,7 @@ static int db_int(sqlite3 *db, const char *zSql){
|
||||
return res;
|
||||
}
|
||||
|
||||
#if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_ENABLE_DBPAGE_VTAB)
|
||||
/*
|
||||
** Convert a 2-byte or 4-byte big-endian integer into a native integer
|
||||
*/
|
||||
@ -5937,6 +5944,8 @@ static int shell_dbinfo_command(ShellState *p, int nArg, char **azArg){
|
||||
utf8_printf(p->out, "%-20s %u\n", "data version", iDataVersion);
|
||||
return 0;
|
||||
}
|
||||
#endif /* !defined(SQLITE_OMIT_VIRTUALTABLE)
|
||||
&& defined(SQLITE_ENABLE_DBPAGE_VTAB) */
|
||||
|
||||
/*
|
||||
** Print the current sqlite3_errmsg() value to stderr and return 1.
|
||||
@ -8479,11 +8488,11 @@ static int do_meta_command(char *zLine, ShellState *p){
|
||||
}
|
||||
}else
|
||||
|
||||
#if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_ENABLE_DBPAGE_VTAB)
|
||||
if( c=='d' && n>=3 && strncmp(azArg[0], "dbinfo", n)==0 ){
|
||||
rc = shell_dbinfo_command(p, nArg, azArg);
|
||||
}else
|
||||
|
||||
#if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_ENABLE_DBPAGE_VTAB)
|
||||
if( c=='r' && strncmp(azArg[0], "recover", n)==0 ){
|
||||
open_db(p, 0);
|
||||
rc = recoverDatabaseCmd(p, nArg, azArg);
|
||||
|
@ -1884,7 +1884,7 @@ struct FuncDestructor {
|
||||
#define SQLITE_FUNC_SLOCHNG 0x2000 /* "Slow Change". Value constant during a
|
||||
** single query - might change over time */
|
||||
#define SQLITE_FUNC_TEST 0x4000 /* Built-in testing functions */
|
||||
#define SQLITE_FUNC_OFFSET 0x8000 /* Built-in sqlite_offset() function */
|
||||
/* 0x8000 -- available for reuse */
|
||||
#define SQLITE_FUNC_WINDOW 0x00010000 /* Built-in window-only function */
|
||||
#define SQLITE_FUNC_INTERNAL 0x00040000 /* For use by NestedParse() only */
|
||||
#define SQLITE_FUNC_DIRECT 0x00080000 /* Not for use in TRIGGERs or VIEWs */
|
||||
@ -1901,6 +1901,7 @@ struct FuncDestructor {
|
||||
#define INLINEFUNC_expr_compare 3
|
||||
#define INLINEFUNC_affinity 4
|
||||
#define INLINEFUNC_iif 5
|
||||
#define INLINEFUNC_sqlite_offset 6
|
||||
#define INLINEFUNC_unlikely 99 /* Default case */
|
||||
|
||||
/*
|
||||
@ -3100,6 +3101,7 @@ struct SrcItem {
|
||||
unsigned isCte :1; /* This is a CTE */
|
||||
unsigned notCte :1; /* This item may not match a CTE */
|
||||
unsigned isUsing :1; /* u3.pUsing is valid */
|
||||
unsigned isOn :1; /* u3.pOn was once valid and non-NULL */
|
||||
unsigned isSynthUsing :1; /* u3.pUsing is synthensized from NATURAL */
|
||||
unsigned isNestedFrom :1; /* pSelect is a SF_NestedFrom subquery */
|
||||
} fg;
|
||||
|
@ -2965,7 +2965,7 @@ deserialize_error:
|
||||
}
|
||||
|
||||
if( objc==(6+isReadonly) ){
|
||||
zDb = Tcl_GetString(objv[2]);
|
||||
zDb = Tcl_GetString(objv[2+isReadonly]);
|
||||
}
|
||||
zTable = Tcl_GetString(objv[objc-3]);
|
||||
zColumn = Tcl_GetString(objv[objc-2]);
|
||||
|
@ -215,6 +215,9 @@ void sqlite3TreeViewSrcList(TreeView *pView, const SrcList *pSrc){
|
||||
if( pItem->fg.isCte ){
|
||||
sqlite3_str_appendf(&x, " CteUse=0x%p", pItem->u2.pCteUse);
|
||||
}
|
||||
if( pItem->fg.isOn || (pItem->fg.isUsing==0 && pItem->u3.pOn!=0) ){
|
||||
sqlite3_str_appendf(&x, " ON");
|
||||
}
|
||||
sqlite3StrAccumFinish(&x);
|
||||
sqlite3TreeViewItem(pView, zLine, i<pSrc->nSrc-1);
|
||||
n = 0;
|
||||
|
@ -1028,7 +1028,7 @@ void sqlite3Update(
|
||||
}else{
|
||||
sqlite3VdbeAddOp3(v, OP_NotExists, iDataCur, labelContinue,regOldRowid);
|
||||
}
|
||||
VdbeCoverageNeverTaken(v);
|
||||
VdbeCoverage(v);
|
||||
}
|
||||
|
||||
/* Do FK constraint checks. */
|
||||
|
35
src/vdbe.c
35
src/vdbe.c
@ -1479,11 +1479,16 @@ case OP_Move: {
|
||||
break;
|
||||
}
|
||||
|
||||
/* Opcode: Copy P1 P2 P3 * *
|
||||
/* Opcode: Copy P1 P2 P3 * P5
|
||||
** Synopsis: r[P2@P3+1]=r[P1@P3+1]
|
||||
**
|
||||
** Make a copy of registers P1..P1+P3 into registers P2..P2+P3.
|
||||
**
|
||||
** If the 0x0002 bit of P5 is set then also clear the MEM_Subtype flag in the
|
||||
** destination. The 0x0001 bit of P5 indicates that this Copy opcode cannot
|
||||
** be merged. The 0x0001 bit is used by the query planner and does not
|
||||
** come into play during query execution.
|
||||
**
|
||||
** This instruction makes a deep copy of the value. A duplicate
|
||||
** is made of any string or blob constant. See also OP_SCopy.
|
||||
*/
|
||||
@ -1498,6 +1503,9 @@ case OP_Copy: {
|
||||
memAboutToChange(p, pOut);
|
||||
sqlite3VdbeMemShallowCopy(pOut, pIn1, MEM_Ephem);
|
||||
Deephemeralize(pOut);
|
||||
if( (pOut->flags & MEM_Subtype)!=0 && (pOp->p5 & 0x0002)!=0 ){
|
||||
pOut->flags &= ~MEM_Subtype;
|
||||
}
|
||||
#ifdef SQLITE_DEBUG
|
||||
pOut->pScopyFrom = 0;
|
||||
#endif
|
||||
@ -3880,6 +3888,11 @@ case OP_Transaction: {
|
||||
}
|
||||
p->expired = 1;
|
||||
rc = SQLITE_SCHEMA;
|
||||
|
||||
/* Set changeCntOn to 0 to prevent the value returned by sqlite3_changes()
|
||||
** from being modified in sqlite3VdbeHalt(). If this statement is
|
||||
** reprepared, changeCntOn will be set again. */
|
||||
p->changeCntOn = 0;
|
||||
}
|
||||
if( rc ) goto abort_due_to_error;
|
||||
break;
|
||||
@ -4179,8 +4192,8 @@ case OP_OpenDup: {
|
||||
pCx->pgnoRoot = pOrig->pgnoRoot;
|
||||
pCx->isOrdered = pOrig->isOrdered;
|
||||
pCx->ub.pBtx = pOrig->ub.pBtx;
|
||||
pCx->hasBeenDuped = 1;
|
||||
pOrig->hasBeenDuped = 1;
|
||||
pCx->noReuse = 1;
|
||||
pOrig->noReuse = 1;
|
||||
rc = sqlite3BtreeCursor(pCx->ub.pBtx, pCx->pgnoRoot, BTREE_WRCSR,
|
||||
pCx->pKeyInfo, pCx->uc.pCursor);
|
||||
/* The sqlite3BtreeCursor() routine can only fail for the first cursor
|
||||
@ -4247,7 +4260,7 @@ case OP_OpenEphemeral: {
|
||||
aMem[pOp->p3].z = "";
|
||||
}
|
||||
pCx = p->apCsr[pOp->p1];
|
||||
if( pCx && !pCx->hasBeenDuped && ALWAYS(pOp->p2<=pCx->nField) ){
|
||||
if( pCx && !pCx->noReuse && ALWAYS(pOp->p2<=pCx->nField) ){
|
||||
/* If the ephermeral table is already open and has no duplicates from
|
||||
** OP_OpenDup, then erase all existing content so that the table is
|
||||
** empty again, rather than creating a new table. */
|
||||
@ -5834,6 +5847,7 @@ case OP_NullRow: {
|
||||
if( pC==0 ) goto no_mem;
|
||||
pC->seekResult = 0;
|
||||
pC->isTable = 1;
|
||||
pC->noReuse = 1;
|
||||
pC->uc.pCursor = sqlite3BtreeFakeValidCursor();
|
||||
}
|
||||
pC->nullRow = 1;
|
||||
@ -7978,7 +7992,6 @@ case OP_VColumn: {
|
||||
|
||||
VdbeCursor *pCur = p->apCsr[pOp->p1];
|
||||
assert( pCur!=0 );
|
||||
assert( pCur->eCurType==CURTYPE_VTAB );
|
||||
assert( pOp->p3>0 && pOp->p3<=(p->nMem+1 - p->nCursor) );
|
||||
pDest = &aMem[pOp->p3];
|
||||
memAboutToChange(p, pDest);
|
||||
@ -7986,6 +7999,7 @@ case OP_VColumn: {
|
||||
sqlite3VdbeMemSetNull(pDest);
|
||||
break;
|
||||
}
|
||||
assert( pCur->eCurType==CURTYPE_VTAB );
|
||||
pVtab = pCur->uc.pVCur->pVtab;
|
||||
pModule = pVtab->pModule;
|
||||
assert( pModule->xColumn );
|
||||
@ -8313,6 +8327,17 @@ case OP_Function: { /* group */
|
||||
break;
|
||||
}
|
||||
|
||||
/* Opcode: ClrSubtype P1 * * * *
|
||||
** Synopsis: r[P1].subtype = 0
|
||||
**
|
||||
** Clear the subtype from register P1.
|
||||
*/
|
||||
case OP_ClrSubtype: { /* in1 */
|
||||
pIn1 = &aMem[pOp->p1];
|
||||
pIn1->flags &= ~MEM_Subtype;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Opcode: FilterAdd P1 * P3 P4 *
|
||||
** Synopsis: filter(P1) += key(P3@P4)
|
||||
**
|
||||
|
@ -86,7 +86,7 @@ struct VdbeCursor {
|
||||
Bool isEphemeral:1; /* True for an ephemeral table */
|
||||
Bool useRandomRowid:1; /* Generate new record numbers semi-randomly */
|
||||
Bool isOrdered:1; /* True if the table is not BTREE_UNORDERED */
|
||||
Bool hasBeenDuped:1; /* This cursor was source or target of OP_OpenDup */
|
||||
Bool noReuse:1; /* OpenEphemeral may not reuse this cursor */
|
||||
u16 seekHit; /* See the OP_SeekHit and OP_IfNoHope opcodes */
|
||||
union { /* pBtx for isEphermeral. pAltMap otherwise */
|
||||
Btree *pBtx; /* Separate file holding temporary table */
|
||||
|
@ -1571,8 +1571,8 @@ static int valueFromExpr(
|
||||
rc = valueFromExpr(db, pExpr->pLeft, enc, aff, ppVal, pCtx);
|
||||
testcase( rc!=SQLITE_OK );
|
||||
if( *ppVal ){
|
||||
sqlite3VdbeMemCast(*ppVal, aff, SQLITE_UTF8);
|
||||
sqlite3ValueApplyAffinity(*ppVal, affinity, SQLITE_UTF8);
|
||||
sqlite3VdbeMemCast(*ppVal, aff, enc);
|
||||
sqlite3ValueApplyAffinity(*ppVal, affinity, enc);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
84
src/where.c
84
src/where.c
@ -682,6 +682,7 @@ static void translateColumnToCopy(
|
||||
pOp->p1 = pOp->p2 + iRegister;
|
||||
pOp->p2 = pOp->p3;
|
||||
pOp->p3 = 0;
|
||||
pOp->p5 = 2; /* Cause the MEM_Subtype flag to be cleared */
|
||||
}else if( pOp->opcode==OP_Rowid ){
|
||||
pOp->opcode = OP_Sequence;
|
||||
pOp->p1 = iAutoidxCur;
|
||||
@ -756,14 +757,17 @@ static int termCanDriveIndex(
|
||||
char aff;
|
||||
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_OuterON)
|
||||
&& (pTerm->eOperator & WO_IS)
|
||||
){
|
||||
/* Cannot use an IS term from the WHERE clause as an index driver for
|
||||
** the RHS of a LEFT JOIN or for the LHS of a RIGHT JOIN. Such a term
|
||||
** can only be used if it is from the ON clause. */
|
||||
return 0;
|
||||
assert( (pSrc->fg.jointype & JT_RIGHT)==0 );
|
||||
if( (pSrc->fg.jointype & (JT_LEFT|JT_LTORJ|JT_RIGHT))!=0 ){
|
||||
testcase( (pSrc->fg.jointype & (JT_LEFT|JT_LTORJ|JT_RIGHT))==JT_LEFT );
|
||||
testcase( (pSrc->fg.jointype & (JT_LEFT|JT_LTORJ|JT_RIGHT))==JT_LTORJ );
|
||||
testcase( ExprHasProperty(pTerm->pExpr, EP_OuterON) )
|
||||
testcase( ExprHasProperty(pTerm->pExpr, EP_InnerON) );
|
||||
if( !ExprHasProperty(pTerm->pExpr, EP_OuterON|EP_InnerON)
|
||||
|| pTerm->pExpr->w.iJoin != pSrc->iCursor
|
||||
){
|
||||
return 0; /* See tag-20191211-001 */
|
||||
}
|
||||
}
|
||||
if( (pTerm->prereqRight & notReady)!=0 ) return 0;
|
||||
assert( (pTerm->eOperator & (WO_OR|WO_AND))==0 );
|
||||
@ -1177,13 +1181,20 @@ static sqlite3_index_info *allocateIndexInfo(
|
||||
assert( pTerm->u.x.leftColumn<pTab->nCol );
|
||||
|
||||
/* tag-20191211-002: WHERE-clause constraints are not useful to the
|
||||
** right-hand table of a LEFT JOIN nor to the left-hand table of a
|
||||
** right-hand table of a LEFT JOIN nor to the either table of a
|
||||
** 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_OuterON)
|
||||
){
|
||||
continue;
|
||||
if( (pSrc->fg.jointype & (JT_LEFT|JT_LTORJ|JT_RIGHT))!=0 ){
|
||||
testcase( (pSrc->fg.jointype & (JT_LEFT|JT_LTORJ|JT_RIGHT))==JT_LEFT );
|
||||
testcase( (pSrc->fg.jointype & (JT_LEFT|JT_LTORJ|JT_RIGHT))==JT_RIGHT );
|
||||
testcase( (pSrc->fg.jointype & (JT_LEFT|JT_LTORJ|JT_RIGHT))==JT_LTORJ );
|
||||
testcase( ExprHasProperty(pTerm->pExpr, EP_OuterON) );
|
||||
testcase( ExprHasProperty(pTerm->pExpr, EP_InnerON) );
|
||||
if( !ExprHasProperty(pTerm->pExpr, EP_OuterON|EP_InnerON)
|
||||
|| pTerm->pExpr->w.iJoin != pSrc->iCursor
|
||||
){
|
||||
continue;
|
||||
}
|
||||
}
|
||||
nTerm++;
|
||||
pTerm->wtFlags |= TERM_OK;
|
||||
@ -2833,12 +2844,28 @@ static int whereLoopAddBtreeIndex(
|
||||
|
||||
/* tag-20191211-001: Do not allow constraints from the WHERE clause to
|
||||
** be used by the right table of a LEFT JOIN nor by the left table of a
|
||||
** 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_OuterON|EP_InnerON)
|
||||
){
|
||||
continue;
|
||||
** RIGHT JOIN. Only constraints in the ON clause are allowed.
|
||||
** See tag-20191211-002 for the vtab equivalent.
|
||||
**
|
||||
** 2022-06-06: See https://sqlite.org/forum/forumpost/206d99a16dd9212f
|
||||
** for an example of a WHERE clause constraints that may not be used on
|
||||
** the right table of a RIGHT JOIN because the constraint implies a
|
||||
** not-NULL condition on the left table of the RIGHT JOIN.
|
||||
**
|
||||
** 2022-06-10: The same condition applies to termCanDriveIndex() above.
|
||||
** https://sqlite.org/forum/forumpost/51e6959f61
|
||||
*/
|
||||
if( (pSrc->fg.jointype & (JT_LEFT|JT_LTORJ|JT_RIGHT))!=0 ){
|
||||
testcase( (pSrc->fg.jointype & (JT_LEFT|JT_LTORJ|JT_RIGHT))==JT_LEFT );
|
||||
testcase( (pSrc->fg.jointype & (JT_LEFT|JT_LTORJ|JT_RIGHT))==JT_RIGHT );
|
||||
testcase( (pSrc->fg.jointype & (JT_LEFT|JT_LTORJ|JT_RIGHT))==JT_LTORJ );
|
||||
testcase( ExprHasProperty(pTerm->pExpr, EP_OuterON) )
|
||||
testcase( ExprHasProperty(pTerm->pExpr, EP_InnerON) );
|
||||
if( !ExprHasProperty(pTerm->pExpr, EP_OuterON|EP_InnerON)
|
||||
|| pTerm->pExpr->w.iJoin != pSrc->iCursor
|
||||
){
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if( IsUniqueIndex(pProbe) && saved_nEq==pProbe->nKeyCol-1 ){
|
||||
@ -3190,15 +3217,18 @@ static int indexMightHelpWithOrderBy(
|
||||
*/
|
||||
static int whereUsablePartialIndex(
|
||||
int iTab, /* The table for which we want an index */
|
||||
int isLeft, /* True if iTab is the right table of a LEFT JOIN */
|
||||
u8 jointype, /* The JT_* flags on the join */
|
||||
WhereClause *pWC, /* The WHERE clause of the query */
|
||||
Expr *pWhere /* The WHERE clause from the partial index */
|
||||
){
|
||||
int i;
|
||||
WhereTerm *pTerm;
|
||||
Parse *pParse = pWC->pWInfo->pParse;
|
||||
Parse *pParse;
|
||||
|
||||
if( jointype & JT_LTORJ ) return 0;
|
||||
pParse = pWC->pWInfo->pParse;
|
||||
while( pWhere->op==TK_AND ){
|
||||
if( !whereUsablePartialIndex(iTab,isLeft,pWC,pWhere->pLeft) ) return 0;
|
||||
if( !whereUsablePartialIndex(iTab,jointype,pWC,pWhere->pLeft) ) return 0;
|
||||
pWhere = pWhere->pRight;
|
||||
}
|
||||
if( pParse->db->flags & SQLITE_EnableQPSG ) pParse = 0;
|
||||
@ -3206,7 +3236,7 @@ static int whereUsablePartialIndex(
|
||||
Expr *pExpr;
|
||||
pExpr = pTerm->pExpr;
|
||||
if( (!ExprHasProperty(pExpr, EP_OuterON) || pExpr->w.iJoin==iTab)
|
||||
&& (isLeft==0 || ExprHasProperty(pExpr, EP_OuterON))
|
||||
&& ((jointype & JT_OUTER)==0 || ExprHasProperty(pExpr, EP_OuterON))
|
||||
&& sqlite3ExprImpliesExpr(pParse, pExpr, pWhere, iTab)
|
||||
&& (pTerm->wtFlags & TERM_VNULL)==0
|
||||
){
|
||||
@ -3372,9 +3402,8 @@ static int whereLoopAddBtree(
|
||||
for(; rc==SQLITE_OK && pProbe;
|
||||
pProbe=(pSrc->fg.isIndexedBy ? 0 : pProbe->pNext), iSortIdx++
|
||||
){
|
||||
int isLeft = (pSrc->fg.jointype & JT_OUTER)!=0;
|
||||
if( pProbe->pPartIdxWhere!=0
|
||||
&& !whereUsablePartialIndex(pSrc->iCursor, isLeft, pWC,
|
||||
&& !whereUsablePartialIndex(pSrc->iCursor, pSrc->fg.jointype, pWC,
|
||||
pProbe->pPartIdxWhere)
|
||||
){
|
||||
testcase( pNew->iTab!=pSrc->iCursor ); /* See ticket [98d973b8f5] */
|
||||
@ -6034,6 +6063,7 @@ void sqlite3WhereEnd(WhereInfo *pWInfo){
|
||||
SrcList *pTabList = pWInfo->pTabList;
|
||||
sqlite3 *db = pParse->db;
|
||||
int iEnd = sqlite3VdbeCurrentAddr(v);
|
||||
int nRJ = 0;
|
||||
|
||||
/* Generate loop termination code.
|
||||
*/
|
||||
@ -6050,8 +6080,7 @@ void sqlite3WhereEnd(WhereInfo *pWInfo){
|
||||
pRJ->endSubrtn = sqlite3VdbeCurrentAddr(v);
|
||||
sqlite3VdbeAddOp3(v, OP_Return, pRJ->regReturn, pRJ->addrSubrtn, 1);
|
||||
VdbeCoverage(v);
|
||||
assert( pParse->withinRJSubrtn>0 );
|
||||
pParse->withinRJSubrtn--;
|
||||
nRJ++;
|
||||
}
|
||||
pLoop = pLevel->pWLoop;
|
||||
if( pLevel->op!=OP_Noop ){
|
||||
@ -6332,5 +6361,6 @@ void sqlite3WhereEnd(WhereInfo *pWInfo){
|
||||
*/
|
||||
pParse->nQueryLoop = pWInfo->savedNQueryLoop;
|
||||
whereInfoFree(db, pWInfo);
|
||||
pParse->withinRJSubrtn -= nRJ;
|
||||
return;
|
||||
}
|
||||
|
@ -612,8 +612,9 @@ void sqlite3WhereTabFuncArgs(Parse*, SrcItem*, WhereClause*);
|
||||
#define WO_AND 0x0400 /* Two or more AND-connected terms */
|
||||
#define WO_EQUIV 0x0800 /* Of the form A==B, both columns */
|
||||
#define WO_NOOP 0x1000 /* This term does not restrict search space */
|
||||
#define WO_ROWVAL 0x2000 /* A row-value term */
|
||||
|
||||
#define WO_ALL 0x1fff /* Mask of all possible WO_* values */
|
||||
#define WO_ALL 0x3fff /* Mask of all possible WO_* values */
|
||||
#define WO_SINGLE 0x01ff /* Mask of all non-compound WO_* values */
|
||||
|
||||
/*
|
||||
|
@ -2624,6 +2624,9 @@ Bitmask sqlite3WhereCodeOneLoopStart(
|
||||
/* Defer processing WHERE clause constraints until after outer
|
||||
** join processing. tag-20220513a */
|
||||
continue;
|
||||
}else if( (pTabItem->fg.jointype & JT_LEFT)==JT_LEFT
|
||||
&& !ExprHasProperty(pE,EP_OuterON) ){
|
||||
continue;
|
||||
}else{
|
||||
Bitmask m = sqlite3WhereGetMask(&pWInfo->sMaskSet, pE->w.iJoin);
|
||||
if( m & pLevel->notReady ){
|
||||
@ -2859,7 +2862,11 @@ SQLITE_NOINLINE void sqlite3WhereRightJoinLoop(
|
||||
mAll |= pLoop->maskSelf;
|
||||
for(k=0; k<pWC->nTerm; k++){
|
||||
WhereTerm *pTerm = &pWC->a[k];
|
||||
if( pTerm->wtFlags & TERM_VIRTUAL ) break;
|
||||
if( (pTerm->wtFlags & (TERM_VIRTUAL|TERM_SLICE))!=0
|
||||
&& pTerm->eOperator!=WO_ROWVAL
|
||||
){
|
||||
break;
|
||||
}
|
||||
if( pTerm->prereqAll & ~mAll ) continue;
|
||||
if( ExprHasProperty(pTerm->pExpr, EP_OuterON|EP_InnerON) ) continue;
|
||||
pSubWhere = sqlite3ExprAnd(pParse, pSubWhere,
|
||||
|
@ -1105,7 +1105,7 @@ static void exprAnalyze(
|
||||
if( prereqAll!=sqlite3WhereExprUsageNN(pMaskSet, pExpr) ){
|
||||
printf("\n*** Incorrect prereqAll computed for:\n");
|
||||
sqlite3TreeViewExpr(0,pExpr,0);
|
||||
abort();
|
||||
assert( 0 );
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -1411,7 +1411,7 @@ static void exprAnalyze(
|
||||
}
|
||||
pTerm = &pWC->a[idxTerm];
|
||||
pTerm->wtFlags |= TERM_CODED|TERM_VIRTUAL; /* Disable the original */
|
||||
pTerm->eOperator = 0;
|
||||
pTerm->eOperator = WO_ROWVAL;
|
||||
}
|
||||
|
||||
/* If there is a vector IN term - e.g. "(a, b) IN (SELECT ...)" - create
|
||||
@ -1612,7 +1612,7 @@ void sqlite3WhereAddLimit(WhereClause *pWC, Select *p){
|
||||
/* This term is a vector operation that has been decomposed into
|
||||
** other, subsequent terms. It can be ignored. See tag-20220128a */
|
||||
assert( pWC->a[ii].wtFlags & TERM_VIRTUAL );
|
||||
assert( pWC->a[ii].eOperator==0 );
|
||||
assert( pWC->a[ii].eOperator==WO_ROWVAL );
|
||||
continue;
|
||||
}
|
||||
if( pWC->a[ii].leftCursor!=iCsr ) return;
|
||||
|
95
test/changes2.test
Normal file
95
test/changes2.test
Normal file
@ -0,0 +1,95 @@
|
||||
# 2022 June 6
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
set testprefix changes2
|
||||
|
||||
do_execsql_test 1.0 {
|
||||
CREATE TABLE some_table (
|
||||
id INTEGER NOT NULL, value VARCHAR(40) NOT NULL, PRIMARY KEY (id)
|
||||
);
|
||||
INSERT INTO some_table (id, value) VALUES (1, 'v1');
|
||||
} {}
|
||||
|
||||
set ::stmt [sqlite3_prepare_v2 db {
|
||||
UPDATE some_table SET value='v2' WHERE id=1 RETURNING id
|
||||
} -1 dummy]
|
||||
|
||||
do_test 1.1 {
|
||||
list [sqlite3_step $::stmt] [db changes]
|
||||
} {SQLITE_ROW 1}
|
||||
|
||||
do_test 1.2 {
|
||||
list [sqlite3_step $::stmt] [db changes]
|
||||
} {SQLITE_DONE 1}
|
||||
|
||||
sqlite3_reset $::stmt
|
||||
|
||||
do_execsql_test 1.2 {
|
||||
DROP TABLE some_table;
|
||||
CREATE TABLE some_table (
|
||||
id INTEGER NOT NULL, value VARCHAR(40) NOT NULL, PRIMARY KEY (id)
|
||||
);
|
||||
INSERT INTO some_table (id, value) VALUES (1, 'v1');
|
||||
} {}
|
||||
|
||||
do_test 1.3 {
|
||||
list [sqlite3_step $::stmt] [db changes]
|
||||
} {SQLITE_ROW 1}
|
||||
|
||||
do_test 1.4 {
|
||||
list [sqlite3_step $::stmt] [db changes]
|
||||
} {SQLITE_DONE 1}
|
||||
|
||||
sqlite3_finalize $::stmt
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
|
||||
do_execsql_test 2.0 {
|
||||
CREATE TABLE t1(a, b);
|
||||
CREATE TABLE log(t);
|
||||
} {}
|
||||
|
||||
set ::stmt [sqlite3_prepare_v2 db {
|
||||
INSERT INTO log VALUES(changes() || ' changes')
|
||||
} -1 dummy]
|
||||
|
||||
do_execsql_test 2.1 {
|
||||
INSERT INTO t1 VALUES (1, 'v1'), (2, 'v2');
|
||||
} {}
|
||||
|
||||
do_test 2.2 {
|
||||
list [sqlite3_step $::stmt] [sqlite3_reset $::stmt]
|
||||
} {SQLITE_DONE SQLITE_OK}
|
||||
|
||||
do_execsql_test 2.3 {
|
||||
CREATE TABLE t3(x);
|
||||
}
|
||||
|
||||
do_execsql_test 2.2 {
|
||||
INSERT INTO t1 VALUES (3, 'v1'), (4, 'v2');
|
||||
} {}
|
||||
|
||||
do_test 2.3 {
|
||||
list [sqlite3_step $::stmt] [sqlite3_reset $::stmt]
|
||||
} {SQLITE_DONE SQLITE_OK}
|
||||
|
||||
sqlite3_finalize $::stmt
|
||||
|
||||
do_execsql_test 2.4 {
|
||||
SELECT * FROM log;
|
||||
} {{2 changes} {2 changes}}
|
||||
|
||||
finish_test
|
||||
|
@ -193,4 +193,42 @@ do_catchsql_test 7.10 {
|
||||
SELECT matchinfo( f , 'pcx') FROM f WHERE b MATCH x'c533';
|
||||
} {1 {database disk image is malformed}}
|
||||
|
||||
reset_db
|
||||
sqlite3_fts3_may_be_corrupt 1
|
||||
do_execsql_test 8.1 {
|
||||
CREATE VIRTUAL TABLE f USING fts3(a);
|
||||
INSERT INTO f(f) VALUES('nodesize=24');
|
||||
BEGIN;
|
||||
INSERT INTO f VALUES('abcdefghijklmnopqrstuvwxyz0123456789');
|
||||
INSERT INTO f VALUES('abcdefghijklmnopqrstuvwxyz0123456789');
|
||||
INSERT INTO f VALUES('abcdefghijklmnopqrstuvwxyz0123456789');
|
||||
|
||||
INSERT INTO f VALUES('abcdefghijklmnopqrstuvwxyz012345678X');
|
||||
INSERT INTO f VALUES('abcdefghijklmnopqrstuvwxyz012345678X');
|
||||
INSERT INTO f VALUES('abcdefghijklmnopqrstuvwxyz012345678X');
|
||||
COMMIT;
|
||||
BEGIN;
|
||||
INSERT INTO f VALUES('abcdefghijklmnopqrstuvwxyz0123456789');
|
||||
INSERT INTO f VALUES('abcdefghijklmnopqrstuvwxyz0123456789');
|
||||
INSERT INTO f VALUES('abcdefghijklmnopqrstuvwxyz0123456789');
|
||||
|
||||
INSERT INTO f VALUES('abcdefghijklmnopqrstuvwxyz012345678X');
|
||||
INSERT INTO f VALUES('abcdefghijklmnopqrstuvwxyz012345678X');
|
||||
INSERT INTO f VALUES('abcdefghijklmnopqrstuvwxyz012345678X');
|
||||
COMMIT;
|
||||
|
||||
SELECT count(*) FROM f_segments;
|
||||
} {4}
|
||||
|
||||
do_execsql_test 8.2 {
|
||||
UPDATE f_segments SET block = (
|
||||
SELECT block FROM f_segments WHERE blockid=1
|
||||
) WHERE blockid=2
|
||||
}
|
||||
|
||||
do_catchsql_test 8.3 {
|
||||
INSERT INTO f(f) VALUES('merge=2,2');
|
||||
} {1 {database disk image is malformed}}
|
||||
sqlite3_fts3_may_be_corrupt 0
|
||||
|
||||
finish_test
|
||||
|
@ -64,7 +64,8 @@ do_execsql_test 2.1 {
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
do_execsql_test 3.0 {
|
||||
breakpoint
|
||||
do_catchsql_test 3.0 {
|
||||
CREATE VIRTUAL TABLE main.Table0 USING fts3();
|
||||
INSERT INTO Table0 VALUES (1), (printf('%8.1280000X') ), (1), (printf('%8.1280000X') ), (1) ;
|
||||
INSERT INTO Table0 VALUES (0), (printf('%8.1280000X%8.1280000X') ), (1), (printf('%1280000.1280000X%#1280000.1280000E%8.1280000X') ), (1) ;
|
||||
@ -72,7 +73,7 @@ do_execsql_test 3.0 {
|
||||
UPDATE Table0_segdir SET start_block = 1;
|
||||
INSERT INTO Table0 VALUES (1) ;
|
||||
INSERT INTO Table0(Table0) VALUES('merge=6,8');
|
||||
}
|
||||
} {1 {database disk image is malformed}}
|
||||
|
||||
set sqlite_fts3_enable_parentheses $saved_sqlite_fts3_enable_parentheses
|
||||
finish_test
|
||||
|
138
test/fuzzcheck.c
138
test/fuzzcheck.c
@ -153,6 +153,8 @@ static struct GlobalVars {
|
||||
int nSql; /* Number of SQL scripts */
|
||||
Blob *pFirstSql; /* First SQL script */
|
||||
unsigned int uRandom; /* Seed for the SQLite PRNG */
|
||||
unsigned char doInvariantChecks; /* True to run query invariant checks */
|
||||
unsigned int nInvariant; /* Number of invariant checks run */
|
||||
char zTestName[100]; /* Name of current test */
|
||||
} g;
|
||||
|
||||
@ -830,6 +832,13 @@ static int progress_handler(void *pClientData) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Flag bits set by block_troublesome_sql()
|
||||
*/
|
||||
#define BTS_SELECT 0x000001
|
||||
#define BTS_NONSELECT 0x000002
|
||||
#define BTS_BADFUNC 0x000004
|
||||
|
||||
/*
|
||||
** Disallow debugging pragmas such as "PRAGMA vdbe_debug" and
|
||||
** "PRAGMA parser_trace" since they can dramatically increase the
|
||||
@ -838,63 +847,126 @@ static int progress_handler(void *pClientData) {
|
||||
** Also block ATTACH if attaching a file from the filesystem.
|
||||
*/
|
||||
static int block_troublesome_sql(
|
||||
void *Notused,
|
||||
void *pClientData,
|
||||
int eCode,
|
||||
const char *zArg1,
|
||||
const char *zArg2,
|
||||
const char *zArg3,
|
||||
const char *zArg4
|
||||
){
|
||||
(void)Notused;
|
||||
(void)zArg2;
|
||||
unsigned int *pFlags = (unsigned int*)pClientData;
|
||||
(void)zArg3;
|
||||
(void)zArg4;
|
||||
if( eCode==SQLITE_PRAGMA ){
|
||||
if( sqlite3_stricmp("busy_timeout",zArg1)==0
|
||||
&& (zArg2==0 || strtoll(zArg2,0,0)>100 || strtoll(zArg2,0,10)>100)
|
||||
){
|
||||
return SQLITE_DENY;
|
||||
}else if( eVerbosity==0 ){
|
||||
if( sqlite3_strnicmp("vdbe_", zArg1, 5)==0
|
||||
|| sqlite3_stricmp("parser_trace", zArg1)==0
|
||||
|| sqlite3_stricmp("temp_store_directory", zArg1)==0
|
||||
switch( eCode ){
|
||||
case SQLITE_PRAGMA: {
|
||||
if( sqlite3_stricmp("busy_timeout",zArg1)==0
|
||||
&& (zArg2==0 || strtoll(zArg2,0,0)>100 || strtoll(zArg2,0,10)>100)
|
||||
){
|
||||
return SQLITE_DENY;
|
||||
}else if( eVerbosity==0 ){
|
||||
if( sqlite3_strnicmp("vdbe_", zArg1, 5)==0
|
||||
|| sqlite3_stricmp("parser_trace", zArg1)==0
|
||||
|| sqlite3_stricmp("temp_store_directory", zArg1)==0
|
||||
){
|
||||
return SQLITE_DENY;
|
||||
}
|
||||
}else if( sqlite3_stricmp("oom",zArg1)==0
|
||||
&& zArg2!=0 && zArg2[0]!=0 ){
|
||||
oomCounter = atoi(zArg2);
|
||||
}
|
||||
}else if( sqlite3_stricmp("oom",zArg1)==0
|
||||
&& zArg2!=0 && zArg2[0]!=0 ){
|
||||
oomCounter = atoi(zArg2);
|
||||
*pFlags |= BTS_NONSELECT;
|
||||
break;
|
||||
}
|
||||
}else if( eCode==SQLITE_ATTACH ){
|
||||
/* Deny the ATTACH if it is attaching anything other than an in-memory
|
||||
** database. */
|
||||
if( zArg1==0 ) return SQLITE_DENY;
|
||||
if( strcmp(zArg1,":memory:")==0 ) return SQLITE_OK;
|
||||
if( sqlite3_strglob("file:*[?]vfs=memdb", zArg1)==0
|
||||
&& sqlite3_strglob("file:*[^/a-zA-Z0-9_.]*[?]vfs=memdb", zArg1)!=0
|
||||
){
|
||||
return SQLITE_OK;
|
||||
case SQLITE_ATTACH: {
|
||||
/* Deny the ATTACH if it is attaching anything other than an in-memory
|
||||
** database. */
|
||||
*pFlags |= BTS_NONSELECT;
|
||||
if( zArg1==0 ) return SQLITE_DENY;
|
||||
if( strcmp(zArg1,":memory:")==0 ) return SQLITE_OK;
|
||||
if( sqlite3_strglob("file:*[?]vfs=memdb", zArg1)==0
|
||||
&& sqlite3_strglob("file:*[^/a-zA-Z0-9_.]*[?]vfs=memdb", zArg1)!=0
|
||||
){
|
||||
return SQLITE_OK;
|
||||
}
|
||||
return SQLITE_DENY;
|
||||
}
|
||||
case SQLITE_SELECT: {
|
||||
*pFlags |= BTS_SELECT;
|
||||
break;
|
||||
}
|
||||
case SQLITE_FUNCTION: {
|
||||
static const char *azBadFuncs[] = {
|
||||
"random",
|
||||
"randomblob",
|
||||
"rtreedepth",
|
||||
};
|
||||
int i;
|
||||
for(i=0; i<sizeof(azBadFuncs)/sizeof(azBadFuncs[0]); i++){
|
||||
if( sqlite3_stricmp(azBadFuncs[i], zArg2)==0 ){
|
||||
*pFlags |= BTS_BADFUNC;
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SQLITE_READ: {
|
||||
/* Benign */
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
*pFlags |= BTS_NONSELECT;
|
||||
}
|
||||
return SQLITE_DENY;
|
||||
}
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/* Implementation found in fuzzinvariant.c */
|
||||
int fuzz_invariant(
|
||||
sqlite3 *db, /* The database connection */
|
||||
sqlite3_stmt *pStmt, /* Test statement stopped on an SQLITE_ROW */
|
||||
int iCnt, /* Invariant sequence number, starting at 0 */
|
||||
int iRow, /* The row number for pStmt */
|
||||
int *pbCorrupt, /* IN/OUT: Flag indicating a corrupt database file */
|
||||
int eVerbosity /* How much debugging output */
|
||||
);
|
||||
|
||||
/*
|
||||
** Run the SQL text
|
||||
*/
|
||||
static int runDbSql(sqlite3 *db, const char *zSql){
|
||||
static int runDbSql(sqlite3 *db, const char *zSql, unsigned int *pBtsFlags){
|
||||
int rc;
|
||||
sqlite3_stmt *pStmt;
|
||||
int bCorrupt = 0;
|
||||
while( isspace(zSql[0]&0x7f) ) zSql++;
|
||||
if( zSql[0]==0 ) return SQLITE_OK;
|
||||
if( eVerbosity>=4 ){
|
||||
printf("RUNNING-SQL: [%s]\n", zSql);
|
||||
fflush(stdout);
|
||||
}
|
||||
(*pBtsFlags) = 0;
|
||||
rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0);
|
||||
if( rc==SQLITE_OK ){
|
||||
int nRow = 0;
|
||||
while( (rc = sqlite3_step(pStmt))==SQLITE_ROW ){
|
||||
nRow++;
|
||||
if( (*pBtsFlags)==BTS_SELECT
|
||||
&& g.doInvariantChecks
|
||||
&& !sqlite3_stmt_isexplain(pStmt)
|
||||
){
|
||||
int iCnt = 0;
|
||||
for(iCnt=0; iCnt<99999; iCnt++){
|
||||
rc = fuzz_invariant(db, pStmt, iCnt, nRow, &bCorrupt, eVerbosity);
|
||||
if( rc==SQLITE_DONE ) break;
|
||||
if( rc!=SQLITE_ERROR ) g.nInvariant++;
|
||||
if( eVerbosity>0 ){
|
||||
if( rc==SQLITE_OK ){
|
||||
printf("invariant-check: ok\n");
|
||||
}else if( rc==SQLITE_CORRUPT ){
|
||||
printf("invariant-check: failed due to database corruption\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if( eVerbosity>=5 ){
|
||||
int j;
|
||||
for(j=0; j<sqlite3_column_count(pStmt); j++){
|
||||
@ -971,6 +1043,7 @@ int runCombinedDbSqlInput(
|
||||
char *zSql = 0; /* SQL text to run */
|
||||
int nSql; /* Bytes of SQL text */
|
||||
FuzzCtx cx; /* Fuzzing context */
|
||||
unsigned int btsFlags = 0; /* Parsing flags */
|
||||
|
||||
if( nByte<10 ) return 0;
|
||||
if( sqlite3_initialize() ) return 0;
|
||||
@ -1058,7 +1131,7 @@ int runCombinedDbSqlInput(
|
||||
|
||||
/* Block debug pragmas and ATTACH/DETACH. But wait until after
|
||||
** deserialize to do this because deserialize depends on ATTACH */
|
||||
sqlite3_set_authorizer(cx.db, block_troublesome_sql, 0);
|
||||
sqlite3_set_authorizer(cx.db, block_troublesome_sql, &btsFlags);
|
||||
|
||||
#ifdef VT02_SOURCES
|
||||
sqlite3_vt02_init(cx.db, 0, 0);
|
||||
@ -1083,7 +1156,7 @@ int runCombinedDbSqlInput(
|
||||
char cSaved = zSql[i+1];
|
||||
zSql[i+1] = 0;
|
||||
if( sqlite3_complete(zSql+j) ){
|
||||
rc = runDbSql(cx.db, zSql+j);
|
||||
rc = runDbSql(cx.db, zSql+j, &btsFlags);
|
||||
j = i+1;
|
||||
}
|
||||
zSql[i+1] = cSaved;
|
||||
@ -1093,7 +1166,7 @@ int runCombinedDbSqlInput(
|
||||
}
|
||||
}
|
||||
if( j<i ){
|
||||
runDbSql(cx.db, zSql+j);
|
||||
runDbSql(cx.db, zSql+j, &btsFlags);
|
||||
}
|
||||
}
|
||||
testrun_finished:
|
||||
@ -1574,6 +1647,7 @@ static void showHelp(void){
|
||||
" --oss-fuzz Enable OSS-FUZZ testing\n"
|
||||
" --prng-seed N Seed value for the PRGN inside of SQLite\n"
|
||||
" -q|--quiet Reduced output\n"
|
||||
" --query-invariants Run query invariant checks\n"
|
||||
" --rebuild Rebuild and vacuum the database file\n"
|
||||
" --result-trace Show the results of each SQL command\n"
|
||||
" --script Output CLI script instead of running tests\n"
|
||||
@ -1734,6 +1808,9 @@ int main(int argc, char **argv){
|
||||
verboseFlag = 0;
|
||||
eVerbosity = 0;
|
||||
}else
|
||||
if( strcmp(z,"query-invariants")==0 ){
|
||||
g.doInvariantChecks = 1;
|
||||
}else
|
||||
if( strcmp(z,"rebuild")==0 ){
|
||||
rebuildFlag = 1;
|
||||
openFlags4Data = SQLITE_OPEN_READWRITE;
|
||||
@ -2269,6 +2346,9 @@ int main(int argc, char **argv){
|
||||
|
||||
if( !quietFlag && !bScript ){
|
||||
sqlite3_int64 iElapse = timeOfDay() - iBegin;
|
||||
if( g.nInvariant ){
|
||||
printf("fuzzcheck: %u query invariants checked\n", g.nInvariant);
|
||||
}
|
||||
printf("fuzzcheck: 0 errors out of %d tests in %d.%03d seconds\n"
|
||||
"SQLite %s %s\n",
|
||||
nTest, (int)(iElapse/1000), (int)(iElapse%1000),
|
||||
|
Binary file not shown.
316
test/fuzzinvariants.c
Normal file
316
test/fuzzinvariants.c
Normal file
@ -0,0 +1,316 @@
|
||||
/*
|
||||
** 2022-06-14
|
||||
**
|
||||
** 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 library is used by fuzzcheck to test query invariants.
|
||||
**
|
||||
** An sqlite3_stmt is passed in that has just returned SQLITE_ROW. This
|
||||
** routine does:
|
||||
**
|
||||
** * Record the output of the current row
|
||||
** * Construct an alternative query that should return the same row
|
||||
** * Run the alternative query and verify that it does in fact return
|
||||
** the same row
|
||||
**
|
||||
*/
|
||||
#include "sqlite3.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
|
||||
/* Forward references */
|
||||
static char *fuzz_invariant_sql(sqlite3_stmt*, int);
|
||||
static int sameValue(sqlite3_value*,sqlite3_value*);
|
||||
static void reportInvariantFailed(sqlite3_stmt*,sqlite3_stmt*,int);
|
||||
|
||||
/*
|
||||
** Do an invariant check on pStmt. iCnt determines which invariant check to
|
||||
** perform. The first check is iCnt==0.
|
||||
**
|
||||
** *pbCorrupt is a flag that, if true, indicates that the database file
|
||||
** is known to be corrupt. A value of non-zero means "yes, the database
|
||||
** is corrupt". A zero value means "we do not know whether or not the
|
||||
** database is corrupt". The value might be set prior to entry, or this
|
||||
** routine might set the value.
|
||||
**
|
||||
** Return values:
|
||||
**
|
||||
** SQLITE_OK This check was successful.
|
||||
**
|
||||
** SQLITE_DONE iCnt is out of range.
|
||||
**
|
||||
** SQLITE_CORRUPT The invariant failed, but the underlying database
|
||||
** file is indicating that it is corrupt, which might
|
||||
** be the cause of the malfunction.
|
||||
**
|
||||
** SQLITE_INTERNAL The invariant failed, and the database file is not
|
||||
** corrupt. (This never happens because this function
|
||||
** will call abort() following an invariant failure.)
|
||||
**
|
||||
** (other) Some other kind of error occurred.
|
||||
*/
|
||||
int fuzz_invariant(
|
||||
sqlite3 *db, /* The database connection */
|
||||
sqlite3_stmt *pStmt, /* Test statement stopped on an SQLITE_ROW */
|
||||
int iCnt, /* Invariant sequence number, starting at 0 */
|
||||
int iRow, /* The row number for pStmt */
|
||||
int *pbCorrupt, /* IN/OUT: Flag indicating a corrupt database file */
|
||||
int eVerbosity /* How much debugging output */
|
||||
){
|
||||
char *zTest;
|
||||
sqlite3_stmt *pTestStmt = 0;
|
||||
int rc;
|
||||
int i;
|
||||
int nCol;
|
||||
|
||||
if( *pbCorrupt ) return SQLITE_DONE;
|
||||
zTest = fuzz_invariant_sql(pStmt, iCnt);
|
||||
if( zTest==0 ) return SQLITE_DONE;
|
||||
rc = sqlite3_prepare_v2(db, zTest, -1, &pTestStmt, 0);
|
||||
if( rc ){
|
||||
if( eVerbosity ){
|
||||
printf("invariant compile failed: %s\n%s\n",
|
||||
sqlite3_errmsg(db), zTest);
|
||||
}
|
||||
sqlite3_free(zTest);
|
||||
sqlite3_finalize(pTestStmt);
|
||||
return rc;
|
||||
}
|
||||
sqlite3_free(zTest);
|
||||
nCol = sqlite3_column_count(pStmt);
|
||||
for(i=0; i<nCol; i++){
|
||||
sqlite3_bind_value(pTestStmt, i+1, sqlite3_column_value(pStmt,i));
|
||||
}
|
||||
if( eVerbosity>=2 ){
|
||||
char *zSql = sqlite3_expanded_sql(pTestStmt);
|
||||
printf("invariant-sql #%d:\n%s\n", iCnt, zSql);
|
||||
sqlite3_free(zSql);
|
||||
}
|
||||
while( (rc = sqlite3_step(pTestStmt))==SQLITE_ROW ){
|
||||
for(i=0; i<nCol; i++){
|
||||
if( !sameValue(sqlite3_column_value(pStmt,i),
|
||||
sqlite3_column_value(pTestStmt,i)) ) break;
|
||||
}
|
||||
if( i>=nCol ) break;
|
||||
}
|
||||
if( rc!=SQLITE_ROW ){
|
||||
/* No matching output row found */
|
||||
sqlite3_stmt *pCk = 0;
|
||||
rc = sqlite3_prepare_v2(db, "PRAGMA integrity_check", -1, &pCk, 0);
|
||||
if( rc ){
|
||||
sqlite3_finalize(pCk);
|
||||
sqlite3_finalize(pTestStmt);
|
||||
return rc;
|
||||
}
|
||||
rc = sqlite3_step(pCk);
|
||||
if( rc!=SQLITE_ROW
|
||||
|| sqlite3_column_text(pCk, 0)==0
|
||||
|| strcmp((const char*)sqlite3_column_text(pCk,0),"ok")!=0
|
||||
){
|
||||
*pbCorrupt = 1;
|
||||
sqlite3_finalize(pCk);
|
||||
sqlite3_finalize(pTestStmt);
|
||||
return SQLITE_CORRUPT;
|
||||
}
|
||||
sqlite3_finalize(pCk);
|
||||
rc = sqlite3_prepare_v2(db,
|
||||
"SELECT 1 FROM bytecode(?1) WHERE opcode='VOpen'", -1, &pCk, 0);
|
||||
if( rc==SQLITE_OK ) rc = sqlite3_step(pCk);
|
||||
sqlite3_finalize(pCk);
|
||||
if( rc==SQLITE_DONE ){
|
||||
reportInvariantFailed(pStmt, pTestStmt, iRow);
|
||||
return SQLITE_INTERNAL;
|
||||
}else if( eVerbosity>0 ){
|
||||
printf("invariant-error ignored due to the use of virtual tables\n");
|
||||
}
|
||||
}
|
||||
sqlite3_finalize(pTestStmt);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Generate SQL used to test a statement invariant.
|
||||
**
|
||||
** Return 0 if the iCnt is out of range.
|
||||
*/
|
||||
static char *fuzz_invariant_sql(sqlite3_stmt *pStmt, int iCnt){
|
||||
const char *zIn;
|
||||
size_t nIn;
|
||||
const char *zAnd = "WHERE";
|
||||
int i;
|
||||
sqlite3_str *pTest;
|
||||
sqlite3_stmt *pBase = 0;
|
||||
sqlite3 *db = sqlite3_db_handle(pStmt);
|
||||
int rc;
|
||||
int nCol = sqlite3_column_count(pStmt);
|
||||
int mxCnt;
|
||||
int bDistinct = 0;
|
||||
int bOrderBy = 0;
|
||||
|
||||
switch( iCnt % 4 ){
|
||||
case 1: bDistinct = 1; break;
|
||||
case 2: bOrderBy = 1; break;
|
||||
case 3: bDistinct = bOrderBy = 1; break;
|
||||
}
|
||||
iCnt /= 4;
|
||||
if( nCol==1 ){
|
||||
mxCnt = 0;
|
||||
}else{
|
||||
mxCnt = nCol;
|
||||
}
|
||||
if( iCnt<0 || iCnt>mxCnt ) return 0;
|
||||
zIn = sqlite3_sql(pStmt);
|
||||
if( zIn==0 ) return 0;
|
||||
nIn = strlen(zIn);
|
||||
while( nIn>0 && (isspace(zIn[nIn-1]) || zIn[nIn-1]==';') ) nIn--;
|
||||
if( strchr(zIn, '?') ) return 0;
|
||||
pTest = sqlite3_str_new(0);
|
||||
sqlite3_str_appendf(pTest, "SELECT %s* FROM (%.*s)",
|
||||
bDistinct ? "DISTINCT " : "", (int)nIn, zIn);
|
||||
rc = sqlite3_prepare_v2(db, sqlite3_str_value(pTest), -1, &pBase, 0);
|
||||
if( rc ){
|
||||
sqlite3_finalize(pBase);
|
||||
pBase = pStmt;
|
||||
}
|
||||
for(i=0; i<sqlite3_column_count(pStmt); i++){
|
||||
const char *zColName = sqlite3_column_name(pBase,i);
|
||||
const char *zSuffix = strchr(zColName, ':');
|
||||
if( zSuffix
|
||||
&& isdigit(zSuffix[1])
|
||||
&& (zSuffix[1]>'3' || isdigit(zSuffix[2]))
|
||||
){
|
||||
/* This is a randomized column name and so cannot be used in the
|
||||
** WHERE clause. */
|
||||
continue;
|
||||
}
|
||||
if( iCnt>0 && i+1!=iCnt ) continue;
|
||||
if( sqlite3_column_type(pStmt, i)==SQLITE_NULL ){
|
||||
sqlite3_str_appendf(pTest, " %s \"%w\" ISNULL", zAnd, zColName);
|
||||
}else{
|
||||
sqlite3_str_appendf(pTest, " %s \"%w\"=?%d", zAnd, zColName, i+1);
|
||||
}
|
||||
zAnd = "AND";
|
||||
}
|
||||
if( pBase!=pStmt ) sqlite3_finalize(pBase);
|
||||
if( bOrderBy ){
|
||||
sqlite3_str_appendf(pTest, " ORDER BY 1");
|
||||
}
|
||||
return sqlite3_str_finish(pTest);
|
||||
}
|
||||
|
||||
/*
|
||||
** Return true if and only if v1 and is the same as v2.
|
||||
*/
|
||||
static int sameValue(sqlite3_value *v1, sqlite3_value *v2){
|
||||
int x = 1;
|
||||
if( sqlite3_value_type(v1)!=sqlite3_value_type(v2) ) return 0;
|
||||
switch( sqlite3_value_type(v1) ){
|
||||
case SQLITE_INTEGER: {
|
||||
x = sqlite3_value_int64(v1)==sqlite3_value_int64(v2);
|
||||
break;
|
||||
}
|
||||
case SQLITE_FLOAT: {
|
||||
x = sqlite3_value_double(v1)==sqlite3_value_double(v2);
|
||||
break;
|
||||
}
|
||||
case SQLITE_TEXT: {
|
||||
const char *z1 = (const char*)sqlite3_value_text(v1);
|
||||
const char *z2 = (const char*)sqlite3_value_text(v2);
|
||||
x = ((z1==0 && z2==0) || (z1!=0 && z2!=0 && strcmp(z1,z1)==0));
|
||||
break;
|
||||
}
|
||||
case SQLITE_BLOB: {
|
||||
int len1 = sqlite3_value_bytes(v1);
|
||||
const unsigned char *b1 = sqlite3_value_blob(v1);
|
||||
int len2 = sqlite3_value_bytes(v2);
|
||||
const unsigned char *b2 = sqlite3_value_blob(v2);
|
||||
if( len1!=len2 ){
|
||||
x = 0;
|
||||
}else if( len1==0 ){
|
||||
x = 1;
|
||||
}else{
|
||||
x = (b1!=0 && b2!=0 && memcmp(b1,b2,len1)==0);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
/*
|
||||
** Print a single row from the prepared statement
|
||||
*/
|
||||
static void printRow(sqlite3_stmt *pStmt, int iRow){
|
||||
int i, nCol;
|
||||
nCol = sqlite3_column_count(pStmt);
|
||||
for(i=0; i<nCol; i++){
|
||||
printf("row%d.col%d] = ", iRow, i);
|
||||
switch( sqlite3_column_type(pStmt, i) ){
|
||||
case SQLITE_NULL: {
|
||||
printf("NULL\n");
|
||||
break;
|
||||
}
|
||||
case SQLITE_INTEGER: {
|
||||
printf("(integer) %lld\n", sqlite3_column_int64(pStmt, i));
|
||||
break;
|
||||
}
|
||||
case SQLITE_FLOAT: {
|
||||
printf("(float) %f\n", sqlite3_column_double(pStmt, i));
|
||||
break;
|
||||
}
|
||||
case SQLITE_TEXT: {
|
||||
printf("(text) \"%s\"\n", sqlite3_column_text(pStmt, i));
|
||||
break;
|
||||
}
|
||||
case SQLITE_BLOB: {
|
||||
int n = sqlite3_column_bytes(pStmt, i);
|
||||
int j;
|
||||
unsigned const char *data = sqlite3_column_blob(pStmt, i);
|
||||
printf("(blob %d bytes) x'", n);
|
||||
for(j=0; j<20 && j<n; j++){
|
||||
printf("%02x", data[j]);
|
||||
}
|
||||
if( j<n ) printf("...");
|
||||
printf("'\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Report a failure of the invariant: The current output row of pOrig
|
||||
** does not appear in any row of the output from pTest.
|
||||
*/
|
||||
static void reportInvariantFailed(
|
||||
sqlite3_stmt *pOrig, /* The original query */
|
||||
sqlite3_stmt *pTest, /* The alternative test query with a missing row */
|
||||
int iRow /* Row number in pOrig */
|
||||
){
|
||||
int iTestRow = 0;
|
||||
printf("Invariant check failed on row %d.\n", iRow);
|
||||
printf("Original query --------------------------------------------------\n");
|
||||
printf("%s\n", sqlite3_expanded_sql(pOrig));
|
||||
printf("Alternative query -----------------------------------------------\n");
|
||||
printf("%s\n", sqlite3_expanded_sql(pTest));
|
||||
printf("Result row that is missing from the alternative -----------------\n");
|
||||
printRow(pOrig, iRow);
|
||||
printf("Complete results from the alternative query ---------------------\n");
|
||||
sqlite3_reset(pTest);
|
||||
while( sqlite3_step(pTest)==SQLITE_ROW ){
|
||||
iTestRow++;
|
||||
printRow(pTest, iTestRow);
|
||||
}
|
||||
sqlite3_finalize(pTest);
|
||||
abort();
|
||||
}
|
@ -13,6 +13,7 @@
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
set testprefix incrblob3
|
||||
|
||||
ifcapable !incrblob {
|
||||
finish_test
|
||||
@ -273,4 +274,41 @@ do_test incrblob3-7.2 {
|
||||
db close
|
||||
tvfs delete
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
reset_db
|
||||
forcedelete test.db2
|
||||
do_execsql_test 8.1 {
|
||||
CREATE TABLE t1(a INTEGER PRIMARY KEY, b);
|
||||
ATTACH 'test.db2' AS aux;
|
||||
CREATE TABLE aux.t1(a INTEGER PRIMARY KEY, b);
|
||||
|
||||
INSERT INTO t1 VALUES(4, 'hello');
|
||||
INSERT INTO aux.t1 VALUES(4, 'world');
|
||||
}
|
||||
|
||||
do_test 8.2 {
|
||||
set ::blob [db incrblob -readonly main t1 b 4]
|
||||
read $::blob
|
||||
} {hello}
|
||||
close $::blob
|
||||
|
||||
do_test 8.3 {
|
||||
set ::blob [db incrblob -readonly aux t1 b 4]
|
||||
read $::blob
|
||||
} {world}
|
||||
close $::blob
|
||||
|
||||
do_test 8.4 {
|
||||
set ::blob [db incrblob -readonly t1 b 4]
|
||||
read $::blob
|
||||
} {hello}
|
||||
close $::blob
|
||||
|
||||
do_test 8.5 {
|
||||
list [catch { db incrblob -readonly nosuchdb t1 b 4 } msg] $msg
|
||||
} {1 {no such table: nosuchdb.t1}}
|
||||
|
||||
|
||||
db close
|
||||
finish_test
|
||||
|
@ -508,4 +508,24 @@ do_execsql_test index6-18.1 {
|
||||
SELECT * FROM t1 WHERE a IS NOT NULL;
|
||||
} {10 10}
|
||||
|
||||
# 2022-06-09
|
||||
# https://sqlite.org/forum/forumpost/c4676c4956
|
||||
# Cannot do a scan of a partial index on the left table of a RIGHT JOIN
|
||||
# since that will cause extra rows to appear in the output during the
|
||||
# right-join no-match loop. The following testcase is verify using
|
||||
# PostgreSQL 14.
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test index6-19.1 {
|
||||
CREATE TABLE t1(a INT, b INT);
|
||||
INSERT INTO t1(a) VALUES(2);
|
||||
CREATE TABLE t2(c INT);
|
||||
CREATE INDEX i0 ON t2(c) WHERE c=3;
|
||||
CREATE TABLE t3(d INT);
|
||||
INSERT INTO t3 VALUES(1);
|
||||
}
|
||||
do_execsql_test index6-19.2 {
|
||||
SELECT * FROM t2 RIGHT JOIN t3 ON d<>0 LEFT JOIN t1 ON c=3 WHERE t1.a<>0;
|
||||
} {}
|
||||
|
||||
finish_test
|
||||
|
@ -34,6 +34,7 @@ do_execsql_test 1.0 {
|
||||
CREATE TABLE t1(x, y);
|
||||
CREATE INDEX t1x ON t1(x) WHERE y=45;
|
||||
}
|
||||
unset -nocomplain a
|
||||
set y [expr 45]
|
||||
do_sqluses_test 1.1 { SELECT * FROM t1 WHERE x=? AND y=$y } {t1 t1x}
|
||||
set y [expr 45.1]
|
||||
|
@ -1067,4 +1067,38 @@ do_catchsql_test join-26.1 {
|
||||
SELECT * FROM t5 JOIN ((t4 JOIN (t5 JOIN t6)) t7);
|
||||
} {/1 {.*}/}
|
||||
|
||||
# 2022-06-09 Invalid subquery flattening caused by
|
||||
# check-in 3f45007d544e5f78 and detected by dbsqlfuzz
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test join-27.1 {
|
||||
CREATE TABLE t1(a INT,b INT,c INT); INSERT INTO t1 VALUES(NULL,NULL,NULL);
|
||||
CREATE TABLE t2(d INT,e INT); INSERT INTO t2 VALUES(NULL,NULL);
|
||||
CREATE INDEX x2 ON t1(c,b);
|
||||
CREATE TABLE t3(x INT); INSERT INTO t3 VALUES(NULL);
|
||||
}
|
||||
do_execsql_test join-27.2 {
|
||||
WITH t99(b) AS MATERIALIZED (
|
||||
SELECT b FROM t2 LEFT JOIN t1 ON c IN (SELECT x FROM t3)
|
||||
)
|
||||
SELECT 5 FROM t2 JOIN t99 ON b IN (1,2,3);
|
||||
} {}
|
||||
do_execsql_test join-27.3 {
|
||||
WITH t99(b) AS NOT MATERIALIZED (
|
||||
SELECT b FROM t2 LEFT JOIN t1 ON c IN (SELECT x FROM t3)
|
||||
)
|
||||
SELECT 5 FROM t2 JOIN t99 ON b IN (1,2,3);
|
||||
} {}
|
||||
do_execsql_test join-27.4 {
|
||||
WITH t99(b) AS (SELECT b FROM t2 LEFT JOIN t1 ON c IN (SELECT x FROM t3))
|
||||
SELECT 5 FROM t2 JOIN t99 ON b IN (1,2,3);
|
||||
} {}
|
||||
do_execsql_test join-27.5 {
|
||||
SELECT 5
|
||||
FROM t2 JOIN (
|
||||
SELECT b FROM t2 LEFT JOIN t1 ON c IN (SELECT x FROM t3)
|
||||
) AS t99 ON b IN (1,2,3);
|
||||
} {}
|
||||
|
||||
|
||||
finish_test
|
||||
|
@ -139,6 +139,72 @@ foreach {id schema} {
|
||||
1 3 3 33
|
||||
1 4 4 44
|
||||
}
|
||||
do_execsql_test join7-$id.32 {
|
||||
SELECT t1.*, t2.* FROM t2 FULL OUTER JOIN t1 ON b=c
|
||||
WHERE b=c
|
||||
ORDER BY +b;
|
||||
} {
|
||||
1 3 3 33
|
||||
1 4 4 44
|
||||
}
|
||||
do_execsql_test join7-$id.33 {
|
||||
SELECT t1.*, t2.* FROM t2 FULL OUTER JOIN t1 ON b=c
|
||||
WHERE b>0
|
||||
ORDER BY +b;
|
||||
} {
|
||||
1 2 NULL NULL
|
||||
1 3 3 33
|
||||
1 4 4 44
|
||||
}
|
||||
do_execsql_test join7-$id.34 {
|
||||
SELECT t1.*, t2.* FROM t2 FULL OUTER JOIN t1 ON b=c
|
||||
WHERE b>0 OR b IS NULL
|
||||
ORDER BY +b;
|
||||
} {
|
||||
NULL NULL 5 55
|
||||
1 2 NULL NULL
|
||||
1 3 3 33
|
||||
1 4 4 44
|
||||
}
|
||||
do_execsql_test join7-$id.35 {
|
||||
SELECT t1.*, t2.* FROM t2 FULL OUTER JOIN t1 ON b=c AND b>3 AND c>4
|
||||
ORDER BY coalesce(b,c,0);
|
||||
} {
|
||||
1 2 NULL NULL
|
||||
NULL NULL 3 33
|
||||
1 3 NULL NULL
|
||||
NULL NULL 4 44
|
||||
1 4 NULL NULL
|
||||
NULL NULL 5 55
|
||||
}
|
||||
do_execsql_test join7-$id.36 {
|
||||
SELECT t1.*, t2.* FROM t2 FULL OUTER JOIN t1 ON b=c AND b>3 WHERE c>4
|
||||
ORDER BY coalesce(b,c,0);
|
||||
} {
|
||||
NULL NULL 5 55
|
||||
}
|
||||
do_execsql_test join7-$id.37 {
|
||||
SELECT t1.*, t2.* FROM t2 FULL OUTER JOIN t1 ON b=c WHERE b>3 AND c>4
|
||||
ORDER BY coalesce(b,c,0);
|
||||
} {
|
||||
}
|
||||
do_execsql_test join7-$id.38 {
|
||||
SELECT t1.*, t2.* FROM t2 FULL OUTER JOIN t1 ON b=c WHERE b>3 OR c>4
|
||||
ORDER BY coalesce(b,c,0);
|
||||
} {
|
||||
1 4 4 44
|
||||
NULL NULL 5 55
|
||||
}
|
||||
do_execsql_test join7-$id.39 {
|
||||
SELECT t1.*, t2.* FROM t2 FULL OUTER JOIN t1 ON b=c AND (b>3 OR c>4)
|
||||
ORDER BY coalesce(b,c,0);
|
||||
} {
|
||||
1 2 NULL NULL
|
||||
NULL NULL 3 33
|
||||
1 3 NULL NULL
|
||||
1 4 4 44
|
||||
NULL NULL 5 55
|
||||
}
|
||||
do_execsql_test join7-$id.40 {
|
||||
SELECT * FROM t1 RIGHT OUTER JOIN t2 ON b=c ORDER BY +b;
|
||||
} {
|
||||
|
302
test/join8.test
302
test/join8.test
@ -445,4 +445,306 @@ do_execsql_test join8-14020 {
|
||||
- 2 4
|
||||
}
|
||||
|
||||
# 2022-05-30
|
||||
# https://sqlite.org/forum/forumpost/3902c7b833
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test join8-15000 {
|
||||
CREATE TABLE t1(x INT);
|
||||
CREATE TABLE t2(y INT);
|
||||
CREATE TABLE t3(z INT);
|
||||
INSERT INTO t1 VALUES(10);
|
||||
INSERT INTO t3 VALUES(20),(30);
|
||||
}
|
||||
do_execsql_test join8-15010 {
|
||||
SELECT * FROM t1 LEFT JOIN t2 ON true JOIN t3 ON t2.y IS NOT NULL;
|
||||
} {}
|
||||
do_execsql_test join8-15020 {
|
||||
SELECT * FROM t1 LEFT JOIN t2 ON true JOIN t3 ON t2.y IS NOT NULL
|
||||
WHERE (t3.z!=400 AND t3.z!=500 AND t3.z!=600);
|
||||
} {}
|
||||
do_execsql_test join8-15100 {
|
||||
PRAGMA automatic_index = 0;
|
||||
CREATE TABLE t4(x TEXT);
|
||||
CREATE TABLE t5(y TEXT);
|
||||
CREATE TABLE t6(z TEXT);
|
||||
INSERT INTO t4 VALUES('a'), ('b');
|
||||
INSERT INTO t5 VALUES('b'), ('c');
|
||||
INSERT INTO t6 VALUES('a'), ('d');
|
||||
} {}
|
||||
db null -
|
||||
do_execsql_test join8-15110 {
|
||||
SELECT * FROM t4 LEFT JOIN t5 ON x=y LEFT JOIN t6 ON (x=z) ORDER BY +x;
|
||||
} {a - a b b -}
|
||||
do_execsql_test join8-15120 {
|
||||
SELECT * FROM t4 LEFT JOIN t5 ON x=y LEFT JOIN t6 ON (x=z)
|
||||
WHERE t5.y!='x' AND t4.x!='x';
|
||||
} {b b -}
|
||||
|
||||
# 2022-05-31
|
||||
# https://sqlite.org/forum/forumpost/c2554d560b
|
||||
reset_db
|
||||
do_execsql_test join8-16000 {
|
||||
CREATE TABLE t1(a TEXT);
|
||||
CREATE TABLE t2(b TEXT);
|
||||
CREATE TABLE t3(c TEXT);
|
||||
INSERT INTO t2(b) VALUES ('x');
|
||||
INSERT INTO t3(c) VALUES ('y'), ('z');
|
||||
} {}
|
||||
db null -
|
||||
do_execsql_test join8-16010 {
|
||||
SELECT * FROM t1 RIGHT JOIN t2 ON true LEFT JOIN t3 ON a<>'';
|
||||
} {- x -}
|
||||
do_execsql_test join8-16020 {
|
||||
SELECT * FROM t1 RIGHT JOIN t2 ON true LEFT JOIN t3 ON a<>'' WHERE c IS NULL;
|
||||
} {- x -}
|
||||
do_execsql_test join8-16020 {
|
||||
SELECT * FROM t1 RIGHT JOIN t2 ON true JOIN t3 ON a<>'' WHERE c IS NULL;
|
||||
} {}
|
||||
do_execsql_test join8-16030 {
|
||||
SELECT * FROM t1 RIGHT JOIN t2 ON true JOIN t3 ON a<>'';
|
||||
} {}
|
||||
do_execsql_test join8-16040 {
|
||||
SELECT * FROM t1 RIGHT JOIN t2 ON true LEFT JOIN t3 ON a<>'' WHERE c<>'';
|
||||
} {}
|
||||
do_execsql_test join8-16050 {
|
||||
SELECT * FROM t1 RIGHT JOIN t2 ON true LEFT JOIN t3 ON a<>'' WHERE c IS NOT NULL;
|
||||
} {}
|
||||
do_execsql_test join8-16060 {
|
||||
SELECT * FROM t1 RIGHT JOIN t2 ON true JOIN t3 ON a<>'' WHERE c<>'';
|
||||
} {}
|
||||
do_execsql_test join8-16070 {
|
||||
SELECT * FROM t1 RIGHT JOIN t2 ON true JOIN t3 ON a<>'' WHERE c IS NOT NULL;
|
||||
} {}
|
||||
|
||||
# 2022-06-01
|
||||
# https://sqlite.org/forum/forumpost/087de2d9ec
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test join8-17000 {
|
||||
CREATE TABLE t1(id INTEGER PRIMARY KEY, x INT, y INT);
|
||||
CREATE TABLE t2(z INT);
|
||||
INSERT INTO t1(id,x,y) VALUES(1, 0, 0);
|
||||
} {}
|
||||
db null NULL
|
||||
do_execsql_test join8-17010 {
|
||||
SELECT * FROM t2 RIGHT JOIN t1 ON true;
|
||||
} {NULL 1 0 0}
|
||||
do_execsql_test join8-17020 {
|
||||
SELECT 99=id AND 0=y AS "truth" FROM t2 RIGHT JOIN t1 ON true;
|
||||
} {0}
|
||||
do_execsql_test join8-17030 {
|
||||
SELECT (99, 0)==(id, y) AS "truth" FROM t2 RIGHT JOIN t1;
|
||||
} {0}
|
||||
do_execsql_test join8-17040 {
|
||||
SELECT * FROM t2 RIGHT JOIN t1 WHERE 99=id AND 0=y;
|
||||
} {}
|
||||
do_execsql_test join8-17041 {
|
||||
SELECT * FROM t2 RIGHT JOIN t1 WHERE 99=+id AND 0=y;
|
||||
} {}
|
||||
do_execsql_test join8-17050 {
|
||||
SELECT * FROM t2 RIGHT JOIN t1 WHERE (99, 0)==(id,y);
|
||||
} {}
|
||||
do_execsql_test join8-17051 {
|
||||
SELECT * FROM t2 RIGHT JOIN t1 WHERE (99, 0)==(+id,y);
|
||||
} {}
|
||||
do_execsql_test join8-17060 {
|
||||
SELECT * FROM t2 RIGHT JOIN t1 WHERE 1=id AND 0=y;
|
||||
} {NULL 1 0 0}
|
||||
do_execsql_test join8-17061 {
|
||||
SELECT * FROM t2 RIGHT JOIN t1 WHERE 1=+id AND 0=y;
|
||||
} {NULL 1 0 0}
|
||||
do_execsql_test join8-17070 {
|
||||
SELECT * FROM t2 RIGHT JOIN t1 WHERE (1, 0)==(id,y);
|
||||
} {NULL 1 0 0}
|
||||
do_execsql_test join8-17071 {
|
||||
SELECT * FROM t2 RIGHT JOIN t1 WHERE (1, 0)==(+id,y);
|
||||
} {NULL 1 0 0}
|
||||
do_execsql_test join8-17080 {
|
||||
CREATE TABLE t3(a INTEGER PRIMARY KEY, b INT);
|
||||
CREATE TABLE t4(x INT, y INT);
|
||||
INSERT INTO t3(a,b) VALUES(1, 3);
|
||||
} {}
|
||||
do_execsql_test join8-17090 {
|
||||
SELECT t3.a FROM t4 RIGHT JOIN t3 ON (x=a) WHERE (b, 4)=(SELECT 3, 4);
|
||||
} {1}
|
||||
do_execsql_test join8-17091 {
|
||||
SELECT t3.a FROM t4 RIGHT JOIN t3 ON (x=a) WHERE (b, 4) IS (SELECT 3, 4);
|
||||
} {1}
|
||||
|
||||
# 2022-06-06
|
||||
# https://sqlite.org/forum/forumpost/206d99a16dd9212f
|
||||
# tag-20191211-001
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test join8-18000 {
|
||||
CREATE TABLE t1(a BOOLEAN); INSERT INTO t1 VALUES (false);
|
||||
CREATE TABLE t2(x INT); INSERT INTO t2 VALUES (0);
|
||||
SELECT *, x NOTNULL, (x NOTNULL)=a FROM t2 RIGHT JOIN t1 ON true WHERE (x NOTNULL)=a;
|
||||
} {}
|
||||
do_execsql_test join8-18010 {
|
||||
CREATE INDEX t1a ON t1(a);
|
||||
SELECT *, x NOTNULL, (x NOTNULL)=a FROM t2 RIGHT JOIN t1 ON true WHERE (x NOTNULL)=a;
|
||||
} {}
|
||||
|
||||
do_execsql_test join8-18020 {
|
||||
CREATE TABLE t3(z);
|
||||
INSERT INTO t3 VALUES('t3value');
|
||||
SELECT *, x NOTNULL, (x NOTNULL)=a FROM t2 RIGHT JOIN t1 ON true INNER JOIN t3 ON (x NOTNULL)=a;
|
||||
} {}
|
||||
|
||||
ifcapable rtree {
|
||||
do_execsql_test join8-18030 {
|
||||
CREATE VIRTUAL TABLE rtree1 USING rtree(a, x1, x2);
|
||||
INSERT INTO rtree1 VALUES(0, 0, 0);
|
||||
}
|
||||
do_execsql_test join8-18040 {
|
||||
SELECT *, x NOTNULL, (x NOTNULL)=a FROM t2
|
||||
RIGHT JOIN rtree1 ON true INNER JOIN t3 ON (x NOTNULL)=+a;
|
||||
} {}
|
||||
do_execsql_test join8-18050 {
|
||||
SELECT *, x NOTNULL, (x NOTNULL)=a FROM t2
|
||||
RIGHT JOIN rtree1 ON true INNER JOIN t3 ON (x NOTNULL)=a;
|
||||
} {}
|
||||
}
|
||||
|
||||
|
||||
reset_db
|
||||
do_execsql_test join8-19000 {
|
||||
CREATE TABLE t1(a INT);
|
||||
CREATE TABLE t2(b INT, c INT);
|
||||
CREATE TABLE t3(d INT);
|
||||
|
||||
INSERT INTO t1 VALUES(10);
|
||||
INSERT INTO t2 VALUES(50,51);
|
||||
INSERT INTO t3 VALUES(299);
|
||||
|
||||
CREATE INDEX t2b ON t2( (b IS NOT NULL) );
|
||||
}
|
||||
|
||||
do_execsql_test join8-19010 {
|
||||
SELECT * FROM t1 LEFT JOIN t2 ON true INNER JOIN t3 ON (b IS NOT NULL)=0;
|
||||
}
|
||||
|
||||
# 2022-06-07
|
||||
# https://sqlite.org/forum/forumpost/323f86cc30
|
||||
reset_db
|
||||
do_execsql_test join8-20000 {
|
||||
CREATE TABLE t1(x TEXT);
|
||||
INSERT INTO t1(x) VALUES('aaa');
|
||||
CREATE VIEW v0(y) AS SELECT x FROM t1;
|
||||
CREATE TABLE t2(z TEXT);
|
||||
} {}
|
||||
db null -
|
||||
do_execsql_test join8-20010 {
|
||||
SELECT * FROM t2 JOIN v0 ON z<>'bbb' RIGHT JOIN t1 ON z<>'ccc';
|
||||
} {- - aaa}
|
||||
do_execsql_test join8-20020 {
|
||||
SELECT * FROM t2 JOIN v0 ON z<>'bbb' RIGHT JOIN t1 ON z<>'ccc' ORDER BY z;
|
||||
} {- - aaa}
|
||||
do_execsql_test join8-20030 {
|
||||
SELECT 99 as "m" FROM t2 JOIN v0 ON z<>'bbb' RIGHT JOIN t1 ON z<>'ccc';
|
||||
} {99}
|
||||
do_execsql_test join8-20040 {
|
||||
SELECT 99 as "m" FROM t2 JOIN v0 ON z<>'bbb' RIGHT JOIN t1 ON z<>'ccc' ORDER BY z;
|
||||
} {99}
|
||||
do_execsql_test join8-20050 {
|
||||
SELECT count(*)
|
||||
FROM (SELECT 99 as "m" FROM t2 JOIN v0 ON z<>'' RIGHT JOIN t1 ON z<>'') AS "t3";
|
||||
} {1}
|
||||
do_execsql_test join8-20060 {
|
||||
SELECT count(*)
|
||||
FROM (SELECT 99 as "m" FROM t2 JOIN v0 ON z<>'' RIGHT JOIN t1 ON z<>'' ORDER BY z) AS "t3";
|
||||
} {1}
|
||||
|
||||
# 2022-06-10
|
||||
# https://sqlite.org/forum/forumpost/8e4c352937e82929
|
||||
#
|
||||
# Do not allow constant propagation between ON and WHERE clause terms.
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test join8-21000 {
|
||||
CREATE TABLE t1(a INT,b BOOLEAN);
|
||||
CREATE TABLE t2(c INT); INSERT INTO t2 VALUES(NULL);
|
||||
CREATE TABLE t3(d INT);
|
||||
}
|
||||
do_execsql_test join8-21010 {
|
||||
SELECT (b IS TRUE) FROM t1 JOIN t3 ON (b=TRUE) RIGHT JOIN t2 ON TRUE;
|
||||
} {0}
|
||||
do_execsql_test join8-22020 {
|
||||
SELECT * FROM t1 JOIN t3 ON (b=TRUE) RIGHT JOIN t2 ON TRUE WHERE (b IS TRUE);
|
||||
} {}
|
||||
|
||||
# 2022-06-10
|
||||
# https://sqlite.org/forum/forumpost/51e6959f61
|
||||
#
|
||||
# Restrictions on the usage of WHERE clause constraints by joins that are
|
||||
# involved with a RIGHT JOIN must also be applied to automatic indexes.
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test join8-22000 {
|
||||
CREATE TABLE t1(a INT);
|
||||
CREATE TABLE t2(b INT);
|
||||
CREATE TABLE t3(c TEXT); INSERT INTO t3 VALUES('x');
|
||||
CREATE TABLE t4(d TEXT); INSERT INTO t4 VALUES('y');
|
||||
SELECT 99
|
||||
FROM t1
|
||||
LEFT JOIN t2 ON true
|
||||
RIGHT JOIN t3 ON true
|
||||
RIGHT JOIN t4 ON true
|
||||
WHERE a=b;
|
||||
} {}
|
||||
|
||||
# 2022-06-13
|
||||
# https://sqlite.org/forum/forumpost/b40696f501
|
||||
#
|
||||
# This optimization that converts "x ISNULL" into "FALSE" when column "x" has a
|
||||
# NOT NULL constraint is too aggresive if the query contains RIGHT JOIN.
|
||||
#
|
||||
reset_db
|
||||
db null -
|
||||
do_execsql_test join8-23000 {
|
||||
CREATE TABLE t1(a TEXT);
|
||||
INSERT INTO t1 VALUES('c');
|
||||
CREATE TABLE t2(b TEXT, c TEXT NOT NULL);
|
||||
INSERT INTO t2 VALUES('a', 'b');
|
||||
CREATE TABLE t3(d TEXT);
|
||||
INSERT INTO t3 VALUES('x');
|
||||
CREATE TABLE t4(e TEXT);
|
||||
INSERT INTO t4 VALUES('y');
|
||||
}
|
||||
do_execsql_test join8-23010 {
|
||||
SELECT *
|
||||
FROM t1
|
||||
LEFT JOIN t2 ON TRUE
|
||||
JOIN t3 ON c=''
|
||||
RIGHT JOIN t4 ON b='';
|
||||
} {- - - - y}
|
||||
do_execsql_test join8-23020 {
|
||||
SELECT *
|
||||
FROM t1
|
||||
LEFT JOIN t2 ON TRUE
|
||||
JOIN t3 ON c=''
|
||||
RIGHT JOIN t4 ON b=''
|
||||
WHERE d ISNULL
|
||||
} {- - - - y}
|
||||
|
||||
# 2022-06-14
|
||||
# dbsqlfuzz 2f3101834d14325a976f601b9267a0fd323d6bbd
|
||||
#
|
||||
# When the OP_NullRow opcode creates a new cursor, it must
|
||||
# set the cursor to no-reuse so that an OP_OpenEphemeral in
|
||||
# a subroutine does not try to reuse it.
|
||||
#
|
||||
reset_db
|
||||
db null -
|
||||
do_execsql_test join8-24000 {
|
||||
CREATE TABLE t4(b INT, c INT);
|
||||
CREATE TABLE t5(a INT, f INT);
|
||||
INSERT INTO t5 VALUES(1,2);
|
||||
WITH t7(x, y) AS (SELECT 100, 200 FROM t5)
|
||||
SELECT * FROM t4 JOIN t7 ON true RIGHT JOIN (SELECT y AS z FROM t7) AS t6 ON (x=z);
|
||||
} {- - - - 200}
|
||||
|
||||
|
||||
finish_test
|
||||
|
36052
test/joinD.test
36052
test/joinD.test
File diff suppressed because it is too large
Load Diff
613
test/joinF.test
Normal file
613
test/joinF.test
Normal file
@ -0,0 +1,613 @@
|
||||
# 2022-05-31
|
||||
#
|
||||
# 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
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
# A few extra tests that were not generated from postgresql output are
|
||||
# added at the end.
|
||||
#
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
db nullvalue -
|
||||
db eval {
|
||||
CREATE TABLE t1(x INT);
|
||||
CREATE TABLE t2(y INT);
|
||||
CREATE TABLE t3(z INT);
|
||||
CREATE TABLE t4(w INT);
|
||||
INSERT INTO t1 VALUES(10);
|
||||
INSERT INTO t3 VALUES(20),(30);
|
||||
INSERT INTO t4 VALUES(50);
|
||||
}
|
||||
do_execsql_test joinF-1 {
|
||||
SELECT *
|
||||
FROM t1 INNER JOIN t2 ON true
|
||||
INNER JOIN t3 ON t2.y IS NOT NULL
|
||||
INNER JOIN t4 ON true
|
||||
ORDER BY coalesce(t1.x,t2.y,t3.z,t4.w,0);
|
||||
} {
|
||||
}
|
||||
do_execsql_test joinF-2 {
|
||||
SELECT *
|
||||
FROM t1 INNER JOIN t2 ON true
|
||||
INNER JOIN t3 ON t2.y IS NOT NULL
|
||||
INNER JOIN t4 ON true
|
||||
WHERE (t3.z!=400 AND t3.z!=500 AND t3.z!=600)
|
||||
ORDER BY coalesce(t1.x,t2.y,t3.z,t4.w,0);
|
||||
} {
|
||||
}
|
||||
do_execsql_test joinF-3 {
|
||||
SELECT *
|
||||
FROM t1 INNER JOIN t2 ON true
|
||||
INNER JOIN t3 ON t2.y IS NOT NULL
|
||||
LEFT JOIN t4 ON true
|
||||
ORDER BY coalesce(t1.x,t2.y,t3.z,t4.w,0);
|
||||
} {
|
||||
}
|
||||
do_execsql_test joinF-4 {
|
||||
SELECT *
|
||||
FROM t1 INNER JOIN t2 ON true
|
||||
INNER JOIN t3 ON t2.y IS NOT NULL
|
||||
LEFT JOIN t4 ON true
|
||||
WHERE (t3.z!=400 AND t3.z!=500 AND t3.z!=600)
|
||||
ORDER BY coalesce(t1.x,t2.y,t3.z,t4.w,0);
|
||||
} {
|
||||
}
|
||||
do_execsql_test joinF-5 {
|
||||
SELECT *
|
||||
FROM t1 INNER JOIN t2 ON true
|
||||
INNER JOIN t3 ON t2.y IS NOT NULL
|
||||
RIGHT JOIN t4 ON true
|
||||
ORDER BY coalesce(t1.x,t2.y,t3.z,t4.w,0);
|
||||
} {
|
||||
- - - 50
|
||||
}
|
||||
do_execsql_test joinF-6 {
|
||||
SELECT *
|
||||
FROM t1 INNER JOIN t2 ON true
|
||||
INNER JOIN t3 ON t2.y IS NOT NULL
|
||||
RIGHT JOIN t4 ON true
|
||||
WHERE (t3.z!=400 AND t3.z!=500 AND t3.z!=600)
|
||||
ORDER BY coalesce(t1.x,t2.y,t3.z,t4.w,0);
|
||||
} {
|
||||
}
|
||||
do_execsql_test joinF-7 {
|
||||
SELECT *
|
||||
FROM t1 INNER JOIN t2 ON true
|
||||
LEFT JOIN t3 ON t2.y IS NOT NULL
|
||||
INNER JOIN t4 ON true
|
||||
ORDER BY coalesce(t1.x,t2.y,t3.z,t4.w,0);
|
||||
} {
|
||||
}
|
||||
do_execsql_test joinF-8 {
|
||||
SELECT *
|
||||
FROM t1 INNER JOIN t2 ON true
|
||||
LEFT JOIN t3 ON t2.y IS NOT NULL
|
||||
INNER JOIN t4 ON true
|
||||
WHERE (t3.z!=400 AND t3.z!=500 AND t3.z!=600)
|
||||
ORDER BY coalesce(t1.x,t2.y,t3.z,t4.w,0);
|
||||
} {
|
||||
}
|
||||
do_execsql_test joinF-9 {
|
||||
SELECT *
|
||||
FROM t1 INNER JOIN t2 ON true
|
||||
LEFT JOIN t3 ON t2.y IS NOT NULL
|
||||
LEFT JOIN t4 ON true
|
||||
ORDER BY coalesce(t1.x,t2.y,t3.z,t4.w,0);
|
||||
} {
|
||||
}
|
||||
do_execsql_test joinF-10 {
|
||||
SELECT *
|
||||
FROM t1 INNER JOIN t2 ON true
|
||||
LEFT JOIN t3 ON t2.y IS NOT NULL
|
||||
LEFT JOIN t4 ON true
|
||||
WHERE (t3.z!=400 AND t3.z!=500 AND t3.z!=600)
|
||||
ORDER BY coalesce(t1.x,t2.y,t3.z,t4.w,0);
|
||||
} {
|
||||
}
|
||||
do_execsql_test joinF-11 {
|
||||
SELECT *
|
||||
FROM t1 INNER JOIN t2 ON true
|
||||
LEFT JOIN t3 ON t2.y IS NOT NULL
|
||||
RIGHT JOIN t4 ON true
|
||||
ORDER BY coalesce(t1.x,t2.y,t3.z,t4.w,0);
|
||||
} {
|
||||
- - - 50
|
||||
}
|
||||
do_execsql_test joinF-12 {
|
||||
SELECT *
|
||||
FROM t1 INNER JOIN t2 ON true
|
||||
LEFT JOIN t3 ON t2.y IS NOT NULL
|
||||
RIGHT JOIN t4 ON true
|
||||
WHERE (t3.z!=400 AND t3.z!=500 AND t3.z!=600)
|
||||
ORDER BY coalesce(t1.x,t2.y,t3.z,t4.w,0);
|
||||
} {
|
||||
}
|
||||
do_execsql_test joinF-13 {
|
||||
SELECT *
|
||||
FROM t1 INNER JOIN t2 ON true
|
||||
RIGHT JOIN t3 ON t2.y IS NOT NULL
|
||||
INNER JOIN t4 ON true
|
||||
ORDER BY coalesce(t1.x,t2.y,t3.z,t4.w,0);
|
||||
} {
|
||||
- - 20 50
|
||||
- - 30 50
|
||||
}
|
||||
do_execsql_test joinF-14 {
|
||||
SELECT *
|
||||
FROM t1 INNER JOIN t2 ON true
|
||||
RIGHT JOIN t3 ON t2.y IS NOT NULL
|
||||
INNER JOIN t4 ON true
|
||||
WHERE (t3.z!=400 AND t3.z!=500 AND t3.z!=600)
|
||||
ORDER BY coalesce(t1.x,t2.y,t3.z,t4.w,0);
|
||||
} {
|
||||
- - 20 50
|
||||
- - 30 50
|
||||
}
|
||||
do_execsql_test joinF-15 {
|
||||
SELECT *
|
||||
FROM t1 INNER JOIN t2 ON true
|
||||
RIGHT JOIN t3 ON t2.y IS NOT NULL
|
||||
LEFT JOIN t4 ON true
|
||||
ORDER BY coalesce(t1.x,t2.y,t3.z,t4.w,0);
|
||||
} {
|
||||
- - 20 50
|
||||
- - 30 50
|
||||
}
|
||||
do_execsql_test joinF-16 {
|
||||
SELECT *
|
||||
FROM t1 INNER JOIN t2 ON true
|
||||
RIGHT JOIN t3 ON t2.y IS NOT NULL
|
||||
LEFT JOIN t4 ON true
|
||||
WHERE (t3.z!=400 AND t3.z!=500 AND t3.z!=600)
|
||||
ORDER BY coalesce(t1.x,t2.y,t3.z,t4.w,0);
|
||||
} {
|
||||
- - 20 50
|
||||
- - 30 50
|
||||
}
|
||||
do_execsql_test joinF-17 {
|
||||
SELECT *
|
||||
FROM t1 INNER JOIN t2 ON true
|
||||
RIGHT JOIN t3 ON t2.y IS NOT NULL
|
||||
RIGHT JOIN t4 ON true
|
||||
ORDER BY coalesce(t1.x,t2.y,t3.z,t4.w,0);
|
||||
} {
|
||||
- - 20 50
|
||||
- - 30 50
|
||||
}
|
||||
do_execsql_test joinF-18 {
|
||||
SELECT *
|
||||
FROM t1 INNER JOIN t2 ON true
|
||||
RIGHT JOIN t3 ON t2.y IS NOT NULL
|
||||
RIGHT JOIN t4 ON true
|
||||
WHERE (t3.z!=400 AND t3.z!=500 AND t3.z!=600)
|
||||
ORDER BY coalesce(t1.x,t2.y,t3.z,t4.w,0);
|
||||
} {
|
||||
- - 20 50
|
||||
- - 30 50
|
||||
}
|
||||
do_execsql_test joinF-19 {
|
||||
SELECT *
|
||||
FROM t1 LEFT JOIN t2 ON true
|
||||
INNER JOIN t3 ON t2.y IS NOT NULL
|
||||
INNER JOIN t4 ON true
|
||||
ORDER BY coalesce(t1.x,t2.y,t3.z,t4.w,0);
|
||||
} {
|
||||
}
|
||||
do_execsql_test joinF-20 {
|
||||
SELECT *
|
||||
FROM t1 LEFT JOIN t2 ON true
|
||||
INNER JOIN t3 ON t2.y IS NOT NULL
|
||||
INNER JOIN t4 ON true
|
||||
WHERE (t3.z!=400 AND t3.z!=500 AND t3.z!=600)
|
||||
ORDER BY coalesce(t1.x,t2.y,t3.z,t4.w,0);
|
||||
} {
|
||||
}
|
||||
do_execsql_test joinF-21 {
|
||||
SELECT *
|
||||
FROM t1 LEFT JOIN t2 ON true
|
||||
INNER JOIN t3 ON t2.y IS NOT NULL
|
||||
LEFT JOIN t4 ON true
|
||||
ORDER BY coalesce(t1.x,t2.y,t3.z,t4.w,0);
|
||||
} {
|
||||
}
|
||||
do_execsql_test joinF-22 {
|
||||
SELECT *
|
||||
FROM t1 LEFT JOIN t2 ON true
|
||||
INNER JOIN t3 ON t2.y IS NOT NULL
|
||||
LEFT JOIN t4 ON true
|
||||
WHERE (t3.z!=400 AND t3.z!=500 AND t3.z!=600)
|
||||
ORDER BY coalesce(t1.x,t2.y,t3.z,t4.w,0);
|
||||
} {
|
||||
}
|
||||
do_execsql_test joinF-23 {
|
||||
SELECT *
|
||||
FROM t1 LEFT JOIN t2 ON true
|
||||
INNER JOIN t3 ON t2.y IS NOT NULL
|
||||
RIGHT JOIN t4 ON true
|
||||
ORDER BY coalesce(t1.x,t2.y,t3.z,t4.w,0);
|
||||
} {
|
||||
- - - 50
|
||||
}
|
||||
do_execsql_test joinF-24 {
|
||||
SELECT *
|
||||
FROM t1 LEFT JOIN t2 ON true
|
||||
INNER JOIN t3 ON t2.y IS NOT NULL
|
||||
RIGHT JOIN t4 ON true
|
||||
WHERE (t3.z!=400 AND t3.z!=500 AND t3.z!=600)
|
||||
ORDER BY coalesce(t1.x,t2.y,t3.z,t4.w,0);
|
||||
} {
|
||||
}
|
||||
do_execsql_test joinF-25 {
|
||||
SELECT *
|
||||
FROM t1 LEFT JOIN t2 ON true
|
||||
LEFT JOIN t3 ON t2.y IS NOT NULL
|
||||
INNER JOIN t4 ON true
|
||||
ORDER BY coalesce(t1.x,t2.y,t3.z,t4.w,0);
|
||||
} {
|
||||
10 - - 50
|
||||
}
|
||||
do_execsql_test joinF-26 {
|
||||
SELECT *
|
||||
FROM t1 LEFT JOIN t2 ON true
|
||||
LEFT JOIN t3 ON t2.y IS NOT NULL
|
||||
INNER JOIN t4 ON true
|
||||
WHERE (t3.z!=400 AND t3.z!=500 AND t3.z!=600)
|
||||
ORDER BY coalesce(t1.x,t2.y,t3.z,t4.w,0);
|
||||
} {
|
||||
}
|
||||
do_execsql_test joinF-27 {
|
||||
SELECT *
|
||||
FROM t1 LEFT JOIN t2 ON true
|
||||
LEFT JOIN t3 ON t2.y IS NOT NULL
|
||||
LEFT JOIN t4 ON true
|
||||
ORDER BY coalesce(t1.x,t2.y,t3.z,t4.w,0);
|
||||
} {
|
||||
10 - - 50
|
||||
}
|
||||
do_execsql_test joinF-28 {
|
||||
SELECT *
|
||||
FROM t1 LEFT JOIN t2 ON true
|
||||
LEFT JOIN t3 ON t2.y IS NOT NULL
|
||||
LEFT JOIN t4 ON true
|
||||
WHERE (t3.z!=400 AND t3.z!=500 AND t3.z!=600)
|
||||
ORDER BY coalesce(t1.x,t2.y,t3.z,t4.w,0);
|
||||
} {
|
||||
}
|
||||
do_execsql_test joinF-29 {
|
||||
SELECT *
|
||||
FROM t1 LEFT JOIN t2 ON true
|
||||
LEFT JOIN t3 ON t2.y IS NOT NULL
|
||||
RIGHT JOIN t4 ON true
|
||||
ORDER BY coalesce(t1.x,t2.y,t3.z,t4.w,0);
|
||||
} {
|
||||
10 - - 50
|
||||
}
|
||||
do_execsql_test joinF-30 {
|
||||
SELECT *
|
||||
FROM t1 LEFT JOIN t2 ON true
|
||||
LEFT JOIN t3 ON t2.y IS NOT NULL
|
||||
RIGHT JOIN t4 ON true
|
||||
WHERE (t3.z!=400 AND t3.z!=500 AND t3.z!=600)
|
||||
ORDER BY coalesce(t1.x,t2.y,t3.z,t4.w,0);
|
||||
} {
|
||||
}
|
||||
do_execsql_test joinF-31 {
|
||||
SELECT *
|
||||
FROM t1 LEFT JOIN t2 ON true
|
||||
RIGHT JOIN t3 ON t2.y IS NOT NULL
|
||||
INNER JOIN t4 ON true
|
||||
ORDER BY coalesce(t1.x,t2.y,t3.z,t4.w,0);
|
||||
} {
|
||||
- - 20 50
|
||||
- - 30 50
|
||||
}
|
||||
do_execsql_test joinF-32 {
|
||||
SELECT *
|
||||
FROM t1 LEFT JOIN t2 ON true
|
||||
RIGHT JOIN t3 ON t2.y IS NOT NULL
|
||||
INNER JOIN t4 ON true
|
||||
WHERE (t3.z!=400 AND t3.z!=500 AND t3.z!=600)
|
||||
ORDER BY coalesce(t1.x,t2.y,t3.z,t4.w,0);
|
||||
} {
|
||||
- - 20 50
|
||||
- - 30 50
|
||||
}
|
||||
do_execsql_test joinF-33 {
|
||||
SELECT *
|
||||
FROM t1 LEFT JOIN t2 ON true
|
||||
RIGHT JOIN t3 ON t2.y IS NOT NULL
|
||||
LEFT JOIN t4 ON true
|
||||
ORDER BY coalesce(t1.x,t2.y,t3.z,t4.w,0);
|
||||
} {
|
||||
- - 20 50
|
||||
- - 30 50
|
||||
}
|
||||
do_execsql_test joinF-34 {
|
||||
SELECT *
|
||||
FROM t1 LEFT JOIN t2 ON true
|
||||
RIGHT JOIN t3 ON t2.y IS NOT NULL
|
||||
LEFT JOIN t4 ON true
|
||||
WHERE (t3.z!=400 AND t3.z!=500 AND t3.z!=600)
|
||||
ORDER BY coalesce(t1.x,t2.y,t3.z,t4.w,0);
|
||||
} {
|
||||
- - 20 50
|
||||
- - 30 50
|
||||
}
|
||||
do_execsql_test joinF-35 {
|
||||
SELECT *
|
||||
FROM t1 LEFT JOIN t2 ON true
|
||||
RIGHT JOIN t3 ON t2.y IS NOT NULL
|
||||
RIGHT JOIN t4 ON true
|
||||
ORDER BY coalesce(t1.x,t2.y,t3.z,t4.w,0);
|
||||
} {
|
||||
- - 20 50
|
||||
- - 30 50
|
||||
}
|
||||
do_execsql_test joinF-36 {
|
||||
SELECT *
|
||||
FROM t1 LEFT JOIN t2 ON true
|
||||
RIGHT JOIN t3 ON t2.y IS NOT NULL
|
||||
RIGHT JOIN t4 ON true
|
||||
WHERE (t3.z!=400 AND t3.z!=500 AND t3.z!=600)
|
||||
ORDER BY coalesce(t1.x,t2.y,t3.z,t4.w,0);
|
||||
} {
|
||||
- - 20 50
|
||||
- - 30 50
|
||||
}
|
||||
do_execsql_test joinF-37 {
|
||||
SELECT *
|
||||
FROM t1 RIGHT JOIN t2 ON true
|
||||
INNER JOIN t3 ON t2.y IS NOT NULL
|
||||
INNER JOIN t4 ON true
|
||||
ORDER BY coalesce(t1.x,t2.y,t3.z,t4.w,0);
|
||||
} {
|
||||
}
|
||||
do_execsql_test joinF-38 {
|
||||
SELECT *
|
||||
FROM t1 RIGHT JOIN t2 ON true
|
||||
INNER JOIN t3 ON t2.y IS NOT NULL
|
||||
INNER JOIN t4 ON true
|
||||
WHERE (t3.z!=400 AND t3.z!=500 AND t3.z!=600)
|
||||
ORDER BY coalesce(t1.x,t2.y,t3.z,t4.w,0);
|
||||
} {
|
||||
}
|
||||
do_execsql_test joinF-39 {
|
||||
SELECT *
|
||||
FROM t1 RIGHT JOIN t2 ON true
|
||||
INNER JOIN t3 ON t2.y IS NOT NULL
|
||||
LEFT JOIN t4 ON true
|
||||
ORDER BY coalesce(t1.x,t2.y,t3.z,t4.w,0);
|
||||
} {
|
||||
}
|
||||
do_execsql_test joinF-40 {
|
||||
SELECT *
|
||||
FROM t1 RIGHT JOIN t2 ON true
|
||||
INNER JOIN t3 ON t2.y IS NOT NULL
|
||||
LEFT JOIN t4 ON true
|
||||
WHERE (t3.z!=400 AND t3.z!=500 AND t3.z!=600)
|
||||
ORDER BY coalesce(t1.x,t2.y,t3.z,t4.w,0);
|
||||
} {
|
||||
}
|
||||
do_execsql_test joinF-41 {
|
||||
SELECT *
|
||||
FROM t1 RIGHT JOIN t2 ON true
|
||||
INNER JOIN t3 ON t2.y IS NOT NULL
|
||||
RIGHT JOIN t4 ON true
|
||||
ORDER BY coalesce(t1.x,t2.y,t3.z,t4.w,0);
|
||||
} {
|
||||
- - - 50
|
||||
}
|
||||
do_execsql_test joinF-42 {
|
||||
SELECT *
|
||||
FROM t1 RIGHT JOIN t2 ON true
|
||||
INNER JOIN t3 ON t2.y IS NOT NULL
|
||||
RIGHT JOIN t4 ON true
|
||||
WHERE (t3.z!=400 AND t3.z!=500 AND t3.z!=600)
|
||||
ORDER BY coalesce(t1.x,t2.y,t3.z,t4.w,0);
|
||||
} {
|
||||
}
|
||||
do_execsql_test joinF-43 {
|
||||
SELECT *
|
||||
FROM t1 RIGHT JOIN t2 ON true
|
||||
LEFT JOIN t3 ON t2.y IS NOT NULL
|
||||
INNER JOIN t4 ON true
|
||||
ORDER BY coalesce(t1.x,t2.y,t3.z,t4.w,0);
|
||||
} {
|
||||
}
|
||||
do_execsql_test joinF-44 {
|
||||
SELECT *
|
||||
FROM t1 RIGHT JOIN t2 ON true
|
||||
LEFT JOIN t3 ON t2.y IS NOT NULL
|
||||
INNER JOIN t4 ON true
|
||||
WHERE (t3.z!=400 AND t3.z!=500 AND t3.z!=600)
|
||||
ORDER BY coalesce(t1.x,t2.y,t3.z,t4.w,0);
|
||||
} {
|
||||
}
|
||||
do_execsql_test joinF-45 {
|
||||
SELECT *
|
||||
FROM t1 RIGHT JOIN t2 ON true
|
||||
LEFT JOIN t3 ON t2.y IS NOT NULL
|
||||
LEFT JOIN t4 ON true
|
||||
ORDER BY coalesce(t1.x,t2.y,t3.z,t4.w,0);
|
||||
} {
|
||||
}
|
||||
do_execsql_test joinF-46 {
|
||||
SELECT *
|
||||
FROM t1 RIGHT JOIN t2 ON true
|
||||
LEFT JOIN t3 ON t2.y IS NOT NULL
|
||||
LEFT JOIN t4 ON true
|
||||
WHERE (t3.z!=400 AND t3.z!=500 AND t3.z!=600)
|
||||
ORDER BY coalesce(t1.x,t2.y,t3.z,t4.w,0);
|
||||
} {
|
||||
}
|
||||
do_execsql_test joinF-47 {
|
||||
SELECT *
|
||||
FROM t1 RIGHT JOIN t2 ON true
|
||||
LEFT JOIN t3 ON t2.y IS NOT NULL
|
||||
RIGHT JOIN t4 ON true
|
||||
ORDER BY coalesce(t1.x,t2.y,t3.z,t4.w,0);
|
||||
} {
|
||||
- - - 50
|
||||
}
|
||||
do_execsql_test joinF-48 {
|
||||
SELECT *
|
||||
FROM t1 RIGHT JOIN t2 ON true
|
||||
LEFT JOIN t3 ON t2.y IS NOT NULL
|
||||
RIGHT JOIN t4 ON true
|
||||
WHERE (t3.z!=400 AND t3.z!=500 AND t3.z!=600)
|
||||
ORDER BY coalesce(t1.x,t2.y,t3.z,t4.w,0);
|
||||
} {
|
||||
}
|
||||
do_execsql_test joinF-49 {
|
||||
SELECT *
|
||||
FROM t1 RIGHT JOIN t2 ON true
|
||||
RIGHT JOIN t3 ON t2.y IS NOT NULL
|
||||
INNER JOIN t4 ON true
|
||||
ORDER BY coalesce(t1.x,t2.y,t3.z,t4.w,0);
|
||||
} {
|
||||
- - 20 50
|
||||
- - 30 50
|
||||
}
|
||||
do_execsql_test joinF-50 {
|
||||
SELECT *
|
||||
FROM t1 RIGHT JOIN t2 ON true
|
||||
RIGHT JOIN t3 ON t2.y IS NOT NULL
|
||||
INNER JOIN t4 ON true
|
||||
WHERE (t3.z!=400 AND t3.z!=500 AND t3.z!=600)
|
||||
ORDER BY coalesce(t1.x,t2.y,t3.z,t4.w,0);
|
||||
} {
|
||||
- - 20 50
|
||||
- - 30 50
|
||||
}
|
||||
do_execsql_test joinF-51 {
|
||||
SELECT *
|
||||
FROM t1 RIGHT JOIN t2 ON true
|
||||
RIGHT JOIN t3 ON t2.y IS NOT NULL
|
||||
LEFT JOIN t4 ON true
|
||||
ORDER BY coalesce(t1.x,t2.y,t3.z,t4.w,0);
|
||||
} {
|
||||
- - 20 50
|
||||
- - 30 50
|
||||
}
|
||||
do_execsql_test joinF-52 {
|
||||
SELECT *
|
||||
FROM t1 RIGHT JOIN t2 ON true
|
||||
RIGHT JOIN t3 ON t2.y IS NOT NULL
|
||||
LEFT JOIN t4 ON true
|
||||
WHERE (t3.z!=400 AND t3.z!=500 AND t3.z!=600)
|
||||
ORDER BY coalesce(t1.x,t2.y,t3.z,t4.w,0);
|
||||
} {
|
||||
- - 20 50
|
||||
- - 30 50
|
||||
}
|
||||
do_execsql_test joinF-53 {
|
||||
SELECT *
|
||||
FROM t1 RIGHT JOIN t2 ON true
|
||||
RIGHT JOIN t3 ON t2.y IS NOT NULL
|
||||
RIGHT JOIN t4 ON true
|
||||
ORDER BY coalesce(t1.x,t2.y,t3.z,t4.w,0);
|
||||
} {
|
||||
- - 20 50
|
||||
- - 30 50
|
||||
}
|
||||
do_execsql_test joinF-54 {
|
||||
SELECT *
|
||||
FROM t1 RIGHT JOIN t2 ON true
|
||||
RIGHT JOIN t3 ON t2.y IS NOT NULL
|
||||
RIGHT JOIN t4 ON true
|
||||
WHERE (t3.z!=400 AND t3.z!=500 AND t3.z!=600)
|
||||
ORDER BY coalesce(t1.x,t2.y,t3.z,t4.w,0);
|
||||
} {
|
||||
- - 20 50
|
||||
- - 30 50
|
||||
}
|
||||
finish_test
|
||||
|
||||
############################################################################
|
||||
# This is the TCL script used to generate the psql script that generated
|
||||
# the data above.
|
||||
#
|
||||
# puts "
|
||||
# \\pset border off
|
||||
# \\pset tuples_only on
|
||||
# \\pset null -
|
||||
#
|
||||
# DROP TABLE IF EXISTS t1;
|
||||
# DROP TABLE IF EXISTS t2;
|
||||
# DROP TABLE IF EXISTS t3;
|
||||
# DROP TABLE IF EXISTS t4;
|
||||
# CREATE TABLE t1(x INT);
|
||||
# CREATE TABLE t2(y INT);
|
||||
# CREATE TABLE t3(z INT);
|
||||
# CREATE TABLE t4(w INT);
|
||||
# INSERT INTO t1 VALUES(10);
|
||||
# INSERT INTO t3 VALUES(20),(30);
|
||||
# INSERT INTO t4 VALUES(50);
|
||||
# "
|
||||
#
|
||||
# proc echo {prefix txt} {
|
||||
# regsub -all {\n} $txt \n$prefix txt
|
||||
# puts "$prefix$txt"
|
||||
# }
|
||||
#
|
||||
# set n 0
|
||||
# foreach j1 {INNER LEFT RIGHT} {
|
||||
# foreach j2 {INNER LEFT RIGHT} {
|
||||
# foreach j3 {INNER LEFT RIGHT} {
|
||||
#
|
||||
# incr n
|
||||
# set q1 ""
|
||||
# append q1 "SELECT *\n"
|
||||
# append q1 " FROM t1 $j1 JOIN t2 ON true\n"
|
||||
# append q1 " $j2 JOIN t3 ON t2.y IS NOT NULL\n"
|
||||
# append q1 " $j3 JOIN t4 ON true\n"
|
||||
# append q1 " ORDER BY coalesce(t1.x,t2.y,t3.z,t4.w,0);"
|
||||
#
|
||||
# echo "\\qecho " "do_execsql_test joinF-$n \{"
|
||||
# echo "\\qecho X " $q1
|
||||
# echo "\\qecho " "\} \{"
|
||||
# puts $q1
|
||||
# echo "\\qecho " "\}"
|
||||
#
|
||||
# incr n
|
||||
# set q1 ""
|
||||
# append q1 "SELECT *\n"
|
||||
# append q1 " FROM t1 $j1 JOIN t2 ON true\n"
|
||||
# append q1 " $j2 JOIN t3 ON t2.y IS NOT NULL\n"
|
||||
# append q1 " $j3 JOIN t4 ON true\n"
|
||||
# append q1 " WHERE (t3.z!=400 AND t3.z!=500 AND t3.z!=600)\n"
|
||||
# append q1 " ORDER BY coalesce(t1.x,t2.y,t3.z,t4.w,0);"
|
||||
#
|
||||
# echo "\\qecho " "do_execsql_test joinF-$n \{"
|
||||
# echo "\\qecho X " $q1
|
||||
# echo "\\qecho " "\} \{"
|
||||
# puts $q1
|
||||
# echo "\\qecho " "\}"
|
||||
#
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
#
|
@ -260,11 +260,23 @@ do_catchsql_test 11.8 {
|
||||
#
|
||||
do_execsql_test 12.1 {
|
||||
DROP TABLE IF EXISTS t1;
|
||||
CREATE TABLE t1(a,b); INSERT INTO t1 VALUES(1,2);
|
||||
CREATE TABLE t1(a INT,b INT); INSERT INTO t1 VALUES(1,2);
|
||||
DROP TABLE IF EXISTS t2;
|
||||
CREATE TABLE t2(x,y); INSERT INTO t2 VALUES(3,4);
|
||||
CREATE TABLE t2(x INT,y INT); INSERT INTO t2 VALUES(3,4);
|
||||
SELECT *,'x' FROM t1 LEFT JOIN t2 ON (a,b)=(x,y);
|
||||
} {1 2 {} {} x}
|
||||
db null -
|
||||
do_execsql_test 12.2 {
|
||||
SELECT t1.*, t2.* FROM t2 RIGHT JOIN t1 ON (a,b)=(x,y);
|
||||
} {1 2 - -}
|
||||
do_execsql_test 12.3 {
|
||||
SELECT t1.*, t2.* FROM t1 FULL JOIN t2 ON (a,b)=(x,y)
|
||||
ORDER BY coalesce(a,x);
|
||||
} {
|
||||
1 2 - -
|
||||
- - 3 4
|
||||
}
|
||||
db null {}
|
||||
|
||||
|
||||
foreach {tn sql} {
|
||||
@ -540,7 +552,7 @@ do_execsql_test 19.36 {
|
||||
SELECT * FROM t1 WHERE (3,32)>=(a,b) ORDER BY a DESC;
|
||||
} {2 22 1 11}
|
||||
|
||||
# 2018-02-18: Memory leak nexted row-value. Detected by OSSFuzz.
|
||||
# 2018-02-18: Memory leak nested row-value. Detected by OSSFuzz.
|
||||
#
|
||||
do_catchsql_test 20.1 {
|
||||
SELECT 1 WHERE (2,(2,0)) IS (2,(2,0));
|
||||
@ -632,9 +644,15 @@ do_execsql_test 26.10 {
|
||||
do_execsql_test 26.20 {
|
||||
SELECT 2 FROM t1 LEFT JOIN t0 ON (c0, x'') != (NULL, 0);
|
||||
} {2}
|
||||
do_execsql_test 26.21 {
|
||||
SELECT 21 FROM t0 RIGHT JOIN t1 ON (c0, x'') != (NULL, 0);
|
||||
} {21}
|
||||
do_execsql_test 26.30 {
|
||||
SELECT 3 FROM t1 LEFT JOIN t0 WHERE (c0, x'') != (NULL, 0);
|
||||
} {3}
|
||||
do_execsql_test 26.31 {
|
||||
SELECT 31 FROM t0 RIGHT JOIN t1 WHERE (c0, x'') != (NULL, 0);
|
||||
} {31}
|
||||
|
||||
# 2019-12-30 ticket 892575cdba4e1e36
|
||||
#
|
||||
@ -700,6 +718,9 @@ do_execsql_test 31.1 {
|
||||
CREATE TABLE b(b1 UNIQUE,b2);
|
||||
SELECT * FROM a LEFT JOIN b ON b2=NULL AND b2=5 WHERE (b1,substr(b.b1,1,1))==(SELECT 1024,'b');
|
||||
} {}
|
||||
do_execsql_test 31.1b {
|
||||
SELECT * FROM b RIGHT JOIN a ON b2=NULL AND b2=5 WHERE (b1,substr(b.b1,1,1))==(SELECT 1024,'b');
|
||||
} {}
|
||||
do_execsql_test 31.2 {
|
||||
CREATE TABLE t1(a);
|
||||
INSERT INTO t1 VALUES(0);
|
||||
@ -707,6 +728,9 @@ do_execsql_test 31.2 {
|
||||
INSERT INTO t2 VALUES(NULL,123,456);
|
||||
SELECT * FROM t1 LEFT JOIN t2 ON b=NULL WHERE (c,d)==(SELECT 123, 456+a);
|
||||
} {}
|
||||
do_execsql_test 31.2b {
|
||||
SELECT * FROM t2 RIGHT JOIN t1 ON b=NULL WHERE (c,d)==(SELECT 123, 456+a);
|
||||
} {}
|
||||
|
||||
# 2022-02-03 dbsqlfuzz 80a9fade844b4fb43564efc972bcb2c68270f5d1
|
||||
reset_db
|
||||
|
@ -213,7 +213,8 @@ do_execsql_test 5.0 {
|
||||
CREATE TABLE T2(a TEXT PRIMARY KEY,n INT);
|
||||
INSERT INTO T2(a, n) VALUES('aaa',0);
|
||||
SELECT * FROM T2
|
||||
WHERE (a,n) IN (SELECT T1.a, V.n FROM T1, (SELECT * FROM (SELECT 0 n)) V);
|
||||
WHERE (a,n) IN (SELECT T1.a, V.n
|
||||
FROM T1, (SELECT * FROM (SELECT 0 n) T3) V);
|
||||
} {aaa 0}
|
||||
|
||||
|
||||
|
@ -28,4 +28,32 @@ do_execsql_test subtype1-130 {
|
||||
SELECT test_setsubtype('hello',123);
|
||||
} {hello}
|
||||
|
||||
# 2022-06-09
|
||||
# https://sqlite.org/forum/forumpost/3d9caa45cbe38c78
|
||||
#
|
||||
# Avoid carrying subtypes through into a subquery that has been flattened
|
||||
# or to which the outer WHERE clause has been pushed down.
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test subtype1-200 {
|
||||
CREATE TABLE t1(a); INSERT INTO t1 VALUES ('x');
|
||||
CREATE VIEW t2(b) AS SELECT json(TRUE);
|
||||
CREATE TABLE t3(b); INSERT INTO t3 VALUES(json(TRUE));
|
||||
}
|
||||
do_execsql_test subtype1-210 {
|
||||
SELECT * FROM t3, t1 WHERE NOT json_quote(b);
|
||||
} {1 x}
|
||||
do_execsql_test subtype1-220 {
|
||||
SELECT * FROM t2, t1 WHERE NOT json_quote(b);
|
||||
} {1 x}
|
||||
do_execsql_test subtype1-230 {
|
||||
WITH t4(a) AS MATERIALIZED (SELECT json(1)) SELECT subtype(a) FROM t4;
|
||||
} {0}
|
||||
do_execsql_test subtype1-231 {
|
||||
WITH t4(a) AS NOT MATERIALIZED (SELECT json(1)) SELECT subtype(a) FROM t4;
|
||||
} {0}
|
||||
|
||||
|
||||
|
||||
|
||||
finish_test
|
||||
|
94
test/windowD.test
Normal file
94
test/windowD.test
Normal file
@ -0,0 +1,94 @@
|
||||
# 2022 June 2
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
set testprefix windowD
|
||||
|
||||
|
||||
do_execsql_test 1.0 {
|
||||
CREATE TABLE t0(c0 TEXT);
|
||||
CREATE VIEW v0(c0, c1)
|
||||
AS SELECT CUME_DIST() OVER (PARTITION BY t0.c0), TRUE FROM t0;
|
||||
INSERT INTO t0 VALUES ('x');
|
||||
}
|
||||
|
||||
do_execsql_test 1.1 {
|
||||
SELECT ('500') IS (v0.c1) FROM v0;
|
||||
} {
|
||||
0
|
||||
}
|
||||
|
||||
do_execsql_test 1.2 {
|
||||
SELECT (('500') IS (v0.c1)) FROM v0, t0;
|
||||
} {
|
||||
0
|
||||
}
|
||||
|
||||
do_execsql_test 1.2 {
|
||||
SELECT (('500') IS (v0.c1)) IS FALSE FROM v0;
|
||||
} {
|
||||
1
|
||||
}
|
||||
|
||||
do_execsql_test 1.3 {
|
||||
SELECT * FROM v0;
|
||||
} {
|
||||
1.0 1
|
||||
}
|
||||
|
||||
do_execsql_test 1.4 {
|
||||
SELECT * FROM v0 WHERE ('500' IS v0.c1) IS FALSE;
|
||||
} {
|
||||
1.0 1
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
|
||||
reset_db
|
||||
do_execsql_test 2.0 {
|
||||
CREATE TABLE t1(x);
|
||||
INSERT INTO t1 VALUES('value');
|
||||
CREATE VIEW v1(a, b, c, d) AS SELECT 1, 2, TRUE, FALSE FROM t1;
|
||||
}
|
||||
|
||||
do_execsql_test 2.1 {
|
||||
SELECT 500 IS a, 500 IS b, 500 IS c, 500 IS d FROM v1
|
||||
} {0 0 0 0}
|
||||
|
||||
do_execsql_test 2.2 {
|
||||
SELECT * FROM v1 WHERE 500 IS c;
|
||||
} {}
|
||||
|
||||
do_execsql_test 2.3 {
|
||||
SELECT * FROM v1 WHERE 500 IS d;
|
||||
} {}
|
||||
|
||||
do_execsql_test 2.4 {
|
||||
CREATE VIEW v2 AS SELECT max(x) OVER () AS a, TRUE AS c FROM t1;
|
||||
}
|
||||
|
||||
do_execsql_test 2.5 {
|
||||
SELECT 500 IS c FROM v2;
|
||||
} 0
|
||||
|
||||
do_execsql_test 2.6 {
|
||||
SELECT * FROM v2 WHERE 500 IS c;
|
||||
} {}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
finish_test
|
||||
|
Loading…
x
Reference in New Issue
Block a user