Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | Merge the latest trunk enhancements into the begin-concurrent branch. |
---|---|
Downloads: | Tarball | ZIP archive |
Timelines: | family | ancestors | descendants | both | begin-concurrent |
Files: | files | file ages | folders |
SHA3-256: |
d6f6ee5cbcd9b263bc2423ce90700ec2 |
User & Date: | drh 2022-05-28 14:25:10.801 |
Context
2022-06-16
| ||
13:37 | Merge the latest trunk enhancements into the begin-concurrent branch. (check-in: 221e07ed78 user: drh tags: begin-concurrent) | |
2022-05-28
| ||
14:32 | Merge the latest trunk enhancements into the begin-concurrent-report branch. (check-in: 034d2c511a user: drh tags: begin-concurrent-report) | |
14:25 | Merge the latest trunk enhancements into the begin-concurrent branch. (check-in: d6f6ee5cbc user: drh tags: begin-concurrent) | |
14:03 | Apply the UPDATE-FROM file from check-in [98b3816bbaf539ea] to update-delete-limit builds. (check-in: 7e87892c24 user: drh tags: trunk) | |
2022-05-10
| ||
12:00 | Merge recent trunk enhancements into the begin-concurrent branch. (check-in: f65bd76760 user: drh tags: begin-concurrent) | |
Changes
Changes to Makefile.in.
︙ | ︙ | |||
1510 1511 1512 1513 1514 1515 1516 | echo 'EXPORTS' >sqlite3.def nm $(REAL_LIBOBJ) | grep ' T ' | grep ' _sqlite3_' \ | sed 's/^.* _//' >>sqlite3.def sqlite3.dll: $(REAL_LIBOBJ) sqlite3.def $(TCC) -shared -o $@ sqlite3.def \ -Wl,"--strip-all" $(REAL_LIBOBJ) | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 | echo 'EXPORTS' >sqlite3.def nm $(REAL_LIBOBJ) | grep ' T ' | grep ' _sqlite3_' \ | sed 's/^.* _//' >>sqlite3.def sqlite3.dll: $(REAL_LIBOBJ) sqlite3.def $(TCC) -shared -o $@ sqlite3.def \ -Wl,"--strip-all" $(REAL_LIBOBJ) # # fiddle/wasm section # fiddle_dir = ext/fiddle 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) $(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 $(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) sqlite3-wasm: $(sqlite3_wasm_js) wasm: fiddle sqlite3-wasm ######################################################################## # Explanation of the emcc build flags: # # -sENVIRONMENT=web: elides bootstrap code related to non-web JS # environments like node.js. Removing this makes the output a tiny # tick larger but hypothetically makes it more portable to # non-browser JS environments. # # -sMODULARIZE: changes how the generated code is structured to avoid # declaring a global Module object and instead installing a function # which loads and initialized the module. The function is named... # # -sEXPORT_NAME=jsFunctionName (see -sMODULARIZE) # # -sEXPORTED_RUNTIME_METHODS=@/absolute/path/to/file: a file # containing a list of emscripten-supplied APIs, one per line, which # must be exported into the generated JS. Must be an absolute path! # # -sEXPORTED_FUNCTIONS=@/absolute/path/to/file: a file containing a # list of C functions, one per line, which must be exported via wasm # so they're visible to JS. C symbols names in that file must all # start with an underscore for reasons known only to the emcc # developers. e.g., _sqlite3_open_v2 and _sqlite3_finalize. Must be # an absolute path! # # --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 # (renamed) when building in wasm mode. # # --pre-js/--post-js=FILE relative or absolute paths to JS files to # prepend/append to the emcc-generated bootstrapping JS. It's # easier/faster to develop with separate JS files (reduces rebuilding # requirements) but certain configurations, namely -sMODULARIZE, may # require using at least a --pre-js file. They can be used # individually and need not be paired. # # -O0..-O3 and -Oz: optimization levels affect not only C-style # optimization but whether or not the resulting generated JS code # gets minified. -O0 compiles _much_ more quickly than -O3 or -Oz, # and doesn't minimize any JS code, so is recommended for # development. -O3 or -Oz are recommended for deployment, but # primarily because -Oz will shrink the wasm file notably. JS-side # minification makes little difference in terms of overall # distributable size. ######################################################################## |
Added ext/fiddle/EXPORTED_FUNCTIONS.fiddle.
> > > > > > > | 1 2 3 4 5 6 7 | _fiddle_exec _fiddle_interrupt _fiddle_experiment _fiddle_the_db _fiddle_db_arg _fiddle_db_filename _fiddle_reset_db |
Added ext/fiddle/EXPORTED_FUNCTIONS.sqlite3-api.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | _sqlite3_bind_blob _sqlite3_bind_double _sqlite3_bind_int _sqlite3_bind_int64 _sqlite3_bind_null _sqlite3_bind_parameter_count _sqlite3_bind_parameter_index _sqlite3_bind_text _sqlite3_changes _sqlite3_clear_bindings _sqlite3_close_v2 _sqlite3_column_blob _sqlite3_column_bytes _sqlite3_column_count _sqlite3_column_count _sqlite3_column_double _sqlite3_column_int _sqlite3_column_int64 _sqlite3_column_name _sqlite3_column_text _sqlite3_column_type _sqlite3_compileoption_get _sqlite3_compileoption_used _sqlite3_create_function_v2 _sqlite3_data_count _sqlite3_db_filename _sqlite3_errmsg _sqlite3_exec _sqlite3_finalize _sqlite3_interrupt _sqlite3_libversion _sqlite3_open _sqlite3_open_v2 _sqlite3_prepare_v2 _sqlite3_prepare_v2 _sqlite3_reset _sqlite3_result_blob _sqlite3_result_double _sqlite3_result_error _sqlite3_result_int _sqlite3_result_null _sqlite3_result_text _sqlite3_sourceid _sqlite3_sql _sqlite3_step _sqlite3_value_blob _sqlite3_value_bytes _sqlite3_value_double _sqlite3_value_text _sqlite3_value_type _free |
Added ext/fiddle/EXPORTED_RUNTIME_METHODS.
> > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 | ALLOC_NORMAL FS UTF8ToString addFunction allocate allocateUTF8OnStack ccall cwrap getValue removeFunction setValue stackAlloc stackRestore stackSave |
Added ext/fiddle/Makefile.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | # This GNU makefile exists primarily to simplify/speed up development # from emacs. It is not part of the canonical build process. default: $(MAKE) -C ../.. wasm -e emcc_opt=-O0 clean: $(MAKE) -C ../../ clean-wasm fiddle_files = emscripten.css fiddle.html \ fiddle.js fiddle-module.js \ fiddle-module.wasm fiddle-worker.js # fiddle_remote is the remote destination for the fiddle app. It # must be a [user@]HOST:/path for rsync. # Note that the target "should probably" contain a symlink of # index.html -> fiddle.html. fiddle_remote ?= ifeq (,$(fiddle_remote)) ifneq (,$(wildcard /home/stephan)) fiddle_remote = wh2:www/wh/sqlite3/. else ifneq (,$(wildcard /home/drh)) #fiddle_remote = if appropriate, add that user@host:/path here endif endif $(fiddle_files): default push-fiddle: $(fiddle_files) @if [ x = "x$(fiddle_remote)" ]; then \ echo "fiddle_remote must be a [user@]HOST:/path for rsync"; \ exit 1; \ fi rsync -va $(fiddle_files) $(fiddle_remote) |
Added ext/fiddle/SqliteTestUtil.js.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 | /* 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. *********************************************************************** This file contains bootstrapping code used by various test scripts which live in this file's directory. */ (function(){ /* querySelectorAll() proxy */ const EAll = function(/*[element=document,] cssSelector*/){ return (arguments.length>1 ? arguments[0] : document) .querySelectorAll(arguments[arguments.length-1]); }; /* querySelector() proxy */ const E = function(/*[element=document,] cssSelector*/){ return (arguments.length>1 ? arguments[0] : document) .querySelector(arguments[arguments.length-1]); }; /** Helpers for writing sqlite3-specific tests. */ self/*window or worker*/.SqliteTestUtil = { /** Running total of the number of tests run via this API. */ counter: 0, /** If expr is a function, it is called and its result is returned, coerced to a bool, else expr, coerced to a bool, is returned. */ toBool: function(expr){ return (expr instanceof Function) ? !!expr() : !!expr; }, /** abort() if expr is false. If expr is a function, it is called and its result is evaluated. */ assert: function f(expr, msg){ if(!f._){ f._ = ('undefined'===typeof abort ? (msg)=>{throw new Error(msg)} : abort); } ++this.counter; if(!this.toBool(expr)){ f._(msg || "Assertion failed."); } return this; }, /** Identical to assert() but throws instead of calling abort(). */ affirm: function(expr, msg){ ++this.counter; if(!this.toBool(expr)) throw new Error(msg || "Affirmation failed."); return this; }, /** Calls f() and squelches any exception it throws. If it does not throw, this function throws. */ mustThrow: function(f, msg){ ++this.counter; let err; try{ f(); } catch(e){err=e;} if(!err) throw new Error(msg || "Expected exception."); return this; }, /** Throws if expr is truthy or expr is a function and expr() returns truthy. */ throwIf: function(expr, msg){ ++this.counter; if(this.toBool(expr)) throw new Error(msg || "throwIf() failed"); return this; }, /** Throws if expr is falsy or expr is a function and expr() returns falsy. */ throwUnless: function(expr, msg){ ++this.counter; if(!this.toBool(expr)) throw new Error(msg || "throwUnless() failed"); return this; } }; /** This is a module object for use with the emscripten-installed initSqlite3Module() factory function. */ self.sqlite3TestModule = { postRun: [ /* function(theModule){...} */ ], //onRuntimeInitialized: function(){}, /* Proxy for C-side stdout output. */ print: function(){ console.log.apply(console, Array.prototype.slice.call(arguments)); }, /* Proxy for C-side stderr output. */ printErr: function(){ console.error.apply(console, Array.prototype.slice.call(arguments)); }, /** Called by the module init bits to report loading progress. It gets passed an empty argument when loading is done (after onRuntimeInitialized() and any this.postRun callbacks have been run). */ setStatus: function f(text){ if(!f.last){ f.last = { text: '', step: 0 }; f.ui = { status: E('#module-status'), progress: E('#module-progress'), spinner: E('#module-spinner') }; } if(text === f.last.text) return; f.last.text = text; if(f.ui.progress){ f.ui.progress.value = f.last.step; f.ui.progress.max = f.last.step + 1; } ++f.last.step; if(text) { f.ui.status.classList.remove('hidden'); f.ui.status.innerText = text; }else{ if(f.ui.progress){ f.ui.progress.remove(); f.ui.spinner.remove(); delete f.ui.progress; delete f.ui.spinner; } f.ui.status.classList.add('hidden'); } } }; })(self/*window or worker*/); |
Added ext/fiddle/emscripten.css.
> > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | /* emcscript-related styling, used during the module load/intialization processes... */ .emscripten { padding-right: 0; margin-left: auto; margin-right: auto; display: block; } div.emscripten { text-align: center; } div.emscripten_border { border: 1px solid black; } #module-spinner { overflow: visible; } #module-spinner > * { margin-top: 1em; } .spinner { height: 50px; width: 50px; margin: 0px auto; animation: rotation 0.8s linear infinite; border-left: 10px solid rgb(0,150,240); border-right: 10px solid rgb(0,150,240); border-bottom: 10px solid rgb(0,150,240); border-top: 10px solid rgb(100,0,200); border-radius: 100%; background-color: rgb(200,100,250); } @keyframes rotation { from {transform: rotate(0deg);} to {transform: rotate(360deg);} } |
Added ext/fiddle/fiddle-worker.js.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 | /* 2022-05-20 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 the JS Worker file for the sqlite3 fiddle app. It loads the sqlite3 wasm module and offers access to the db via the Worker message-passing interface. Forewarning: this API is still very much Under Construction and subject to any number of changes as experience reveals what those need to be. Because we can have only a single message handler, as opposed to an arbitrary number of discrete event listeners like with DOM elements, we have to define a lower-level message API. Messages abstractly look like: { type: string, data: type-specific value } Where 'type' is used for dispatching and 'data' is a 'type'-dependent value. The 'type' values expected by each side of the main/worker connection vary. The types are described below but subject to change at any time as this experiment evolves. Workers-to-Main types - stdout, stderr: indicate stdout/stderr output from the wasm layer. The data property is the string of the output, noting that the emscripten binding emits these one line at a time. Thus, if a C-side puts() emits multiple lines in a single call, the JS side will see that as multiple calls. Example: {type:'stdout', data: 'Hi, world.'} - module: Status text. This is intended to alert the main thread about module loading status so that, e.g., the main thread can update a progress widget and DTRT when the module is finished loading and available for work. Status messages come in the form {type:'module', data:{ type:'status', data: {text:string|null, step:1-based-integer} } with an incrementing step value for each subsequent message. When the module loading is complete, a message with a text value of null is posted. - working: data='start'|'end'. Indicates that work is about to be sent to the module or has just completed. This can be used, e.g., to disable UI elements which should not be activated while work is pending. Example: {type:'working', data:'start'} Main-to-Worker types: - shellExec: data=text to execute as if it had been entered in the sqlite3 CLI shell app (as opposed to sqlite3_exec()). This event causes the worker to emit a 'working' event (data='start') before it starts and a 'working' event (data='end') when it finished. If called while work is currently being executed it emits stderr message instead of doing actual work, as the underlying db cannot handle concurrent tasks. Example: {type:'shellExec', data: 'select * from sqlite_master'} - More TBD as the higher-level db layer develops. */ /* Apparent browser(s) bug: console messages emitted may be duplicated in the console, even though they're provably only run once. See: https://stackoverflow.com/questions/49659464 Noting that it happens in Firefox as well as Chrome. Harmless but annoying. */ "use strict"; (function(){ /** Posts a message in the form {type,data} unless passed more than 2 args, in which case it posts {type, data:[arg1...argN]}. */ const wMsg = function(type,data){ postMessage({ type, data: arguments.length<3 ? data : Array.prototype.slice.call(arguments,1) }); }; const stdout = function(){wMsg('stdout', Array.prototype.slice.call(arguments));}; const stderr = function(){wMsg('stderr', Array.prototype.slice.call(arguments));}; self.onerror = function(/*message, source, lineno, colno, error*/) { const err = arguments[4]; if(err && 'ExitStatus'==err.name){ /* This is relevant for the sqlite3 shell binding but not the lower-level binding. */ fiddleModule.isDead = true; stderr("FATAL ERROR:", err.message); stderr("Restarting the app requires reloading the page."); wMsg('error', err); } console.error(err); fiddleModule.setStatus('Exception thrown, see JavaScript console: '+err); }; const Sqlite3Shell = { /** Returns the name of the currently-opened db. */ dbFilename: function f(){ if(!f._) f._ = fiddleModule.cwrap('fiddle_db_filename', "string", ['string']); return f._(); }, /** Runs the given text through the shell as if it had been typed in by a user. Fires a working/start event before it starts and working/end event when it finishes. */ exec: function f(sql){ if(!f._) f._ = fiddleModule.cwrap('fiddle_exec', null, ['string']); if(fiddleModule.isDead){ stderr("shell module has exit()ed. Cannot run SQL."); return; } wMsg('working','start'); try { if(f._running){ stderr('Cannot run multiple commands concurrently.'); }else{ f._running = true; f._(sql); } } finally { delete f._running; wMsg('working','end'); } }, resetDb: function f(){ if(!f._) f._ = fiddleModule.cwrap('fiddle_reset_db', null); stdout("Resetting database."); f._(); stdout("Reset",this.dbFilename()); }, /* Interrupt can't work: this Worker is tied up working, so won't get the interrupt event which would be needed to perform the interrupt. */ interrupt: function f(){ if(!f._) f._ = fiddleModule.cwrap('fiddle_interrupt', null); stdout("Requesting interrupt."); f._(); } }; self.onmessage = function f(ev){ ev = ev.data; if(!f.cache){ f.cache = { prevFilename: null }; } //console.debug("worker: onmessage.data",ev); switch(ev.type){ case 'shellExec': Sqlite3Shell.exec(ev.data); return; case 'db-reset': Sqlite3Shell.resetDb(); return; case 'interrupt': Sqlite3Shell.interrupt(); return; /** Triggers the export of the current db. Fires an event in the form: {type:'db-export', data:{ filename: name of db, buffer: contents of the db file (Uint8Array), error: on error, a message string and no buffer property. } } */ case 'db-export': { const fn = Sqlite3Shell.dbFilename(); stdout("Exporting",fn+"."); const fn2 = fn ? fn.split(/[/\\]/).pop() : null; try{ if(!fn2) throw new Error("DB appears to be closed."); wMsg('db-export',{ filename: fn2, buffer: fiddleModule.FS.readFile(fn, {encoding:"binary"}) }); }catch(e){ /* Post a failure message so that UI elements disabled during the export can be re-enabled. */ wMsg('db-export',{ filename: fn, error: e.message }); } return; } case 'open': { /* Expects: { buffer: ArrayBuffer | Uint8Array, filename: for logging/informational purposes only } */ const opt = ev.data; let buffer = opt.buffer; if(buffer instanceof Uint8Array){ }else if(buffer instanceof ArrayBuffer){ buffer = new Uint8Array(buffer); }else{ stderr("'open' expects {buffer:Uint8Array} containing an uploaded db."); return; } const fn = ( opt.filename ? opt.filename.split(/[/\\]/).pop().replace('"','_') : ("db-"+((Math.random() * 10000000) | 0)+ "-"+((Math.random() * 10000000) | 0)+".sqlite3") ); /* We cannot delete the existing db file until the new one is installed, which means that we risk overflowing our quota (if any) by having both the previous and current db briefly installed in the virtual filesystem. */ fiddleModule.FS.createDataFile("/", fn, buffer, true, true); const oldName = Sqlite3Shell.dbFilename(); Sqlite3Shell.exec('.open "/'+fn+'"'); if(oldName !== fn){ fiddleModule.FS.unlink(oldName); } stdout("Replaced DB with",fn+"."); return; } }; console.warn("Unknown fiddle-worker message type:",ev); }; /** emscripten module for use with build mode -sMODULARIZE. */ const fiddleModule = { print: stdout, printErr: stderr, /** Intercepts status updates from the emscripting module init and fires worker events with a type of 'status' and a payload of: { text: string | null, // null at end of load process step: integer // starts at 1, increments 1 per call } We have no way of knowing in advance how many steps will be processed/posted, so creating a "percentage done" view is not really practical. One can be approximated by giving it a current value of message.step and max value of message.step+1, though. When work is finished, a message with a text value of null is submitted. After a message with text==null is posted, the module may later post messages about fatal problems, e.g. an exit() being triggered, so it is recommended that UI elements for posting status messages not be outright removed from the DOM when text==null, and that they instead be hidden until/unless text!=null. */ setStatus: function f(text){ if(!f.last) f.last = { step: 0, text: '' }; else if(text === f.last.text) return; f.last.text = text; wMsg('module',{ type:'status', data:{step: ++f.last.step, text: text||null} }); } }; importScripts('fiddle-module.js'); /** initFiddleModule() is installed via fiddle-module.js due to building with: emcc ... -sMODULARIZE=1 -sEXPORT_NAME=initFiddleModule */ initFiddleModule(fiddleModule).then(function(thisModule){ wMsg('fiddle-ready'); }); })(); |
Added ext/fiddle/fiddle.html.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 | <!doctype html> <html lang="en-us"> <head> <meta charset="utf-8"> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <title>sqlite3 fiddle</title> <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. --> <!--script src="jqterm/jqterm-bundle.min.js"></script> <link rel="stylesheet" href="jqterm/jquery.terminal.min.css"/--> <link rel="stylesheet" href="emscripten.css"/> <style> /* The following styles are for app-level use. */ textarea { font-family: monospace; flex: 1 1 auto; } header { font-size: 130%; font-weight: bold; } #main-wrapper { display: flex; flex-direction: column-reverse; flex: 20 1 auto; } #main-wrapper.side-by-side { flex-direction: row-reverse; } #main-wrapper.swapio { flex-direction: column; } #main-wrapper.side-by-side.swapio { flex-direction: row; } .ta-wrapper{ display: flex; flex-direction: column; align-items: stretch; margin: 0 0.25em; flex: 1 1 auto; } .ta-wrapper.input { flex: 10 1 auto; } .ta-wrapper.output { flex: 20 1 auto; } .ta-wrapper textarea { font-size: 110%; filter: invert(100%); flex: 10 1 auto; } .button-bar { display: flex; justify-content: center; flex: 0 1 auto; flex-wrap: wrap; } .button-bar button { margin: 0.25em 1em; } label[for] { cursor: pointer; } .error { color: red; background-color: yellow; } .hidden, .initially-hidden { position: absolute !important; opacity: 0 !important; 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 { border-radius: 0.5em; border: 1px inset; display: flex; flex-direction: column; } .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 > div { display: flex; flex-wrap: wrap; font-size: 70%; margin: 0 0.5em 0.5em 0.5em; } .fieldset > .legend > span { position: relative; } .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 { content: " [hide]"; position: relative; } .fieldset.collapsed > .legend::after { content: " [show]"; position: relative; } span.labeled-input { padding: 0.25em; margin: 0.25em 0.5em; border-radius: 0.25em; white-space: nowrap; background: #0002; } #notes-caveats { border-top: 1px dotted; padding-top: 0.25em; margin-top: 0.5em; } .center { text-align: center; } body.terminal-mode { max-height: calc(100% - 2em); display: flex; flex-direction: column; align-items: stretch; } #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> <!-- 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 id='view-terminal' class='app-view hidden initially-hidden'> This is a placeholder for a terminal-like view. </div> <div id='view-split' class='app-view initially-hidden'> <div class='fieldset options collapsible'> <span class='legend'><span>Options</span></span> <div class=''> <span class='labeled-input'> <input type='checkbox' id='opt-cb-sbs' data-csstgt='#main-wrapper' data-cssclass='side-by-side' data-config='sideBySide'> <label for='opt-cb-sbs'>Side-by-side</label> </span> <span class='labeled-input'> <input type='checkbox' id='opt-cb-swapio' data-csstgt='#main-wrapper' data-cssclass='swapio' data-config='swapInOut'> <label for='opt-cb-swapio'>Swap in/out</label> </span> <span class='labeled-input'> <input type='checkbox' id='opt-cb-autoscroll' data-config='autoScrollOutput'> <label for='opt-cb-autoscroll'>Auto-scroll output</label> </span> <span class='labeled-input'> <input type='checkbox' id='opt-cb-autoclear' data-config='autoClearOutput'> <label for='opt-cb-autoclear'>Auto-clear output</label> </span> <span class='labeled-input'> <input type='file' id='load-db'/> <label>Load DB</label> </span> <span class='labeled-input'> <button id='btn-export'>Download DB</button> </span> <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 --> <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'> <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 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> </div> <!-- #view-split --> <!-- Maintenance notes: ... TODO... currently being refactored... --> <script src="fiddle.js"></script> </body> </html> |
Added ext/fiddle/fiddle.js.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 | /* 2022-05-20 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 the main entry point for the sqlite3 fiddle app. It sets up the various UI bits, loads a Worker for the db connection, and manages the communication between the UI and worker. */ (function(){ 'use strict'; /* Recall that the 'self' symbol, except where locally overwritten, refers to the global window or worker object. */ const storage = (function(NS/*namespace object in which to store this module*/){ /* Pedantic licensing note: this code originated in the Fossil SCM source tree, where it has a different license, but the person who ported it into sqlite is the same one who wrote it for fossil. */ 'use strict'; NS = NS||{}; /** This module provides a basic wrapper around localStorage or sessionStorage or a dummy proxy object if neither of those are available. */ const tryStorage = function f(obj){ if(!f.key) f.key = 'storage.access.check'; try{ obj.setItem(f.key, 'f'); const x = obj.getItem(f.key); obj.removeItem(f.key); if(x!=='f') throw new Error(f.key+" failed") return obj; }catch(e){ return undefined; } }; /** Internal storage impl for this module. */ const $storage = tryStorage(window.localStorage) || tryStorage(window.sessionStorage) || tryStorage({ // A basic dummy xyzStorage stand-in $$$:{}, setItem: function(k,v){this.$$$[k]=v}, getItem: function(k){ return this.$$$.hasOwnProperty(k) ? this.$$$[k] : undefined; }, removeItem: function(k){delete this.$$$[k]}, clear: function(){this.$$$={}} }); /** For the dummy storage we need to differentiate between $storage and its real property storage for hasOwnProperty() to work properly... */ const $storageHolder = $storage.hasOwnProperty('$$$') ? $storage.$$$ : $storage; /** A prefix which gets internally applied to all storage module property keys so that localStorage and sessionStorage across the same browser profile instance do not "leak" across multiple apps being hosted by the same origin server. Such cross-polination is still there but, with this key prefix applied, it won't be immediately visible via the storage API. With this in place we can justify using localStorage instead of sessionStorage. One implication of using localStorage and sessionStorage is that their scope (the same "origin" and client application/profile) allows multiple apps on the same origin to use the same storage. Thus /appA/foo could then see changes made via /appB/foo. The data do not cross user- or browser boundaries, though, so it "might" arguably be called a feature. storageKeyPrefix was added so that we can sandbox that state for each separate app which shares an origin. See: https://fossil-scm.org/forum/forumpost/4afc4d34de Sidebar: it might seem odd to provide a key prefix and stick all properties in the topmost level of the storage object. We do that because adding a layer of object to sandbox each app would mean (de)serializing that whole tree on every storage property change. e.g. instead of storageObject.projectName.foo we have storageObject[storageKeyPrefix+'foo']. That's soley for efficiency's sake (in terms of battery life and environment-internal storage-level effort). */ const storageKeyPrefix = ( $storageHolder===$storage/*localStorage or sessionStorage*/ ? ( (NS.config ? (NS.config.projectCode || NS.config.projectName || NS.config.shortProjectName) : false) || window.location.pathname )+'::' : ( '' /* transient storage */ ) ); /** A proxy for localStorage or sessionStorage or a page-instance-local proxy, if neither one is availble. Which exact storage implementation is uses is unspecified, and apps must not rely on it. */ NS.storage = { storageKeyPrefix: storageKeyPrefix, /** Sets the storage key k to value v, implicitly converting it to a string. */ set: (k,v)=>$storage.setItem(storageKeyPrefix+k,v), /** Sets storage key k to JSON.stringify(v). */ setJSON: (k,v)=>$storage.setItem(storageKeyPrefix+k,JSON.stringify(v)), /** Returns the value for the given storage key, or dflt if the key is not found in the storage. */ get: (k,dflt)=>$storageHolder.hasOwnProperty( storageKeyPrefix+k ) ? $storage.getItem(storageKeyPrefix+k) : dflt, /** Returns true if the given key has a value of "true". If the key is not found, it returns true if the boolean value of dflt is "true". (Remember that JS persistent storage values are all strings.) */ getBool: function(k,dflt){ return 'true'===this.get(k,''+(!!dflt)); }, /** Returns the JSON.parse()'d value of the given storage key's value, or dflt is the key is not found or JSON.parse() fails. */ getJSON: function f(k,dflt){ try { const x = this.get(k,f); return x===f ? dflt : JSON.parse(x); } catch(e){return dflt} }, /** Returns true if the storage contains the given key, else false. */ contains: (k)=>$storageHolder.hasOwnProperty(storageKeyPrefix+k), /** Removes the given key from the storage. Returns this. */ remove: function(k){ $storage.removeItem(storageKeyPrefix+k); return this; }, /** Clears ALL keys from the storage. Returns this. */ clear: function(){ this.keys().forEach((k)=>$storage.removeItem(/*w/o prefix*/k)); return this; }, /** Returns an array of all keys currently in the storage. */ keys: ()=>Object.keys($storageHolder).filter((v)=>(v||'').startsWith(storageKeyPrefix)), /** Returns true if this storage is transient (only available until the page is reloaded), indicating that fileStorage and sessionStorage are unavailable. */ isTransient: ()=>$storageHolder!==$storage, /** Returns a symbolic name for the current storage mechanism. */ storageImplName: function(){ if($storage===window.localStorage) return 'localStorage'; else if($storage===window.sessionStorage) return 'sessionStorage'; else return 'transient'; }, /** Returns a brief help text string for the currently-selected storage type. */ storageHelpDescription: function(){ return { localStorage: "Browser-local persistent storage with an "+ "unspecified long-term lifetime (survives closing the browser, "+ "but maybe not a browser upgrade).", sessionStorage: "Storage local to this browser tab, "+ "lost if this tab is closed.", "transient": "Transient storage local to this invocation of this page." }[this.storageImplName()]; } }; return NS.storage; })({})/*storage API setup*/; /** Name of the stored copy of SqliteFiddle.config. */ const configStorageKey = 'sqlite3-fiddle-config'; /** The SqliteFiddle object is intended to be the primary app-level object for the main-thread side of the sqlite fiddle application. It uses a worker thread to load the sqlite WASM module and communicate with it. */ const SF/*local convenience alias*/ = window.SqliteFiddle/*canonical name*/ = { /* Config options. */ config: { /* If true, SqliteFiddle.echo() will auto-scroll the output widget to the bottom when it receives output, else it won't. */ autoScrollOutput: true, /* If true, the output area will be cleared before each command is run, else it will not. */ autoClearOutput: false, /* If true, SqliteFiddle.echo() will echo its output to the console, in addition to its normal output widget. That slows it down but is useful for testing. */ echoToConsole: false, /* If true, display input/output areas side-by-side. */ sideBySide: false, /* If true, swap positions of the input/output areas. */ swapInOut: false }, /** Emits the given text, followed by a line break, to the output widget. If given more than one argument, they are join()'d together with a space between each. As a special case, if passed a single array, that array is used in place of the arguments array (this is to facilitate receiving lists of arguments via worker events). */ echo: function f(text) { /* Maintenance reminder: we currently require/expect a textarea output element. It might be nice to extend this to behave differently if the output element is a non-textarea element, in which case it would need to append the given text as a TEXT node and add a line break. */ if(!f._){ f._ = document.getElementById('output'); f._.value = ''; // clear browser cache } if(arguments.length > 1) text = Array.prototype.slice.call(arguments).join(' '); else if(1===arguments.length && Array.isArray(text)) text = text.join(' '); // These replacements are necessary if you render to raw HTML //text = text.replace(/&/g, "&"); //text = text.replace(/</g, "<"); //text = text.replace(/>/g, ">"); //text = text.replace('\n', '<br>', 'g'); if(null===text){/*special case: clear output*/ f._.value = ''; return; }else if(this.echo._clearPending){ delete this.echo._clearPending; f._.value = ''; } if(this.config.echoToConsole) console.log(text); if(this.jqTerm) this.jqTerm.echo(text); f._.value += text + "\n"; if(this.config.autoScrollOutput){ f._.scrollTop = f._.scrollHeight; } }, _msgMap: {}, /** Adds a worker message handler for messages of the given type. */ addMsgHandler: function f(type,callback){ if(Array.isArray(type)){ type.forEach((t)=>this.addMsgHandler(t, callback)); return this; } (this._msgMap.hasOwnProperty(type) ? this._msgMap[type] : (this._msgMap[type] = [])).push(callback); return this; }, /** Given a worker message, runs all handlers for msg.type. */ runMsgHandlers: function(msg){ const list = (this._msgMap.hasOwnProperty(msg.type) ? this._msgMap[msg.type] : false); if(!list){ console.warn("No handlers found for message type:",msg); return false; } //console.debug("runMsgHandlers",msg); list.forEach((f)=>f(msg)); return true; }, /** Removes all message handlers for the given message type. */ clearMsgHandlers: function(type){ delete this._msgMap[type]; return this; }, /* Posts a message in the form {type, data} to the db worker. Returns this. */ wMsg: function(type,data){ this.worker.postMessage({type, data}); return this; }, /** Prompts for confirmation and, if accepted, deletes all content and tables in the (transient) database. */ resetDb: function(){ if(window.confirm("Really destroy all content and tables " +"in the (transient) db?")){ this.wMsg('db-reset'); } return this; }, /** Stores this object's config in the browser's storage. */ storeConfig: function(){ storage.setJSON(configStorageKey,this.config); } }; if(1){ /* Restore SF.config */ const storedConfig = storage.getJSON(configStorageKey); if(storedConfig){ /* Copy all properties to SF.config which are currently in storedConfig. We don't bother copying any other properties: those have been removed from the app in the meantime. */ Object.keys(SF.config).forEach(function(k){ if(storedConfig.hasOwnProperty(k)){ SF.config[k] = storedConfig[k]; } }); } } SF.worker = new Worker('fiddle-worker.js'); SF.worker.onmessage = (ev)=>SF.runMsgHandlers(ev.data); SF.addMsgHandler(['stdout', 'stderr'], (ev)=>SF.echo(ev.data)); /* querySelectorAll() proxy */ const EAll = function(/*[element=document,] cssSelector*/){ return (arguments.length>1 ? arguments[0] : document) .querySelectorAll(arguments[arguments.length-1]); }; /* querySelector() proxy */ const E = function(/*[element=document,] cssSelector*/){ return (arguments.length>1 ? arguments[0] : document) .querySelector(arguments[arguments.length-1]); }; /** Handles status updates from the Module object. */ SF.addMsgHandler('module', function f(ev){ ev = ev.data; if('status'!==ev.type){ console.warn("Unexpected module-type message:",ev); return; } if(!f.ui){ f.ui = { status: E('#module-status'), progress: E('#module-progress'), spinner: E('#module-spinner') }; } const msg = ev.data; if(f.ui.progres){ progress.value = msg.step; progress.max = msg.step + 1/*we don't know how many steps to expect*/; } if(1==msg.step){ f.ui.progress.classList.remove('hidden'); f.ui.spinner.classList.remove('hidden'); } if(msg.text){ f.ui.status.classList.remove('hidden'); f.ui.status.innerText = msg.text; }else{ if(f.ui.progress){ f.ui.progress.remove(); f.ui.spinner.remove(); delete f.ui.progress; delete f.ui.spinner; } f.ui.status.classList.add('hidden'); /* The module can post messages about fatal problems, e.g. an exit() being triggered or assertion failure, after the last "load" message has arrived, so leave f.ui.status and message listener intact. */ } }); /** The 'fiddle-ready' event is fired (with no payload) when the wasm module has finished loading. Interestingly, that happens _before_ the final module:status event */ SF.addMsgHandler('fiddle-ready', function(){ SF.clearMsgHandlers('fiddle-ready'); self.onSFLoaded(); }); /** Performs all app initialization which must wait until after the worker module is loaded. This function removes itself when it's called. */ self.onSFLoaded = function(){ delete this.onSFLoaded; // Unhide all elements which start out hidden EAll('.initially-hidden').forEach((e)=>e.classList.remove('initially-hidden')); E('#btn-reset').addEventListener('click',()=>SF.resetDb()); const taInput = E('#input'); const btnClearIn = E('#btn-clear'); btnClearIn.addEventListener('click',function(){ taInput.value = ''; },false); // Ctrl-enter and shift-enter both run the current SQL. taInput.addEventListener('keydown',function(ev){ if((ev.ctrlKey || ev.shiftKey) && 13 === ev.keyCode){ ev.preventDefault(); ev.stopPropagation(); btnShellExec.click(); } }, false); const taOutput = E('#output'); const btnClearOut = E('#btn-clear-output'); btnClearOut.addEventListener('click',function(){ taOutput.value = ''; if(SF.jqTerm) SF.jqTerm.clear(); },false); const btnShellExec = E('#btn-shell-exec'); btnShellExec.addEventListener('click',function(ev){ let sql; ev.preventDefault(); if(taInput.selectionStart<taInput.selectionEnd){ sql = taInput.value.substring(taInput.selectionStart,taInput.selectionEnd).trim(); }else{ sql = taInput.value.trim(); } if(sql) SF.dbExec(sql); },false); const btnInterrupt = E("#btn-interrupt"); //btnInterrupt.classList.add('hidden'); /** To be called immediately before work is sent to the worker. Updates some UI elements. The 'working'/'end' event will apply the inverse, undoing the bits this function does. This impl is not in the 'working'/'start' event handler because that event is given to us asynchronously _after_ we need to have performed this work. */ const preStartWork = function f(){ if(!f._){ const title = E('title'); f._ = { btnLabel: btnShellExec.innerText, pageTitle: title, pageTitleOrig: title.innerText }; } f._.pageTitle.innerText = "[working...] "+f._.pageTitleOrig; btnShellExec.setAttribute('disabled','disabled'); btnInterrupt.removeAttribute('disabled','disabled'); }; /* Sends the given text to the db module to evaluate as if it had been entered in the sqlite3 CLI shell. If it's null or empty, this is a no-op except that the very first call will initialize the db and output an informational header. */ SF.dbExec = function f(sql){ if(this.config.autoClearOutput){ this.echo._clearPending = true; } preStartWork(); this.wMsg('shellExec',sql); }; SF.addMsgHandler('working',function f(ev){ switch(ev.data){ case 'start': /* See notes in preStartWork(). */; return; case 'end': preStartWork._.pageTitle.innerText = preStartWork._.pageTitleOrig; btnShellExec.innerText = preStartWork._.btnLabel; btnShellExec.removeAttribute('disabled'); btnInterrupt.setAttribute('disabled','disabled'); return; } console.warn("Unhandled 'working' event:",ev.data); }); /* For each checkbox with data-csstgt, set up a handler which toggles the given CSS class on the element matching E(data-csstgt). */ EAll('input[type=checkbox][data-csstgt]') .forEach(function(e){ const tgt = E(e.dataset.csstgt); const cssClass = e.dataset.cssclass || 'error'; e.checked = tgt.classList.contains(cssClass); e.addEventListener('change', function(){ tgt.classList[ this.checked ? 'add' : 'remove' ](cssClass) }, false); }); /* For each checkbox with data-config=X, set up a binding to SF.config[X]. These must be set up AFTER data-csstgt checkboxes so that those two states can be synced properly. */ EAll('input[type=checkbox][data-config]') .forEach(function(e){ const confVal = !!SF.config[e.dataset.config]; if(e.checked !== confVal){ /* Ensure that data-csstgt mappings (if any) get synced properly. */ e.checked = confVal; e.dispatchEvent(new Event('change')); } e.addEventListener('change', function(){ SF.config[this.dataset.config] = this.checked; SF.storeConfig(); }, false); }); /* For each button with data-cmd=X, map a click handler which calls SF.dbExec(X). */ const cmdClick = function(){SF.dbExec(this.dataset.cmd);}; EAll('button[data-cmd]').forEach( e => e.addEventListener('click', cmdClick, false) ); btnInterrupt.addEventListener('click',function(){ SF.wMsg('interrupt'); }); /** 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 ]; btnExport.addEventListener('click',function(){ eDisableDuringExport.forEach(e=>e.setAttribute('disabled','disabled')); SF.wMsg('db-export'); }); SF.addMsgHandler('db-export', function(ev){ eDisableDuringExport.forEach(e=>e.removeAttribute('disabled')); ev = ev.data; if(ev.error){ SF.echo("Export failed:",ev.error); return; } const blob = new Blob([ev.buffer], {type:"application/x-sqlite3"}); const a = document.createElement('a'); document.body.appendChild(a); a.href = window.URL.createObjectURL(blob); a.download = ev.filename; a.addEventListener('click',function(){ setTimeout(function(){ SF.echo("Exported (possibly auto-downloaded):",ev.filename); window.URL.revokeObjectURL(a.href); a.remove(); },500); }); a.click(); }); /** Handle load/import of an external db file. */ E('#load-db').addEventListener('change',function(){ const f = this.files[0]; const r = new FileReader(); const status = {loaded: 0, total: 0}; this.setAttribute('disabled','disabled'); r.addEventListener('loadstart', function(){ SF.echo("Loading",f.name,"..."); }); r.addEventListener('progress', function(ev){ SF.echo("Loading progress:",ev.loaded,"of",ev.total,"bytes."); }); const that = this; r.addEventListener('load', function(){ that.removeAttribute('disabled'); SF.echo("Loaded",f.name+". Opening db..."); SF.wMsg('open',{ filename: f.name, buffer: this.result }); }); r.addEventListener('error',function(){ that.removeAttribute('disabled'); SF.echo("Loading",f.name,"failed for unknown reasons."); }); r.addEventListener('abort',function(){ that.removeAttribute('disabled'); SF.echo("Cancelled loading of",f.name+"."); }); r.readAsArrayBuffer(f); }); EAll('.fieldset.collapsible').forEach(function(fs){ const legend = E(fs,'span.legend'), content = EAll(fs,':scope > div'); legend.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 and all of its children, recursively. For some DOM structure cases, a parent may have a reported height of 0 even though children have non-0 sizes. Returns 0 if !e or if the element really has no height. */ const effectiveHeight = function f(e){ if(!e) return 0; if(!f.measure){ f.measure = function callee(e, depth){ if(!e) return; const m = e.getBoundingClientRect(); if(0===depth){ callee.top = m.top; callee.bottom = m.bottom; }else{ callee.top = m.top ? Math.min(callee.top, m.top) : callee.top; callee.bottom = Math.max(callee.bottom, m.bottom); } Array.prototype.forEach.call(e.children,(e)=>callee(e,depth+1)); if(0===depth){ //console.debug("measure() height:",e.className, callee.top, callee.bottom, (callee.bottom - callee.top)); f.extra += callee.bottom - callee.top; } return f.extra; }; } f.extra = 0; f.measure(e,0); return f.extra; }; /** Returns a function, that, as long as it continues to be invoked, will not be triggered. The function will be called after it stops being called for N milliseconds. If `immediate` is passed, call the callback immediately and hinder future invocations until at least the given time has passed. If passed only 1 argument, or passed a falsy 2nd argument, the default wait time set in this function's $defaultDelay property is used. Source: underscore.js, by way of https://davidwalsh.name/javascript-debounce-function */ const debounce = function f(func, wait, immediate) { var timeout; if(!wait) wait = f.$defaultDelay; return function() { const context = this, args = Array.prototype.slice.call(arguments); const later = function() { timeout = undefined; if(!immediate) func.apply(context, args); }; const callNow = immediate && !timeout; clearTimeout(timeout); timeout = setTimeout(later, wait); if(callNow) func.apply(context, args); }; }; debounce.$defaultDelay = 500 /*arbitrary*/; const ForceResizeKludge = (function(){ /* Workaround for Safari mayhem regarding use of vh CSS units.... We cannot use vh units to set the main view size because Safari chokes on that, so we calculate that height here. Larger than ~95% is too big for Firefox on Android, causing the input area to move off-screen. */ const appViews = EAll('.app-view'); const elemsToCount = [ /* Elements which we need to always count in the visible body size. */ E('body > header'), E('body > footer') ]; const resized = function f(){ if(f.$disabled) return; const wh = window.innerHeight; var ht; var extra = 0; elemsToCount.forEach((e)=>e ? extra += effectiveHeight(e) : false); ht = wh - extra; appViews.forEach(function(e){ e.style.height = e.style.maxHeight = [ "calc(", (ht>=100 ? ht : 100), "px", " - 2em"/*fudge value*/,")" /* ^^^^ hypothetically not needed, but both Chrome/FF on Linux will force scrollbars on the body if this value is too small. */ ].join(''); }); }; resized.$disabled = true/*gets deleted when setup is finished*/; window.addEventListener('resize', debounce(resized, 250), false); return resized; })(); /** Set up a selection list of examples */ (function(){ const xElem = E('#select-examples'); const examples = [ {name: "Timer on", sql: ".timer on"}, {name: "Setup table T", sql:`.nullvalue NULL CREATE TABLE t(a,b); INSERT INTO t(a,b) VALUES('abc',123),('def',456),(NULL,789),('ghi',012); SELECT * FROM t;`}, {name: "Table list", sql: ".tables"}, {name: "Box Mode", sql: ".mode box"}, {name: "JSON Mode", sql: ".mode json"}, {name: "Mandlebrot", sql: `WITH RECURSIVE xaxis(x) AS (VALUES(-2.0) UNION ALL SELECT x+0.05 FROM xaxis WHERE x<1.2), yaxis(y) AS (VALUES(-1.0) UNION ALL SELECT y+0.1 FROM yaxis WHERE y<1.0), m(iter, cx, cy, x, y) AS ( SELECT 0, x, y, 0.0, 0.0 FROM xaxis, yaxis UNION ALL SELECT iter+1, cx, cy, x*x-y*y + cx, 2.0*x*y + cy FROM m WHERE (x*x + y*y) < 4.0 AND iter<28 ), m2(iter, cx, cy) AS ( SELECT max(iter), cx, cy FROM m GROUP BY cx, cy ), a(t) AS ( SELECT group_concat( substr(' .+*#', 1+min(iter/7,4), 1), '') FROM m2 GROUP BY cy ) SELECT group_concat(rtrim(t),x'0a') as Mandelbrot FROM a;`} ]; const newOpt = function(lbl,val){ const o = document.createElement('option'); o.value = val; if(!val) o.setAttribute('disabled',true); o.appendChild(document.createTextNode(lbl)); xElem.appendChild(o); }; newOpt("Examples (replaces input!)"); examples.forEach((o)=>newOpt(o.name, o.sql)); //xElem.setAttribute('disabled',true); xElem.selectedIndex = 0; xElem.addEventListener('change', function(){ taInput.value = '-- ' + this.selectedOptions[0].innerText + '\n' + this.value; SF.dbExec(this.value); }); })()/* example queries */; SF.echo(null/*clear any output generated by the init process*/); if(window.jQuery && window.jQuery.terminal){ /* Set up the terminal-style view... */ const eTerm = window.jQuery('#view-terminal').empty(); SF.jqTerm = eTerm.terminal(SF.dbExec.bind(SF),{ prompt: 'sqlite> ', greetings: false /* note that the docs incorrectly call this 'greeting' */ }); /* Set up a button to toggle the views... */ const head = E('header#titlebar'); const btnToggleView = document.createElement('button'); btnToggleView.appendChild(document.createTextNode("Toggle View")); head.appendChild(btnToggleView); btnToggleView.addEventListener('click',function f(){ EAll('.app-view').forEach(e=>e.classList.toggle('hidden')); if(document.body.classList.toggle('terminal-mode')){ ForceResizeKludge(); } }, false); btnToggleView.click()/*default to terminal view*/; } SF.dbExec(null/*init the db and output the header*/); SF.echo('This experimental app is provided in the hope that it', 'may prove interesting or useful but is not an officially', 'supported deliverable of the sqlite project. It is subject to', 'any number of changes or outright removal at any time.\n'); delete ForceResizeKludge.$disabled; ForceResizeKludge(); }/*onSFLoaded()*/; })(); |
Added ext/fiddle/index.md.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 | This directory houses a "fiddle"-style application which embeds a [Web Assembly (WASM)](https://en.wikipedia.org/wiki/WebAssembly) build of the sqlite3 shell app into an HTML page, effectively running the shell in a client-side browser. It requires [emscripten][] and that the build environment be set up for emscripten. A mini-HOWTO for setting that up follows... First, install the Emscripten SDK, as documented [here](https://emscripten.org/docs/getting_started/downloads.html) and summarized below for Linux environments: ``` # Clone the emscripten repository: $ git clone https://github.com/emscripten-core/emsdk.git $ cd emsdk # Download and install the latest SDK tools: $ ./emsdk install latest # Make the "latest" SDK "active" for the current user: $ ./emsdk activate latest ``` Those parts only need to be run once. The following needs to be run for each shell instance which needs the `emcc` compiler: ``` # Activate PATH and other environment variables in the current terminal: $ source ./emsdk_env.sh $ which emcc /path/to/emsdk/upstream/emscripten/emcc ``` That `env` script needs to be sourced for building this application from the top of the sqlite3 build tree: ``` $ make fiddle ``` Or: ``` $ cd ext/fiddle $ make ``` That will generate the fiddle application under [ext/fiddle](/dir/ext/fiddle), as `fiddle.html`. That application cannot, due to XMLHttpRequest security limitations, run if the HTML file is opened directly in the browser (i.e. if it is opened using a `file://` URL), so it needs to be served via an HTTP server. For example, using [althttpd][]: ``` $ cd ext/fiddle $ althttpd -debug 1 -jail 0 -port 9090 -root . ``` Then browse to `http://localhost:9090/fiddle.html`. Note that when serving this app via [althttpd][], it must be a version from 2022-05-17 or newer so that it recognizes the `.wasm` file extension and responds with the mimetype `application/wasm`, as the WASM loader is pedantic about that detail. # Known Quirks and Limitations Some "impedence mismatch" between C and WASM/JavaScript is to be expected. ## No I/O sqlite3 shell commands which require file I/O or pipes are disabled in the WASM build. ## `exit()` Triggered from C When C code calls `exit()`, as happens (for example) when running an "unsafe" command when safe mode is active, WASM's connection to the sqlite3 shell environment has no sensible choice but to shut down because `exit()` leaves it in a state we can no longer recover from. The JavaScript-side application attempts to recognize this and warn the user that restarting the application is necessary. Currently the only way to restart it is to reload the page. Restructuring the shell code such that it could be "rebooted" without restarting the JS app would require some invasive changes which are not currently on any TODO list but have not been entirely ruled out long-term. [emscripten]: https://emscripten.org [althttpd]: https://sqlite.org/althttpd |
Added ext/fiddle/sqlite3-api.js.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 | /* 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. *********************************************************************** This file is intended to be appended to the emcc-generated sqlite3.js via emcc: emcc ... -sMODULARIZE -sEXPORT_NAME=initSqlite3Module --post-js=THIS_FILE It is loaded by importing the emcc-generated sqlite3.js, then: initSqlite3Module({module object}).then( function(theModule){ theModule.sqlite3 == an object containing this file's deliverables: { api: bindings for much of the core sqlite3 APIs, SQLite3: high-level OO API wrapper } }); It is up to the caller to provide a module object compatible with emcc, but it can be a plain empty object. The object passed to initSqlite3Module() will get populated by the emscripten-generated bits and, in part, by the code from this file. Specifically, this file installs the `theModule.sqlite3` part shown above. The resulting sqlite3.api object wraps the standard sqlite3 C API in a way as close to its native form as JS allows for. The sqlite3.SQLite3 object provides a higher-level wrapper more appropriate for general client-side use in JS. Because using certain parts of the low-level API properly requires some degree of WASM-related magic, it is not recommended that that API be used as-is in client-level code. Rather, client code should use the higher-level OO API or write a custom wrapper on top of the lower-level API. In short, most of the C-style API is used in an intuitive manner from JS but any C-style APIs which take pointers-to-pointer arguments require WASM-specific interfaces installed by emcscripten-generated code. Those which take or return only integers, doubles, strings, or "plain" pointers to db or statement objects can be used in "as normal," noting that "pointers" in wasm are simply 32-bit integers. # Goals and Non-goals of this API Goals: - Except where noted in the non-goals, provide a more-or-less complete wrapper to the sqlite3 C API, insofar as WASM feature parity with C allows for. In fact, provide at least 3... - (1) The aforementioned C-style API. (2) An OO-style API on top of that, designed to run in the same thread (main window or Web Worker) as the C API. (3) A less-capable wrapper which can work across the main window/worker boundary, where the sqlite3 API is one of those and this wrapper is in the other. That constellation places some considerable limitations on how the API can be interacted with, but keeping the DB operations out of the UI thread is generally desirable. - Insofar as possible, support client-side storage using JS filesystem APIs. As of this writing, such things are still very much TODO. Non-goals: - As WASM is a web-centric technology and UTF-8 is the King of Encodings in that realm, there are no current plans to support the UTF16-related APIs. They would add a complication to the bindings for no appreciable benefit. - Supporting old or niche-market platforms. WASM is built for a modern web and requires modern platforms. */ 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){ '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 different structure because we want the db connection to run in a worker thread and feed data back into the main thread. Regardless of those differences, it makes a great point of reference: https://github.com/sql-js/sql.js Some of the specific design goals here: - Bind a low-level sqlite3 API which is close to the native one in terms of usage. - Create a higher-level one, more akin to sql.js and node.js-style implementations. This one would speak directly to the low-level API. This API could be used by clients who import the low-level API directly into their main thread (which we don't want to recommend but also don't want to outright forbid). - Create a second higher-level one which speaks to the low-level API via worker messages. This one would be intended for use in the main thread, talking to the low-level UI via worker messages. Because workers have only a single message channel, some acrobatics will be needed here to feed async work results back into client-side callbacks (as those callbacks cannot simply be passed to the worker). Exactly what those acrobatics should look like is not yet entirely clear and much experimentation is pending. */ const SQM = namespace/*the sqlite module object */; /** Set up the main sqlite3 binding API here, mimicking the C API as closely as we can. Attribution: though not a direct copy/paste, much of what follows is strongly influenced by the sql.js implementation. */ const api = { /* It is important that the following integer values match those from the C code. Ideally we could fetch them from the C API, e.g., in the form of a JSON object, but getting that JSON string constructed within our current confines is currently not worth the effort. Reminder to self: we could probably do so by adding the proverbial level of indirection, calling in to C to get it, and having that C func call an emscripten-installed/JS-implemented library function which builds the result object: const obj = {}; sqlite3__get_enum(function(key,val){ obj[key] = val; }); but whether or not we can pass a function that way, via a (void*) is as yet unknown. */ /* Minimum subset of sqlite result codes we'll need. */ SQLITE_OK: 0, SQLITE_ROW: 100, SQLITE_DONE: 101, /* sqlite data types */ SQLITE_INTEGER: 1, SQLITE_FLOAT: 2, SQLITE_TEXT: 3, SQLITE_BLOB: 4, SQLITE_NULL: 5, /* create_function() flags */ SQLITE_DETERMINISTIC: 0x000000800, SQLITE_DIRECTONLY: 0x000080000, SQLITE_INNOCUOUS: 0x000200000, /* sqlite encodings, used for creating UDFs, noting that we will only support UTF8. */ SQLITE_UTF8: 1 }; const cwrap = SQM.cwrap; [/* C-side functions to bind. Each entry is an array with 3 or 4 elements: ["c-side name", "result type" (cwrap() syntax), [arg types in cwrap() syntax] ] If it has 4 elements, the first one is an alternate name to use for the JS-side binding. That's required when overloading a binding for two different uses. */ ["sqlite3_bind_blob","number",["number", "number", "number", "number", "number"]], ["sqlite3_bind_double","number",["number", "number", "number"]], ["sqlite3_bind_int","number",["number", "number", "number"]], /*Noting that JS/wasm combo does not currently support 64-bit integers: ["sqlite3_bind_int64","number",["number", "number", "number"]],*/ ["sqlite3_bind_null","void",["number"]], ["sqlite3_bind_parameter_count", "number", ["number"]], ["sqlite3_bind_parameter_index","number",["number", "string"]], ["sqlite3_bind_text","number",["number", "number", "number", "number", "number"]], ["sqlite3_changes", "number", ["number"]], ["sqlite3_clear_bindings","number",["number"]], ["sqlite3_close_v2", "number", ["number"]], ["sqlite3_column_blob","number", ["number", "number"]], ["sqlite3_column_bytes","number",["number", "number"]], ["sqlite3_column_count", "number", ["number"]], ["sqlite3_column_count","number",["number"]], ["sqlite3_column_double","number",["number", "number"]], ["sqlite3_column_int","number",["number", "number"]], /*Noting that JS/wasm combo does not currently support 64-bit integers: ["sqlite3_column_int64","number",["number", "number"]],*/ ["sqlite3_column_name","string",["number", "number"]], ["sqlite3_column_text","string",["number", "number"]], ["sqlite3_column_type","number",["number", "number"]], ["sqlite3_compileoption_get", "string", ["number"]], ["sqlite3_compileoption_used", "number", ["string"]], ["sqlite3_create_function_v2", "number", ["number", "string", "number", "number","number", "number", "number", "number", "number"]], ["sqlite3_data_count", "number", ["number"]], ["sqlite3_db_filename", "string", ["number", "string"]], ["sqlite3_errmsg", "string", ["number"]], ["sqlite3_exec", "number", ["number", "string", "number", "number", "number"]], ["sqlite3_finalize", "number", ["number"]], ["sqlite3_interrupt", "void", ["number"]], ["sqlite3_libversion", "string", []], ["sqlite3_open", "number", ["string", "number"]], //["sqlite3_open_v2", "number", ["string", "number", "number", "string"]], //^^^^ TODO: add the flags needed for the 3rd arg ["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. */ "number", ["number", "number", "number", "number", "number"]], ["sqlite3_reset", "number", ["number"]], ["sqlite3_result_blob",null,["number", "number", "number", "number"]], ["sqlite3_result_double",null,["number", "number"]], ["sqlite3_result_error",null,["number", "string", "number"]], ["sqlite3_result_int",null,["number", "number"]], ["sqlite3_result_null",null,["number"]], ["sqlite3_result_text",null,["number", "string", "number", "number"]], ["sqlite3_sourceid", "string", []], ["sqlite3_sql", "string", ["number"]], ["sqlite3_step", "number", ["number"]], ["sqlite3_value_blob", "number", ["number"]], ["sqlite3_value_bytes","number",["number"]], ["sqlite3_value_double","number",["number"]], ["sqlite3_value_text", "string", ["number"]], ["sqlite3_value_type", "number", ["number"]] //["sqlite3_normalized_sql", "string", ["number"]] ].forEach(function(a){ const k = (4==a.length) ? a.shift() : a[0]; api[k] = cwrap.apply(this, a); }); /* What follows is colloquially known as "OO API #1". It is a binding of the sqlite3 API which is designed to be run within the same thread (main or worker) as the one in which the sqlite3 WASM binding was initialized. This wrapper cannot use the sqlite3 binding if, e.g., the wrapper is in the main thread and the sqlite3 API is in a worker. */ /** Memory for use in some pointer-to-pointer-passing routines. */ const pPtrArg = stackAlloc(4); /** Throws a new error, concatenating all args with a space between each. */ const toss = function(){ throw new Error(Array.prototype.join.call(arguments, ' ')); }; /** The DB class wraps a sqlite3 db handle. It accepts the following argument signatures: - () - (undefined) (same effect as ()) - (Uint8Array holding an sqlite3 db image) It always generates a random filename and sets is to the `filename` property of this object. 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. */ const DB = function(arg){ const fn = "db-"+((Math.random() * 10000000) | 0)+ "-"+((Math.random() * 10000000) | 0)+".sqlite3"; let buffer; if(name 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."); } if(buffer){ FS.createDataFile("/", fn, buffer, true, true); } setValue(pPtrArg, 0, "i32"); this.checkRc(api.sqlite3_open(fn, pPtrArg)); this._pDb = getValue(pPtrArg, "i32"); this.filename = fn; this._statements = {/*map of open Stmt _pointers_ to Stmt*/}; this._udfs = {/*map of UDF names to wasm function _pointers_*/}; }; /** Internal-use enum for mapping JS types to DB-bindable types. These do not (and need not) line up with the SQLITE_type values. All values in this enum must be truthy and distinct but they need not be numbers. */ const BindTypes = { null: 1, number: 2, string: 3, boolean: 4, blob: 5 }; BindTypes['undefined'] == BindTypes.null; /** This class wraps sqlite3_stmt. Calling this constructor directly will trigger an exception. Use DB.prepare() to create new instances. */ const Stmt = function(){ if(BindTypes!==arguments[2]){ toss("Do not call the Stmt constructor directly. Use DB.prepare()."); } this.db = arguments[0]; this._pStmt = arguments[1]; this.columnCount = api.sqlite3_column_count(this._pStmt); this.parameterCount = api.sqlite3_bind_parameter_count(this._pStmt); this._allocs = [/*list of alloc'd memory blocks for bind() values*/] }; /** Throws if the given DB has been closed, else it is returned. */ const affirmDbOpen = function(db){ if(!db._pDb) toss("DB has been closed."); return db; }; /** Returns true if n is a 32-bit (signed) integer, else false. */ const isInt32 = function(n){ return (n===n|0 && n<0xFFFFFFFF) ? true : undefined; }; /** Expects to be passed (arguments) from DB.exec() and DB.execMulti(). Does the argument processing/validation, throws on error, and returns a new object on success: { sql: the SQL, obt: optionsObj, cbArg: function} cbArg is only set if the opt.callback is set, in which case it's a function which expects to be passed the current Stmt and returns the callback argument of the type indicated by the input arguments. */ const parseExecArgs = function(args){ const out = {}; switch(args.length){ case 1: if('string'===typeof args[0]){ out.sql = args[0]; out.opt = {}; }else if(args[0] && 'object'===typeof args[0]){ out.opt = args[0]; out.sql = out.opt.sql; } break; case 2: out.sql = args[0]; out.opt = args[1]; break; default: toss("Invalid argument count for exec()."); }; if('string'!==typeof out.sql) toss("Missing SQL argument."); if(out.opt.callback){ switch((undefined===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; default: toss("Invalid rowMode:",out.opt.rowMode); } } return out; }; /** If object opts has _its own_ property named p then that property's value is returned, else dflt is returned. */ const getOwnOption = (opts, p, dflt)=> opts.hasOwnProperty(p) ? opts[p] : dflt; DB.prototype = { /** Expects to be given an sqlite3 API result code. If it is falsy, this function returns this object, else it throws an exception with an error message from sqlite3_errmsg(), using this object's db handle. Note that if it's passed a non-error code like SQLITE_ROW or SQLITE_DONE, it will still throw but the error string might be "Not an error." The various non-0 non-error codes need to be checked for in client code where they are expected. */ checkRc: function(sqliteResultCode){ if(!sqliteResultCode) return this; toss("sqlite result code",sqliteResultCode+":", api.sqlite3_errmsg(this._pDb) || "Unknown db error."); }, /** Finalizes all open statements and closes this database connection. This is a no-op if the db has already been closed. */ close: function(){ if(this._pDb){ let s; const that = this; Object.keys(this._statements).forEach(function(k,s){ delete that._statements[k]; if(s && s._pStmt) s.finalize(); }); 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; } }, /** Similar to this.filename but will return NULL for special names like ":memory:". Not of much use until we have filesystem support. Throws if the DB has been closed. If passed an argument it then it will return the filename of the ATTACHEd db with that name, else it assumes a name of `main`. */ fileName: function(dbName){ return api.sqlite3_db_filename(affirmDbOpen(this)._pDb, dbName||"main"); }, /** Compiles the given SQL and returns a prepared Stmt. This is the only way to create new Stmt objects. Throws on error. */ prepare: function(sql){ affirmDbOpen(this); setValue(pPtrArg,0,"i32"); this.checkRc(api.sqlite3_prepare_v2(this._pDb, sql, -1, pPtrArg, null)); const pStmt = getValue(pPtrArg, "i32"); if(!pStmt) toss("Empty SQL is not permitted."); const stmt = new Stmt(this, pStmt, BindTypes); this._statements[pStmt] = stmt; 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 supports one additional option not used by execMulti(): - .multi: if true, this function acts as a proxy for execMulti(). */ exec: function(/*(sql [,optionsObj]) or (optionsObj)*/){ affirmDbOpen(this); const arg = parseExecArgs(arguments); if(!arg.sql) return this; else if(arg.opt.multi){ return this.execMulti(arg, undefined, BindTypes); } const opt = arg.opt; let stmt; try { stmt = this.prepare(arg.sql); if(opt.bind) stmt.bind(opt.bind); if(opt.callback){ while(stmt.step()){ stmt._isLocked = true; opt.callback(arg.cbArg(stmt), stmt); stmt._isLocked = false; } }else{ stmt.step(); } }finally{ if(stmt){ delete stmt._isLocked; stmt.finalize(); } } return this; }/*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. If no SQL is provided, or a non-string is provided, an exception is triggered. Empty SQL, on the other hand, is simply a no-op. The optional options object may contain any of the following properties: - .sql = the SQL to run (unless it's provided as the first argument). - .bind = a single value valid as an argument for Stmt.bind(). This is ONLY applied to the FIRST non-empty statement in the SQL which has any bindable parameters. (Empty statements are skipped entirely.) - .callback = a function which gets called for each row of the FIRST statement in the SQL (if it has any result rows). The second argument passed to the callback is always the current Stmt object (so that the caller may collect column names, or similar). The first argument passed to the callback defaults to the current Stmt object but may be changed with ... - .rowMode = a string describing what type of argument should be passed as the first argument to the callback. A value of 'object' causes the results of `stmt.get({})` to be passed to the object. A value of 'array' causes the results of `stmt.get([])` to be passed to the callback. A value of 'stmt' is equivalent to the default, passing the current Stmt to the callback (noting that it's always passed as the 2nd argument). Any other value triggers an exception. - saveSql = an optional array. If set, the SQL of each executed statement is appended to this array before the statement is executed (but after it is prepared - we don't have the string until after that). Empty SQL statements are elided. 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 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()). */ execMulti: function(/*(sql [,obj]) || (obj)*/){ affirmDbOpen(this); const arg = (BindTypes===arguments[2] /* ^^^ Being passed on from exec() */ ? arguments[0] : parseExecArgs(arguments)); if(!arg.sql) return this; const opt = arg.opt; const stack = stackSave(); let stmt; let bind = opt.bind; let rowMode = ( (opt.callback && opt.rowMode) ? opt.rowMode : false); try{ let pSql = SQM.allocateUTF8OnStack(arg.sql) const pzTail = stackAlloc(4); while(getValue(pSql, "i8")){ setValue(pPtrArg, 0, "i32"); setValue(pzTail, 0, "i32"); this.checkRc(api.sqlite3_prepare_v2_sqlptr( this._pDb, pSql, -1, pPtrArg, pzTail )); const pStmt = getValue(pPtrArg, "i32"); pSql = getValue(pzTail, "i32"); if(!pStmt) continue; if(opt.saveSql){ opt.saveSql.push(api.sqlite3_sql(pStmt).trim()); } stmt = new Stmt(this, pStmt, BindTypes); if(bind && stmt.parameterCount){ stmt.bind(bind); bind = null; } if(opt.callback && null!==rowMode){ while(stmt.step()){ stmt._isLocked = true; callback(arg.cbArg(stmt), stmt); stmt._isLocked = false; } rowMode = null; }else{ // Do we need to while(stmt.step()){} here? stmt.step(); } stmt.finalize(); stmt = null; } }finally{ if(stmt){ delete stmt._isLocked; stmt.finalize(); } stackRestore(stack); } return this; }/*execMulti()*/, /** Creates a new scalar UDF (User-Defined Function) which is accessible via SQL code. This function may be called in any of the following forms: - (name, function) - (name, function, optionsObject) - (name, optionsObject) - (optionsObject) In the final two cases, the function must be defined as the 'callback' property of the options object. In the final case, the function's name must be the 'name' property. This can only be used to create scalar functions, not aggregate or window functions. UDFs cannot be removed from a DB handle after they're added. On success, returns this object. Throws on error. When called from SQL, arguments to the UDF, and its result, will be converted between JS and SQL with as much fidelity as is feasible, triggering an exception if a type conversion cannot be determined. Some freedom is afforded to numeric conversions due to friction between the JS and C worlds: integers which are larger than 32 bits will be treated as doubles, as JS does not support 64-bit integers and it is (as of this writing) illegal to use WASM functions which take or return 64-bit integers from JS. The optional options object may contain flags to modify how the function is defined: - .arity: the number of arguments which SQL calls to this function expect or require. The default value is the callback's length property (i.e. the number of declared parameters it has). A value of -1 means that the function is variadic and may accept any number of arguments, up to sqlite3's compile-time limits. sqlite3 will enforce the argument count if is zero or greater. The following properties correspond to flags documented at: https://sqlite.org/c3ref/create_function.html - .deterministic = SQLITE_DETERMINISTIC - .directOnly = SQLITE_DIRECTONLY - .innocuous = SQLITE_INNOCUOUS Maintenance reminder: the ability to add new WASM-accessible functions to the runtime requires that the WASM build is compiled with emcc's `-sALLOW_TABLE_GROWTH` flag. */ createFunction: function f(name, callback,opt){ switch(arguments.length){ case 1: /* (optionsObject) */ opt = name; name = opt.name; callback = opt.callback; break; case 2: /* (name, callback|optionsObject) */ if(!(callback instanceof Function)){ opt = callback; callback = opt.callback; } break; default: break; } if(!opt) opt = {}; if(!(callback instanceof Function)){ toss("Invalid arguments: expecting a callback function."); }else if('string' !== typeof name){ toss("Invalid arguments: missing function name."); } if(!f._extractArgs){ /* Static init */ f._extractArgs = function(argc, pArgv){ let i, pVal, valType, arg; const tgt = []; for(i = 0; i < argc; ++i){ pVal = getValue(pArgv + (4 * i), "i32"); valType = api.sqlite3_value_type(pVal); switch(valType){ case api.SQLITE_INTEGER: case api.SQLITE_FLOAT: arg = api.sqlite3_value_double(pVal); break; case SQLITE_TEXT: arg = api.sqlite3_value_text(pVal); break; case SQLITE_BLOB:{ const n = api.sqlite3_value_bytes(ptr); const pBlob = api.sqlite3_value_blob(ptr); arg = new Uint8Array(n); let i; for(i = 0; i < n; ++i) arg[i] = HEAP8[pBlob+i]; break; } default: arg = null; break; } tgt.push(arg); } return tgt; }/*_extractArgs()*/; f._setResult = function(pCx, val){ switch(typeof val) { case 'boolean': api.sqlite3_result_int(pCx, val ? 1 : 0); break; case 'number': { (isInt32(val) ? api.sqlite3_result_int : api.sqlite3_result_double)(pCx, val); break; } case 'string': api.sqlite3_result_text(pCx, val, -1, -1/*==SQLITE_TRANSIENT*/); break; case 'object': if(null===val) { api.sqlite3_result_null(pCx); break; }else if(undefined!==val.length){ const pBlob = SQM.allocate(val, SQM.ALLOC_NORMAL); api.sqlite3_result_blob(pCx, pBlob, val.length, -1/*==SQLITE_TRANSIENT*/); SQM._free(blobptr); break; } // else fall through default: toss("Don't not how to handle this UDF result value:",val); }; }/*_setResult()*/; }/*static init*/ const wrapper = function(pCx, argc, pArgv){ try{ f._setResult(pCx, callback.apply(null, f._extractArgs(argc, pArgv))); }catch(e){ api.sqlite3_result_error(pCx, e.message, -1); } }; const pUdf = SQM.addFunction(wrapper, "viii"); let fFlags = 0; if(getOwnOption(opt, 'deterministic')) fFlags |= api.SQLITE_DETERMINISTIC; if(getOwnOption(opt, 'directOnly')) fFlags |= api.SQLITE_DIRECTONLY; if(getOwnOption(opt, 'innocuous')) fFlags |= api.SQLITE_INNOCUOUS; name = name.toLowerCase(); try { this.checkRc(api.sqlite3_create_function_v2( this._pDb, name, (opt.hasOwnProperty('arity') ? +opt.arity : callback.length), api.SQLITE_UTF8 | fFlags, null/*pApp*/, pUdf, null/*xStep*/, null/*xFinal*/, null/*xDestroy*/)); }catch(e){ SQM.removeFunction(pUdf); throw e; } if(this._udfs.hasOwnProperty(name)){ SQM.removeFunction(this._udfs[name]); } this._udfs[name] = pUdf; return this; }/*createFunction()*/, /** Prepares the given SQL, step()s it one time, and returns the value of the first result column. If it has no results, undefined is returned. If passed a second argument, it is treated like an argument to Stmt.bind(), so may be any type supported by that function. Throws on error (e.g. malformed SQL). */ selectValue: function(sql,bind){ let stmt, rc; try { stmt = this.prepare(sql).bind(bind); if(stmt.step()) rc = stmt.get(0); }finally{ if(stmt) stmt.finalize(); } return rc; }, /** 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. Maintenance reminder: the corresponding sql.js impl of this feature closes the current db, finalizing any active statements and (seemingly unnecessarily) destroys any UDFs, copies the file, and then re-opens it (without restoring the UDFs). Those gymnastics are not necessary on the tested platform but might be necessary on others. Because of that eventuality, this interface currently enforces that no statements are active when this is run. It will throw if any are. */ exportBinaryImage: function(){ affirmDbOpen(this); if(Object.keys(this._statements).length){ toss("Cannot export with prepared statements active!", "finalize() all statements and try again."); } const img = FS.readFile(this.filename, {encoding:"binary"}); return img; } }/*DB.prototype*/; /** Throws if the given Stmt has been finalized, else stmt is returned. */ const affirmStmtOpen = function(stmt){ if(!stmt._pStmt) toss("Stmt has been closed."); return stmt; }; /** Returns an opaque truthy value from the BindTypes enum if v's type is a valid bindable type, else returns a falsy value. As a special case, a value of undefined is treated as a bind type of null. */ const isSupportedBindType = function(v){ let t = BindTypes[(null===v||undefined===v) ? 'null' : typeof v]; switch(t){ case BindTypes.boolean: case BindTypes.null: case BindTypes.number: case BindTypes.string: return t; default: if(v instanceof Uint8Array) return BindTypes.blob; return undefined; } }; /** If isSupportedBindType(v) returns a truthy value, this function returns that value, else it throws. */ const affirmSupportedBindType = function(v){ return isSupportedBindType(v) || toss("Unsupport bind() argument type."); }; /** If key is a number and within range of stmt's bound parameter count, key is returned. If key is not a number then it is checked against named parameters. If a match is found, its index is returned. Else it throws. */ const affirmParamIndex = function(stmt,key){ const n = ('number'===typeof key) ? key : api.sqlite3_bind_parameter_index(stmt._pStmt, key); if(0===n || (n===key && (n!==(n|0)/*floating point*/))){ toss("Invalid bind() parameter name: "+key); } else if(n<1 || n>stmt.parameterCount) toss("Bind index",key,"is out of range."); return n; }; /** Throws if ndx is not an integer or if it is out of range for stmt.columnCount, else returns stmt. Reminder: this will also fail after the statement is finalized but the resulting error will be about an out-of-bounds column index. */ const affirmColIndex = function(stmt,ndx){ if((ndx !== (ndx|0)) || ndx<0 || ndx>=stmt.columnCount){ toss("Column index",ndx,"is out of range."); } return stmt; }; /** If stmt._isLocked is truthy, this throws an exception complaining that the 2nd argument (an operation name, e.g. "bind()") is not legal while the statement is "locked". Locking happens before an exec()-like callback is passed a statement, to ensure that the callback does not mutate or finalize the statement. If it does not throw, it returns stmt. */ const affirmUnlocked = function(stmt,currentOpName){ if(stmt._isLocked){ toss("Operation is illegal when statement is locked:",currentOpName); } return stmt; }; /** Binds a single bound parameter value on the given stmt at the given index (numeric or named) using the given bindType (see the BindTypes enum) and value. Throws on error. Returns stmt on success. */ const bindOne = function f(stmt,ndx,bindType,val){ affirmUnlocked(stmt, 'bind()'); if(!f._){ f._ = { string: function(stmt, ndx, val, asBlob){ const bytes = intArrayFromString(val,true); const pStr = SQM.allocate(bytes, ALLOC_NORMAL); stmt._allocs.push(pStr); const func = asBlob ? api.sqlite3_bind_blob : api.sqlite3_bind_text; return func(stmt._pStmt, ndx, pStr, bytes.length, 0); } }; } affirmSupportedBindType(val); ndx = affirmParamIndex(stmt,ndx); let rc = 0; switch((null===val || undefined===val) ? BindTypes.null : bindType){ case BindTypes.null: rc = api.sqlite3_bind_null(stmt._pStmt, ndx); break; case BindTypes.string:{ rc = f._.string(stmt, ndx, val, false); break; } case BindTypes.number: { const m = (isInt32(val) ? api.sqlite3_bind_int /*It's illegal to bind a 64-bit int from here*/ : api.sqlite3_bind_double); rc = m(stmt._pStmt, ndx, val); break; } case BindTypes.boolean: rc = api.sqlite3_bind_int(stmt._pStmt, ndx, val ? 1 : 0); break; case BindTypes.blob: { if('string'===typeof val){ rc = f._.string(stmt, ndx, val, true); }else{ const len = val.length; if(undefined===len){ toss("Binding a value as a blob requires", "that it have a length member."); } const pBlob = SQM.allocate(val, ALLOC_NORMAL); stmt._allocs.push(pBlob); rc = api.sqlite3_bind_blob(stmt._pStmt, ndx, pBlob, len, 0); } } default: toss("Unsupported bind() argument type."); } if(rc) stmt.db.checkRc(rc); return stmt; }; /** Frees any memory explicitly allocated for the given Stmt object. Returns stmt. */ const freeBindMemory = function(stmt){ let m; while(undefined !== (m = stmt._allocs.pop())){ SQM._free(m); } return stmt; }; Stmt.prototype = { /** "Finalizes" this statement. This is a no-op if the statement has already been finalizes. Returns undefined. Most methods in this class will throw if called after this is. */ finalize: function(){ if(this._pStmt){ affirmUnlocked(this,'finalize()'); freeBindMemory(this); delete this.db._statements[this._pStmt]; api.sqlite3_finalize(this._pStmt); delete this.columnCount; delete this.parameterCount; delete this._pStmt; delete this.db; delete this._isLocked; } }, /** Clears all bound values. Returns this object. Throws if this statement has been finalized. */ clearBindings: function(){ freeBindMemory( affirmUnlocked(affirmStmtOpen(this), 'clearBindings()') ); api.sqlite3_clear_bindings(this._pStmt); this._mayGet = false; return this; }, /** Resets this statement so that it may be step()ed again from the beginning. Returns this object. Throws if this statement has been finalized. If passed a truthy argument then this.clearBindings() is also called, otherwise any existing bindings, along with any memory allocated for them, are retained. */ reset: function(alsoClearBinds){ affirmUnlocked(this,'reset()'); if(alsoClearBinds) this.clearBindings(); api.sqlite3_reset(affirmStmtOpen(this)._pStmt); this._mayGet = false; return this; }, /** Binds one or more values to its bindable parameters. It accepts 1 or 2 arguments: If passed a single argument, it must be either an array, an object, or a value of a bindable type (see below). If passed 2 arguments, the first one is the 1-based bind index or bindable parameter name and the second one must be a value of a bindable type. Bindable value types: - null is bound as NULL. - undefined as a standalone value is a no-op intended to simplify certain client-side use cases: passing undefined as a value to this function will not actually bind anything and this function will skip confirmation that binding is even legal. (Those semantics simplify certain client-side uses.) Conversely, a value of undefined as an array or object property when binding an array/object (see below) is treated the same as null. - Numbers are bound as either doubles or integers: doubles if they are larger than 32 bits, else double or int32, depending on whether they have a fractional part. (It is, as of this writing, illegal to call (from JS) a WASM function which either takes or returns an int64.) Booleans are bound as integer 0 or 1. It is not expected the distinction of binding doubles which have no fractional parts is integers is significant for the majority of clients due to sqlite3's data typing model. This API does not currently support the BigInt type. - Strings are bound as strings (use bindAsBlob() to force blob binding). - Uint8Array instances are bound as blobs. If passed an array, each element of the array is bound at the parameter index equal to the array index plus 1 (because arrays are 0-based but binding is 1-based). If passed an object, each object key is treated as a bindable parameter name. The object keys _must_ match any bindable parameter names, including any `$`, `@`, or `:` prefix. Because `$` is a legal identifier chararacter in JavaScript, that is the suggested prefix for bindable parameters. It returns this object on success and throws on error. Errors include: - Any bind index is out of range, a named bind parameter does not match, or this statement has no bindable parameters. - Any value to bind is of an unsupported type. - Passed no arguments or more than two. - The statement has been finalized. */ bind: function(/*[ndx,] arg*/){ affirmStmtOpen(this); let ndx, arg; switch(arguments.length){ case 1: ndx = 1; arg = arguments[0]; break; case 2: ndx = arguments[0]; arg = arguments[1]; break; default: toss("Invalid bind() arguments."); } if(undefined===arg){ /* It might seem intuitive to bind undefined as NULL but this approach simplifies certain client-side uses when passing on arguments between 2+ levels of functions. */ return this; }else if(!this.parameterCount){ toss("This statement has no bindable parameters."); } this._mayGet = false; if(null===arg){ /* bind NULL */ return bindOne(this, ndx, BindTypes.null, arg); } else if(Array.isArray(arg)){ /* bind each entry by index */ if(1!==arguments.length){ toss("When binding an array, an index argument is not permitted."); } arg.forEach((v,i)=>bindOne(this, i+1, affirmSupportedBindType(v), v)); return this; } else if('object'===typeof arg/*null was checked above*/){ /* bind by name */ if(1!==arguments.length){ toss("When binding an object, an index argument is not permitted."); } Object.keys(arg) .forEach(k=>bindOne(this, k, affirmSupportedBindType(arg[k]), arg[k])); return this; }else{ return bindOne(this, ndx, affirmSupportedBindType(arg), arg); } toss("Should not reach this point."); }, /** Special case of bind() which binds the given value using the BLOB binding mechanism instead of the default selected one for the value. The ndx may be a numbered or named bind index. The value must be of type string, Uint8Array, or null/undefined (both treated as null). If passed a single argument, a bind index of 1 is assumed. */ bindAsBlob: function(ndx,arg){ affirmStmtOpen(this); if(1===arguments.length){ ndx = 1; arg = arguments[0]; } const t = affirmSupportedBindType(arg); if(BindTypes.string !== t && BindTypes.blob !== t && BindTypes.null !== t){ toss("Invalid value type for bindAsBlob()"); } this._mayGet = false; return bindOne(this, ndx, BindTypes.blob, arg); }, /** Steps the statement one time. If the result indicates that a row of data is available, true is returned. If no row of data is available, false is returned. Throws on error. */ step: function(){ affirmUnlocked(this, 'step()'); const rc = api.sqlite3_step(affirmStmtOpen(this)._pStmt); switch(rc){ case api.SQLITE_DONE: return this._mayGet = false; case api.SQLITE_ROW: return this._mayGet = true; default: this._mayGet = false; console.warn("sqlite3_step() rc=",rc,"SQL =", api.sqlite3_sql(this._pStmt)); this.db.checkRc(rc); }; }, /** Fetches the value from the given 0-based column index of the current data row, throwing if index is out of range. Requires that step() has just returned a truthy value, else an exception is thrown. By default it will determine the data type of the result automatically. If passed a second arugment, it must be one of the enumeration values for sqlite3 types, which are defined as members of the sqlite3 module: SQLITE_INTEGER, SQLITE_FLOAT, SQLITE_TEXT, SQLITE_BLOB. Any other value, except for undefined, will trigger an exception. Passing undefined is the same as not passing a value. It is legal to, e.g., fetch an integer value as a string, in which case sqlite3 will convert the value to a string. If ndx is an array, this function behaves a differently: it assigns the indexes of the array, from 0 to the number of result columns, to the values of the corresponding column, and returns that array. If ndx is a plain object, this function behaves even differentlier: it assigns the properties of the object to the values of their corresponding result columns. Blobs are returned as Uint8Array instances. Potential TODO: add type ID SQLITE_JSON, which fetches the result as a string and passes it (if it's not null) to JSON.parse(), returning the result of that. Until then, getJSON() can be used for that. */ get: function(ndx,asType){ if(!affirmStmtOpen(this)._mayGet){ toss("Stmt.step() has not (recently) returned true."); } if(Array.isArray(ndx)){ let i = 0; while(i<this.columnCount){ ndx[i] = this.get(i++); } return ndx; }else if(ndx && 'object'===typeof ndx){ let i = 0; while(i<this.columnCount){ ndx[api.sqlite3_column_name(this._pStmt,i)] = this.get(i++); } return ndx; } affirmColIndex(this, ndx); switch(undefined===asType ? api.sqlite3_column_type(this._pStmt, ndx) : asType){ case api.SQLITE_NULL: return null; case api.SQLITE_INTEGER:{ return 0 | api.sqlite3_column_double(this._pStmt, ndx); /* ^^^^^^^^ strips any fractional part and handles handles >32bits */ } case api.SQLITE_FLOAT: return api.sqlite3_column_double(this._pStmt, ndx); case api.SQLITE_TEXT: return api.sqlite3_column_text(this._pStmt, ndx); case api.SQLITE_BLOB: { const n = api.sqlite3_column_bytes(this._pStmt, ndx); 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]; return rc; } default: toss("Don't know how to translate", "type of result column #"+ndx+"."); } abort("Not reached."); }, /** Equivalent to get(ndx) but coerces the result to an integer. */ getInt: function(ndx){return this.get(ndx,api.SQLITE_INTEGER)}, /** Equivalent to get(ndx) but coerces the result to a float. */ getFloat: function(ndx){return this.get(ndx,api.SQLITE_FLOAT)}, /** Equivalent to get(ndx) but coerces the result to a string. */ getString: function(ndx){return this.get(ndx,api.SQLITE_TEXT)}, /** Equivalent to get(ndx) but coerces the result to a Uint8Array. */ getBlob: function(ndx){return this.get(ndx,api.SQLITE_BLOB)}, /** A convenience wrapper around get() which fetches the value as a string and then, if it is not null, passes it to JSON.parse(), returning that result. Throws if parsing fails. If the result is null, null is returned. An empty string, on the other hand, will trigger an exception. */ getJSON: function(ndx){ const s = this.get(ndx, api.SQLITE_STRING); return null===s ? s : JSON.parse(s); }, /** Returns the result column name of the given index, or throws if index is out of bounds or this statement has been finalized. This can be used without having run step() first. */ getColumnName: function(ndx){ return api.sqlite3_column_name( affirmColIndex(affirmStmtOpen(this),ndx)._pStmt, ndx ); }, /** If this statement potentially has result columns, this function returns an array of all such names. If passed an array, it is used as the target and all names are appended to it. Returns the target array. Throws if this statement cannot have result columns. This object's columnCount member holds the number of columns. */ getColumnNames: function(tgt){ affirmColIndex(affirmStmtOpen(this),0); if(!tgt) tgt = []; for(let i = 0; i < this.columnCount; ++i){ tgt.push(api.sqlite3_column_name(this._pStmt, i)); } return tgt; }, /** If this statement has named bindable parameters and the given name matches one, its 1-based bind index is returned. If no match is found, 0 is returned. If it has no bindable parameters, the undefined value is returned. */ getParamIndex: function(name){ return (affirmStmtOpen(this).parameterCount ? api.sqlite3_bind_parameter_index(this._pStmt, name) : undefined); } }/*Stmt.prototype*/; /** OO binding's namespace. */ const SQLite3 = { version: { lib: api.sqlite3_libversion(), ooApi: "0.0.1" }, DB, Stmt, /** Reports whether a given compile-time option, named by the given argument. 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 which maps each such option to true or false, indicating whether or not the given option was included in this build. That object is returned. If optName is an object, its keys are expected to be compilation options and this function sets each entry to true or false. That object is returned. If passed no arguments then it returns an object mapping all known compilation options to their compile-time values, or boolean true if they are defined with no value. In all other cases it returns true if the given option was active when when compiling the sqlite3 module, else false. Compile-time option names may optionally include their "SQLITE_" prefix. When it returns an object of all options, the prefix is elided. */ compileOptionUsed: function f(optName){ if(!arguments.length){ if(!f._opt){ f._rx = /^([^=]+)=(.+)/; f._rxInt = /^-?\d+$/; f._opt = function(opt, rv){ const m = f._rx.exec(opt); rv[0] = (m ? m[1] : opt); rv[1] = m ? (f._rxInt.test(m[2]) ? +m[2] : m[2]) : true; }; } const rc = {}, ov = [0,0]; let i = 0, k; while((k = api.sqlite3_compileoption_get(i++))){ f._opt(k,ov); rc[ov[0]] = ov[1]; } return rc; } else if(Array.isArray(optName)){ const rc = {}; optName.forEach((v)=>{ rc[v] = api.sqlite3_compileoption_used(v); }); return rc; } else if('object' === typeof optName){ Object.keys(optName).forEach((k)=> { optName[k] = api.sqlite3_compileoption_used(k); }); return optName; } return ( 'string'===typeof optName ) ? !!api.sqlite3_compileoption_used(optName) : false; } }; namespace.sqlite3 = { api: api, SQLite3 }; }); |
Added ext/fiddle/testing.css.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | textarea { font-family: monospace; } header { font-size: 130%; font-weight: bold; } .hidden, .initially-hidden { position: absolute !important; opacity: 0 !important; pointer-events: none !important; display: none !important; } fieldset.options { font-size: 75%; } fieldset > legend { padding: 0 0.5em; } span.labeled-input { padding: 0.25em; margin: 0.25em 0.5em; border-radius: 0.25em; white-space: nowrap; background: #0002; } .center { text-align: center; } .error { color: red; background-color: yellow; } |
Added ext/fiddle/testing1.html.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | <!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-api.js tests</title> <style></style> </head> <body> <header id='titlebar'><span>sqlite3-api.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="sqlite3.js"></script> <script src="SqliteTestUtil.js"></script> <script src="testing1.js"></script> </body> </html> |
Added ext/fiddle/testing1.js.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 | /* 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-api.js. */ (function(){ const T = self.SqliteTestUtil; const log = console.log.bind(console); const assert = function(condition, text) { if (!condition) { throw new Error('Assertion failed' + (text ? ': ' + text : '')); } }; const test1 = function(db,sqlite3){ const api = sqlite3.api; log("Basic sanity tests..."); T.assert(db._pDb); let st = db.prepare("select 3 as a"); //log("statement =",st); T.assert(st._pStmt) .assert(!st._mayGet) .assert('a' === st.getColumnName(0)) .assert(st === db._statements[st._pStmt]) .assert(1===st.columnCount) .assert(0===st.parameterCount) .mustThrow(()=>st.bind(1,null)) .assert(true===st.step()) .assert(3 === st.get(0)) .mustThrow(()=>st.get(1)) .mustThrow(()=>st.get(0,~api.SQLITE_INTEGER)) .assert(3 === st.get(0,api.SQLITE_INTEGER)) .assert(3 === st.getInt(0)) .assert('3' === st.get(0,api.SQLITE_TEXT)) .assert('3' === st.getString(0)) .assert(3.0 === st.get(0,api.SQLITE_FLOAT)) .assert(3.0 === st.getFloat(0)) .assert(st.get(0,api.SQLITE_BLOB) instanceof Uint8Array) .assert(st.getBlob(0) instanceof Uint8Array) .assert(3 === st.get([])[0]) .assert(3 === st.get({}).a) .assert(3 === st.getJSON(0)) .assert(st._mayGet) .assert(false===st.step()) .assert(!st._mayGet) ; let pId = st._pStmt; st.finalize(); T.assert(!st._pStmt) .assert(!db._statements[pId]); let list = []; db.exec({ sql:`CREATE TABLE t(a,b); INSERT INTO t(a,b) VALUES(1,2),(3,4),(?,?);`, multi: true, saveSql: list, bind: [5,6] }); T.assert(2 === list.length); //log("Exec'd SQL:", list); let counter = 0, colNames = []; db.exec("SELECT a a, b b FROM t",{ rowMode: 'object', 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); 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(6 === counter); }; const testUDF = function(db){ log("Testing UDF..."); db.createFunction("foo",function(a,b){return a+b}); T.assert(7===db.selectValue("select foo(3,4)")). assert(5===db.selectValue("select foo(3,?)",2)). assert(5===db.selectValue("select foo(?,?)",[1,4])). assert(5===db.selectValue("select foo($a,$b)",{$a:0,$b:5})); db.createFunction("bar", { arity: -1, callback: function(){ var rc = 0; for(let i = 0; i < arguments.length; ++i) rc += arguments[i]; return rc; } }); log("Testing DB::selectValue() w/ UDF..."); T.assert(0===db.selectValue("select bar()")). assert(1===db.selectValue("select bar(1)")). assert(3===db.selectValue("select bar(1,2)")). assert(-1===db.selectValue("select bar(1,2,-4)")); 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})); }; const runTests = function(Module){ T.assert(Module._free instanceof Function). assert(Module.allocate instanceof Function). assert(Module.addFunction instanceof Function). assert(Module.removeFunction instanceof Function); const sqlite3 = Module.sqlite3; const api = sqlite3.api; const oo = sqlite3.SQLite3; console.log("Loaded module:",api.sqlite3_libversion(), api.sqlite3_sourceid()); log("Build options:",oo.compileOptionUsed()); const db = new oo.DB(); try { log("DB:",db.filename); [ test1, testUDF ].forEach((f)=>f(db, sqlite3)); }finally{ db.close(); } log("Total Test count:",T.counter); }; initSqlite3Module(self.sqlite3TestModule).then(function(theModule){ /** Use a timeout so that we are (hopefully) out from under the module init stack when our setup gets run. Just on principle, not because we _need_ to be. */ //console.debug("theModule =",theModule); setTimeout(()=>runTests(theModule), 0); }); })(); |
Changes to src/alter.c.
︙ | ︙ | |||
1313 1314 1315 1316 1317 1318 1319 | if( pStep->pSelect ){ sqlite3SelectPrep(pParse, pStep->pSelect, &sNC); if( pParse->nErr ) rc = pParse->rc; } if( rc==SQLITE_OK && pStep->zTarget ){ SrcList *pSrc = sqlite3TriggerStepSrc(pParse, pStep); if( pSrc ){ | > > > > > > > > > > > > > > > > > | | | < | | < < < < < < < < < < < > | | 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 | if( pStep->pSelect ){ sqlite3SelectPrep(pParse, pStep->pSelect, &sNC); if( pParse->nErr ) rc = pParse->rc; } if( rc==SQLITE_OK && pStep->zTarget ){ SrcList *pSrc = sqlite3TriggerStepSrc(pParse, pStep); if( pSrc ){ Select *pSel = sqlite3SelectNew( pParse, pStep->pExprList, pSrc, 0, 0, 0, 0, 0, 0 ); if( pSel==0 ){ pStep->pExprList = 0; pSrc = 0; rc = SQLITE_NOMEM; }else{ sqlite3SelectPrep(pParse, pSel, 0); rc = pParse->nErr ? SQLITE_ERROR : SQLITE_OK; assert( pStep->pExprList==0 || pStep->pExprList==pSel->pEList ); assert( pSrc==pSel->pSrc ); if( pStep->pExprList ) pSel->pEList = 0; pSel->pSrc = 0; sqlite3SelectDelete(db, pSel); } if( pStep->pFrom ){ int i; for(i=0; i<pStep->pFrom->nSrc && rc==SQLITE_OK; i++){ SrcItem *p = &pStep->pFrom->a[i]; if( p->pSelect ){ sqlite3SelectPrep(pParse, p->pSelect, 0); } } } if( db->mallocFailed ){ rc = SQLITE_NOMEM; } sNC.pSrcList = pSrc; if( rc==SQLITE_OK && pStep->pWhere ){ rc = sqlite3ResolveExprNames(&sNC, pStep->pWhere); } if( rc==SQLITE_OK ){ |
︙ | ︙ | |||
1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 | if( isLegacy==0 ){ rc = renameResolveTrigger(&sParse); if( rc==SQLITE_OK ){ renameWalkTrigger(&sWalker, pTrigger); for(pStep=pTrigger->step_list; pStep; pStep=pStep->pNext){ if( pStep->zTarget && 0==sqlite3_stricmp(pStep->zTarget, zOld) ){ renameTokenFind(&sParse, &sCtx, pStep->zTarget); } } } } } #endif } | > > > > > > > > > | 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 | if( isLegacy==0 ){ rc = renameResolveTrigger(&sParse); if( rc==SQLITE_OK ){ renameWalkTrigger(&sWalker, pTrigger); for(pStep=pTrigger->step_list; pStep; pStep=pStep->pNext){ if( pStep->zTarget && 0==sqlite3_stricmp(pStep->zTarget, zOld) ){ renameTokenFind(&sParse, &sCtx, pStep->zTarget); } if( pStep->pFrom ){ int i; for(i=0; i<pStep->pFrom->nSrc; i++){ SrcItem *pItem = &pStep->pFrom->a[i]; if( 0==sqlite3_stricmp(pItem->zName, zOld) ){ renameTokenFind(&sParse, &sCtx, pItem->zName); } } } } } } } #endif } |
︙ | ︙ |
Changes to src/expr.c.
︙ | ︙ | |||
1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 | sqlite3 *db = pParse->db; assert( pToken ); pNew = sqlite3ExprAlloc(db, TK_FUNCTION, pToken, 1); if( pNew==0 ){ sqlite3ExprListDelete(db, pList); /* Avoid memory leak when malloc fails */ return 0; } pNew->w.iOfst = (int)(pToken->z - pParse->zTail); if( pList && pList->nExpr > pParse->db->aLimit[SQLITE_LIMIT_FUNCTION_ARG] && !pParse->nested ){ sqlite3ErrorMsg(pParse, "too many arguments on function %T", pToken); } | > | 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 | sqlite3 *db = pParse->db; assert( pToken ); pNew = sqlite3ExprAlloc(db, TK_FUNCTION, pToken, 1); if( pNew==0 ){ sqlite3ExprListDelete(db, pList); /* Avoid memory leak when malloc fails */ return 0; } assert( !ExprHasProperty(pNew, EP_InnerON|EP_OuterON) ); pNew->w.iOfst = (int)(pToken->z - pParse->zTail); if( pList && pList->nExpr > pParse->db->aLimit[SQLITE_LIMIT_FUNCTION_ARG] && !pParse->nested ){ sqlite3ErrorMsg(pParse, "too many arguments on function %T", pToken); } |
︙ | ︙ | |||
1343 1344 1345 1346 1347 1348 1349 | #ifndef SQLITE_OMIT_WINDOWFUNC || ExprHasProperty(p, EP_WinFunc) #endif ){ nSize = EXPR_FULLSIZE; }else{ assert( !ExprHasProperty(p, EP_TokenOnly|EP_Reduced) ); | | | 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 | #ifndef SQLITE_OMIT_WINDOWFUNC || ExprHasProperty(p, EP_WinFunc) #endif ){ nSize = EXPR_FULLSIZE; }else{ assert( !ExprHasProperty(p, EP_TokenOnly|EP_Reduced) ); assert( !ExprHasProperty(p, EP_OuterON) ); assert( !ExprHasProperty(p, EP_MemToken) ); assert( !ExprHasVVAProperty(p, EP_NoReduce) ); if( p->pLeft || p->x.pList ){ nSize = EXPR_REDUCEDSIZE | EP_Reduced; }else{ assert( p->pRight==0 ); nSize = EXPR_TOKENONLYSIZE | EP_TokenOnly; |
︙ | ︙ | |||
2169 2170 2171 2172 2173 2174 2175 | ** malformed schema error. */ static int exprNodeIsConstant(Walker *pWalker, Expr *pExpr){ /* If pWalker->eCode is 2 then any term of the expression that comes from ** the ON or USING clauses of an outer join disqualifies the expression ** from being considered constant. */ | | | 2170 2171 2172 2173 2174 2175 2176 2177 2178 2179 2180 2181 2182 2183 2184 | ** malformed schema error. */ static int exprNodeIsConstant(Walker *pWalker, Expr *pExpr){ /* If pWalker->eCode is 2 then any term of the expression that comes from ** the ON or USING clauses of an outer join disqualifies the expression ** from being considered constant. */ if( pWalker->eCode==2 && ExprHasProperty(pExpr, EP_OuterON) ){ pWalker->eCode = 0; return WRC_Abort; } switch( pExpr->op ){ /* Consider functions to be constant if all their arguments are constant ** and either pWalker->eCode==4 or 5 or the function has the |
︙ | ︙ | |||
2316 2317 2318 2319 2320 2321 2322 | ** clause, not an ON clause. */ int sqlite3ExprIsTableConstraint(Expr *pExpr, const SrcItem *pSrc){ if( pSrc->fg.jointype & JT_LTORJ ){ return 0; /* rule (3) */ } if( pSrc->fg.jointype & JT_LEFT ){ | | | | 2317 2318 2319 2320 2321 2322 2323 2324 2325 2326 2327 2328 2329 2330 2331 2332 2333 2334 | ** clause, not an ON clause. */ int sqlite3ExprIsTableConstraint(Expr *pExpr, const SrcItem *pSrc){ if( pSrc->fg.jointype & JT_LTORJ ){ return 0; /* rule (3) */ } if( pSrc->fg.jointype & JT_LEFT ){ if( !ExprHasProperty(pExpr, EP_OuterON) ) return 0; /* rule (4a) */ if( pExpr->w.iJoin!=pSrc->iCursor ) return 0; /* rule (4b) */ }else{ if( ExprHasProperty(pExpr, EP_OuterON) ) return 0; /* rule (5) */ } return sqlite3ExprIsTableConstant(pExpr, pSrc->iCursor); /* rules (1), (2) */ } /* ** sqlite3WalkExpr() callback used by sqlite3ExprIsConstantOrGroupBy(). |
︙ | ︙ | |||
2741 2742 2743 2744 2745 2746 2747 | int eType = 0; /* Type of RHS table. IN_INDEX_* */ int iTab; /* Cursor of the RHS table */ int mustBeUnique; /* True if RHS must be unique */ Vdbe *v = sqlite3GetVdbe(pParse); /* Virtual machine being coded */ assert( pX->op==TK_IN ); mustBeUnique = (inFlags & IN_INDEX_LOOP)!=0; | < < < | < | 2742 2743 2744 2745 2746 2747 2748 2749 2750 2751 2752 2753 2754 2755 2756 | int eType = 0; /* Type of RHS table. IN_INDEX_* */ int iTab; /* Cursor of the RHS table */ int mustBeUnique; /* True if RHS must be unique */ Vdbe *v = sqlite3GetVdbe(pParse); /* Virtual machine being coded */ assert( pX->op==TK_IN ); mustBeUnique = (inFlags & IN_INDEX_LOOP)!=0; iTab = pParse->nTab++; /* If the RHS of this IN(...) operator is a SELECT, and if it matters ** whether or not the SELECT result contains NULL values, check whether ** or not NULL is actually possible (it may not be, for example, due ** to NOT NULL constraints in the schema). If no NULL values are possible, ** set prRhsHasNull to 0 before continuing. */ if( prRhsHasNull && ExprUseXSelect(pX) ){ |
︙ | ︙ | |||
3079 3080 3081 3082 3083 3084 3085 | if( ExprUseXSelect(pExpr) ){ ExplainQueryPlan((pParse, 0, "REUSE LIST SUBQUERY %d", pExpr->x.pSelect->selId)); } assert( ExprUseYSub(pExpr) ); sqlite3VdbeAddOp2(v, OP_Gosub, pExpr->y.sub.regReturn, pExpr->y.sub.iAddr); | | | < | 3076 3077 3078 3079 3080 3081 3082 3083 3084 3085 3086 3087 3088 3089 3090 3091 | if( ExprUseXSelect(pExpr) ){ ExplainQueryPlan((pParse, 0, "REUSE LIST SUBQUERY %d", pExpr->x.pSelect->selId)); } assert( ExprUseYSub(pExpr) ); sqlite3VdbeAddOp2(v, OP_Gosub, pExpr->y.sub.regReturn, pExpr->y.sub.iAddr); assert( iTab!=pExpr->iTable ); sqlite3VdbeAddOp2(v, OP_OpenDup, iTab, pExpr->iTable); sqlite3VdbeJumpHere(v, addrOnce); return; } /* Begin coding the subroutine */ assert( !ExprUseYWin(pExpr) ); ExprSetProperty(pExpr, EP_Subrtn); |
︙ | ︙ | |||
5074 5075 5076 5077 5078 5079 5080 | if( xJump ){ xJump(pParse, &exprAnd, dest, jumpIfNull); }else{ /* Mark the expression is being from the ON or USING clause of a join ** so that the sqlite3ExprCodeTarget() routine will not attempt to move ** it into the Parse.pConstExpr list. We should use a new bit for this, ** for clarity, but we are out of bits in the Expr.flags field so we | | | | 5070 5071 5072 5073 5074 5075 5076 5077 5078 5079 5080 5081 5082 5083 5084 5085 | if( xJump ){ xJump(pParse, &exprAnd, dest, jumpIfNull); }else{ /* Mark the expression is being from the ON or USING clause of a join ** so that the sqlite3ExprCodeTarget() routine will not attempt to move ** it into the Parse.pConstExpr list. We should use a new bit for this, ** for clarity, but we are out of bits in the Expr.flags field so we ** have to reuse the EP_OuterON bit. Bummer. */ pDel->flags |= EP_OuterON; sqlite3ExprCodeTarget(pParse, &exprAnd, dest); } sqlite3ReleaseTempReg(pParse, regFree1); } sqlite3ExprDelete(db, pDel); /* Ensure adequate test coverage */ |
︙ | ︙ | |||
5760 5761 5762 5763 5764 5765 5766 | ** This routine controls an optimization. False positives (setting ** pWalker->eCode to 1 when it should not be) are deadly, but false-negatives ** (never setting pWalker->eCode) is a harmless missed optimization. */ static int impliesNotNullRow(Walker *pWalker, Expr *pExpr){ testcase( pExpr->op==TK_AGG_COLUMN ); testcase( pExpr->op==TK_AGG_FUNCTION ); | | | 5756 5757 5758 5759 5760 5761 5762 5763 5764 5765 5766 5767 5768 5769 5770 | ** This routine controls an optimization. False positives (setting ** pWalker->eCode to 1 when it should not be) are deadly, but false-negatives ** (never setting pWalker->eCode) is a harmless missed optimization. */ static int impliesNotNullRow(Walker *pWalker, Expr *pExpr){ testcase( pExpr->op==TK_AGG_COLUMN ); testcase( pExpr->op==TK_AGG_FUNCTION ); if( ExprHasProperty(pExpr, EP_OuterON) ) return WRC_Prune; switch( pExpr->op ){ case TK_ISNOT: case TK_ISNULL: case TK_NOTNULL: case TK_IS: case TK_OR: case TK_VECTOR: |
︙ | ︙ | |||
5857 5858 5859 5860 5861 5862 5863 | ** False negatives are acceptable. In other words, it is ok to return ** zero even if expression p will never be true of every column of iTab ** is NULL. A false negative is merely a missed optimization opportunity. ** ** False positives are not allowed, however. A false positive may result ** in an incorrect answer. ** | | | 5853 5854 5855 5856 5857 5858 5859 5860 5861 5862 5863 5864 5865 5866 5867 | ** False negatives are acceptable. In other words, it is ok to return ** zero even if expression p will never be true of every column of iTab ** is NULL. A false negative is merely a missed optimization opportunity. ** ** False positives are not allowed, however. A false positive may result ** in an incorrect answer. ** ** Terms of p that are marked with EP_OuterON (and hence that come from ** the ON or USING clauses of OUTER JOINS) are excluded from the analysis. ** ** This routine is used to check if a LEFT JOIN can be converted into ** an ordinary JOIN. The p argument is the WHERE clause. If the WHERE ** clause requires that some column of the right table of the LEFT JOIN ** be non-NULL, then the LEFT JOIN can be safely converted into an ** ordinary join. |
︙ | ︙ |
Changes to src/func.c.
︙ | ︙ | |||
2096 2097 2098 2099 2100 2101 2102 | } ans = log(x)/b; }else{ ans = log(x); switch( SQLITE_PTR_TO_INT(sqlite3_user_data(context)) ){ case 1: /* Convert from natural logarithm to log base 10 */ | | | | 2096 2097 2098 2099 2100 2101 2102 2103 2104 2105 2106 2107 2108 2109 2110 2111 2112 2113 2114 | } ans = log(x)/b; }else{ ans = log(x); switch( SQLITE_PTR_TO_INT(sqlite3_user_data(context)) ){ case 1: /* Convert from natural logarithm to log base 10 */ ans /= M_LN10; break; case 2: /* Convert from natural logarithm to log base 2 */ ans /= M_LN2; break; default: break; } } sqlite3_result_double(context, ans); } |
︙ | ︙ |
Changes to src/loadext.c.
︙ | ︙ | |||
499 500 501 502 503 504 505 | 0, 0, 0, #endif /* Version 3.39.0 and later */ #ifndef SQLITE_OMIT_DESERIALIZE sqlite3_deserialize, | | | > | 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 | 0, 0, 0, #endif /* Version 3.39.0 and later */ #ifndef SQLITE_OMIT_DESERIALIZE sqlite3_deserialize, sqlite3_serialize, #else 0, 0, #endif sqlite3_db_name }; /* True if x is the directory separator character */ #if SQLITE_OS_WIN # define DirSep(X) ((X)=='/'||(X)=='\\') #else |
︙ | ︙ |
Changes to src/main.c.
︙ | ︙ | |||
4621 4622 4623 4624 4625 4626 4627 4628 4629 4630 4631 4632 4633 4634 | /* ** Return the Btree pointer identified by zDbName. Return NULL if not found. */ Btree *sqlite3DbNameToBtree(sqlite3 *db, const char *zDbName){ int iDb = zDbName ? sqlite3FindDbName(db, zDbName) : 0; return iDb<0 ? 0 : db->aDb[iDb].pBt; } /* ** Return the filename of the database associated with a database ** connection. */ const char *sqlite3_db_filename(sqlite3 *db, const char *zDbName){ Btree *pBt; | > > > > > > > > > > > > > > > > > > | 4621 4622 4623 4624 4625 4626 4627 4628 4629 4630 4631 4632 4633 4634 4635 4636 4637 4638 4639 4640 4641 4642 4643 4644 4645 4646 4647 4648 4649 4650 4651 4652 | /* ** Return the Btree pointer identified by zDbName. Return NULL if not found. */ Btree *sqlite3DbNameToBtree(sqlite3 *db, const char *zDbName){ int iDb = zDbName ? sqlite3FindDbName(db, zDbName) : 0; return iDb<0 ? 0 : db->aDb[iDb].pBt; } /* ** Return the name of the N-th database schema. Return NULL if N is out ** of range. */ const char *sqlite3_db_name(sqlite3 *db, int N){ #ifdef SQLITE_ENABLE_API_ARMOR if( !sqlite3SafetyCheckOk(db) ){ (void)SQLITE_MISUSE_BKPT; return 0; } #endif if( N<0 || N>=db->nDb ){ return 0; }else{ return db->aDb[N].zDbSName; } } /* ** Return the filename of the database associated with a database ** connection. */ const char *sqlite3_db_filename(sqlite3 *db, const char *zDbName){ Btree *pBt; |
︙ | ︙ |
Changes to src/os.h.
︙ | ︙ | |||
34 35 36 37 38 39 40 41 42 43 44 45 46 47 | #endif /* Maximum pathname length. Note: FILENAME_MAX defined by stdio.h */ #ifndef SQLITE_MAX_PATHLEN # define SQLITE_MAX_PATHLEN FILENAME_MAX #endif /* ** The default size of a disk sector */ #ifndef SQLITE_DEFAULT_SECTOR_SIZE # define SQLITE_DEFAULT_SECTOR_SIZE 4096 #endif | > > > > > > > | 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 | #endif /* Maximum pathname length. Note: FILENAME_MAX defined by stdio.h */ #ifndef SQLITE_MAX_PATHLEN # define SQLITE_MAX_PATHLEN FILENAME_MAX #endif /* Maximum number of symlinks that will be resolved while trying to ** expand a filename in xFullPathname() in the VFS. */ #ifndef SQLITE_MAX_SYMLINK # define SQLITE_MAX_SYMLINK 200 #endif /* ** The default size of a disk sector */ #ifndef SQLITE_DEFAULT_SECTOR_SIZE # define SQLITE_DEFAULT_SECTOR_SIZE 4096 #endif |
︙ | ︙ |
Changes to src/os_unix.c.
︙ | ︙ | |||
6469 6470 6471 6472 6473 6474 6475 | }else{ *pResOut = osAccess(zPath, W_OK|R_OK)==0; } return SQLITE_OK; } /* | | < < > > > > | | | > | < | < < | | > > > > > > > > > > > > > > > > | < < < < < < < | < < < < < < < | < | < < | < < | | | > > > > > > > > > > > | < < < > > > > | < > | | > > > | > | < < < < | < | > | > > > > > | < > > | < < < < > > > | | | < < > > | < > | < | < > < < < < < < < < | < | < < < < < < | < < < < | < < < < < < | < | < < | < < | < < | < < < | < < < < < < < < < < | < < < < | < | | < < < < < | < | < | | < < | 6469 6470 6471 6472 6473 6474 6475 6476 6477 6478 6479 6480 6481 6482 6483 6484 6485 6486 6487 6488 6489 6490 6491 6492 6493 6494 6495 6496 6497 6498 6499 6500 6501 6502 6503 6504 6505 6506 6507 6508 6509 6510 6511 6512 6513 6514 6515 6516 6517 6518 6519 6520 6521 6522 6523 6524 6525 6526 6527 6528 6529 6530 6531 6532 6533 6534 6535 6536 6537 6538 6539 6540 6541 6542 6543 6544 6545 6546 6547 6548 6549 6550 6551 6552 6553 6554 6555 6556 6557 6558 6559 6560 6561 6562 6563 6564 6565 6566 6567 6568 6569 6570 6571 6572 6573 6574 6575 6576 6577 6578 6579 6580 6581 6582 6583 6584 6585 6586 6587 6588 6589 6590 6591 6592 6593 6594 6595 6596 6597 6598 6599 6600 6601 6602 6603 6604 6605 6606 6607 6608 6609 6610 6611 6612 | }else{ *pResOut = osAccess(zPath, W_OK|R_OK)==0; } return SQLITE_OK; } /* ** A pathname under construction */ typedef struct DbPath DbPath; struct DbPath { int rc; /* Non-zero following any error */ int nSymlink; /* Number of symlinks resolved */ char *zOut; /* Write the pathname here */ int nOut; /* Bytes of space available to zOut[] */ int nUsed; /* Bytes of zOut[] currently being used */ }; /* Forward reference */ static void appendAllPathElements(DbPath*,const char*); /* ** Append a single path element to the DbPath under construction */ static void appendOnePathElement( DbPath *pPath, /* Path under construction, to which to append zName */ const char *zName, /* Name to append to pPath. Not zero-terminated */ int nName /* Number of significant bytes in zName */ ){ assert( nName>0 ); assert( zName!=0 ); if( zName[0]=='.' ){ if( nName==1 ) return; if( zName[1]=='.' && nName==2 ){ if( pPath->nUsed<=1 ){ pPath->rc = SQLITE_ERROR; return; } assert( pPath->zOut[0]=='/' ); while( pPath->zOut[--pPath->nUsed]!='/' ){} return; } } if( pPath->nUsed + nName + 2 >= pPath->nOut ){ pPath->rc = SQLITE_ERROR; return; } pPath->zOut[pPath->nUsed++] = '/'; memcpy(&pPath->zOut[pPath->nUsed], zName, nName); pPath->nUsed += nName; #if defined(HAVE_READLINK) && defined(HAVE_LSTAT) if( pPath->rc==SQLITE_OK ){ const char *zIn; struct stat buf; pPath->zOut[pPath->nUsed] = 0; zIn = pPath->zOut; if( osLstat(zIn, &buf)!=0 ){ if( errno!=ENOENT ){ pPath->rc = unixLogError(SQLITE_CANTOPEN_BKPT, "lstat", zIn); } }else if( S_ISLNK(buf.st_mode) ){ ssize_t got; char zLnk[SQLITE_MAX_PATHLEN+2]; if( pPath->nSymlink++ > SQLITE_MAX_SYMLINK ){ pPath->rc = SQLITE_CANTOPEN_BKPT; return; } got = osReadlink(zIn, zLnk, sizeof(zLnk)-2); if( got<=0 || got>=(ssize_t)sizeof(zLnk)-2 ){ pPath->rc = unixLogError(SQLITE_CANTOPEN_BKPT, "readlink", zIn); return; } zLnk[got] = 0; if( zLnk[0]=='/' ){ pPath->nUsed = 0; }else{ pPath->nUsed -= nName + 1; } appendAllPathElements(pPath, zLnk); } } #endif } /* ** Append all path elements in zPath to the DbPath under construction. */ static void appendAllPathElements( DbPath *pPath, /* Path under construction, to which to append zName */ const char *zPath /* Path to append to pPath. Is zero-terminated */ ){ int i = 0; int j = 0; do{ while( zPath[i] && zPath[i]!='/' ){ i++; } if( i>j ){ appendOnePathElement(pPath, &zPath[j], i-j); } j = i+1; }while( zPath[i++] ); } /* ** Turn a relative pathname into a full pathname. The relative path ** is stored as a nul-terminated string in the buffer pointed to by ** zPath. ** ** zOut points to a buffer of at least sqlite3_vfs.mxPathname bytes ** (in this case, MAX_PATHNAME bytes). The full-path is written to ** this buffer before returning. */ static int unixFullPathname( sqlite3_vfs *pVfs, /* Pointer to vfs object */ const char *zPath, /* Possibly relative input path */ int nOut, /* Size of output buffer in bytes */ char *zOut /* Output buffer */ ){ DbPath path; UNUSED_PARAMETER(pVfs); path.rc = 0; path.nUsed = 0; path.nSymlink = 0; path.nOut = nOut; path.zOut = zOut; if( zPath[0]!='/' ){ char zPwd[SQLITE_MAX_PATHLEN+2]; if( osGetcwd(zPwd, sizeof(zPwd)-2)==0 ){ return unixLogError(SQLITE_CANTOPEN_BKPT, "getcwd", zPath); } appendAllPathElements(&path, zPwd); } appendAllPathElements(&path, zPath); zOut[path.nUsed] = 0; if( path.rc || path.nUsed<2 ) return SQLITE_CANTOPEN_BKPT; if( path.nSymlink ) return SQLITE_OK_SYMLINK; return SQLITE_OK; } #ifndef SQLITE_OMIT_LOAD_EXTENSION /* ** Interfaces for opening a shared library, finding entry points ** within the shared library, and closing the shared library. */ #include <dlfcn.h> |
︙ | ︙ |
Changes to src/parse.y.
︙ | ︙ | |||
935 936 937 938 939 940 941 | ////////////////////////// The UPDATE command //////////////////////////////// // %if SQLITE_ENABLE_UPDATE_DELETE_LIMIT || SQLITE_UDL_CAPABLE_PARSER cmd ::= with UPDATE orconf(R) xfullname(X) indexed_opt(I) SET setlist(Y) from(F) where_opt_ret(W) orderby_opt(O) limit_opt(L). { sqlite3SrcListIndexedBy(pParse, X, &I); | > > > > > > > > > > | > > > > > > > > > > > | > | 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 | ////////////////////////// The UPDATE command //////////////////////////////// // %if SQLITE_ENABLE_UPDATE_DELETE_LIMIT || SQLITE_UDL_CAPABLE_PARSER cmd ::= with UPDATE orconf(R) xfullname(X) indexed_opt(I) SET setlist(Y) from(F) where_opt_ret(W) orderby_opt(O) limit_opt(L). { sqlite3SrcListIndexedBy(pParse, X, &I); if( F ){ SrcList *pFromClause = F; if( pFromClause->nSrc>1 ){ Select *pSubquery; Token as; pSubquery = sqlite3SelectNew(pParse,0,pFromClause,0,0,0,0,SF_NestedFrom,0); as.n = 0; as.z = 0; pFromClause = sqlite3SrcListAppendFromTerm(pParse,0,0,0,&as,pSubquery,0); } X = sqlite3SrcListAppendList(pParse, X, pFromClause); } sqlite3ExprListCheckLength(pParse,Y,"set list"); #ifndef SQLITE_ENABLE_UPDATE_DELETE_LIMIT if( O || L ){ updateDeleteLimitError(pParse,O,L); O = 0; L = 0; } #endif sqlite3Update(pParse,X,Y,W,R,O,L,0); } %else cmd ::= with UPDATE orconf(R) xfullname(X) indexed_opt(I) SET setlist(Y) from(F) where_opt_ret(W). { sqlite3SrcListIndexedBy(pParse, X, &I); sqlite3ExprListCheckLength(pParse,Y,"set list"); if( F ){ SrcList *pFromClause = F; if( pFromClause->nSrc>1 ){ Select *pSubquery; Token as; pSubquery = sqlite3SelectNew(pParse,0,pFromClause,0,0,0,0,SF_NestedFrom,0); as.n = 0; as.z = 0; pFromClause = sqlite3SrcListAppendFromTerm(pParse,0,0,0,&as,pSubquery,0); } X = sqlite3SrcListAppendList(pParse, X, pFromClause); } sqlite3Update(pParse,X,Y,W,R,0,0,0); } %endif %type setlist {ExprList*} |
︙ | ︙ | |||
1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 | A = sqlite3PExpr(pParse,TK_IS,A,Y); binaryToUnaryIfNull(pParse, Y, A, TK_ISNULL); } expr(A) ::= expr(A) IS NOT expr(Y). { A = sqlite3PExpr(pParse,TK_ISNOT,A,Y); binaryToUnaryIfNull(pParse, Y, A, TK_NOTNULL); } expr(A) ::= NOT(B) expr(X). {A = sqlite3PExpr(pParse, @B, X, 0);/*A-overwrites-B*/} expr(A) ::= BITNOT(B) expr(X). {A = sqlite3PExpr(pParse, @B, X, 0);/*A-overwrites-B*/} expr(A) ::= PLUS|MINUS(B) expr(X). [BITNOT] { A = sqlite3PExpr(pParse, @B==TK_PLUS ? TK_UPLUS : TK_UMINUS, X, 0); | > > > > > > > > | 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 | A = sqlite3PExpr(pParse,TK_IS,A,Y); binaryToUnaryIfNull(pParse, Y, A, TK_ISNULL); } expr(A) ::= expr(A) IS NOT expr(Y). { A = sqlite3PExpr(pParse,TK_ISNOT,A,Y); binaryToUnaryIfNull(pParse, Y, A, TK_NOTNULL); } expr(A) ::= expr(A) IS NOT DISTINCT FROM expr(Y). { A = sqlite3PExpr(pParse,TK_IS,A,Y); binaryToUnaryIfNull(pParse, Y, A, TK_ISNULL); } expr(A) ::= expr(A) IS DISTINCT FROM expr(Y). { A = sqlite3PExpr(pParse,TK_ISNOT,A,Y); binaryToUnaryIfNull(pParse, Y, A, TK_NOTNULL); } expr(A) ::= NOT(B) expr(X). {A = sqlite3PExpr(pParse, @B, X, 0);/*A-overwrites-B*/} expr(A) ::= BITNOT(B) expr(X). {A = sqlite3PExpr(pParse, @B, X, 0);/*A-overwrites-B*/} expr(A) ::= PLUS|MINUS(B) expr(X). [BITNOT] { A = sqlite3PExpr(pParse, @B==TK_PLUS ? TK_UPLUS : TK_UMINUS, X, 0); |
︙ | ︙ | |||
1274 1275 1276 1277 1278 1279 1280 | ** expr1 IN () ** expr1 NOT IN () ** ** simplify to constants 0 (false) and 1 (true), respectively, ** regardless of the value of expr1. */ sqlite3ExprUnmapAndDelete(pParse, A); | | > | 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 | ** expr1 IN () ** expr1 NOT IN () ** ** simplify to constants 0 (false) and 1 (true), respectively, ** regardless of the value of expr1. */ sqlite3ExprUnmapAndDelete(pParse, A); A = sqlite3Expr(pParse->db, TK_STRING, N ? "true" : "false"); if( A ) sqlite3ExprIdToTrueFalse(A); }else{ Expr *pRHS = Y->a[0].pExpr; if( Y->nExpr==1 && sqlite3ExprIsConstant(pRHS) && A->op!=TK_VECTOR ){ Y->a[0].pExpr = 0; sqlite3ExprListDelete(pParse->db, Y); pRHS = sqlite3PExpr(pParse, TK_UPLUS, pRHS, 0); A = sqlite3PExpr(pParse, TK_EQ, A, pRHS); |
︙ | ︙ |
Changes to src/printf.c.
︙ | ︙ | |||
950 951 952 953 954 955 956 | } /* ** If pExpr has a byte offset for the start of a token, record that as ** as the error offset. */ void sqlite3RecordErrorOffsetOfExpr(sqlite3 *db, const Expr *pExpr){ | | > > | 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 | } /* ** If pExpr has a byte offset for the start of a token, record that as ** as the error offset. */ void sqlite3RecordErrorOffsetOfExpr(sqlite3 *db, const Expr *pExpr){ while( pExpr && (ExprHasProperty(pExpr,EP_OuterON|EP_InnerON) || pExpr->w.iOfst<=0) ){ pExpr = pExpr->pLeft; } if( pExpr==0 ) return; db->errByteOffset = pExpr->w.iOfst; } /* |
︙ | ︙ |
Changes to src/resolve.c.
︙ | ︙ | |||
924 925 926 927 928 929 930 | NameContext *p; int i; for(i=0, p=pNC; p && i<ArraySize(anRef); p=p->pNext, i++){ anRef[i] = p->nRef; } sqlite3WalkExpr(pWalker, pExpr->pLeft); if( 0==sqlite3ExprCanBeNull(pExpr->pLeft) && !IN_RENAME_OBJECT ){ | | | 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 | NameContext *p; int i; for(i=0, p=pNC; p && i<ArraySize(anRef); p=p->pNext, i++){ anRef[i] = p->nRef; } sqlite3WalkExpr(pWalker, pExpr->pLeft); if( 0==sqlite3ExprCanBeNull(pExpr->pLeft) && !IN_RENAME_OBJECT ){ testcase( ExprHasProperty(pExpr, EP_OuterON) ); assert( !ExprHasProperty(pExpr, EP_IntValue) ); if( pExpr->op==TK_NOTNULL ){ pExpr->u.zToken = "true"; ExprSetProperty(pExpr, EP_IsTrue); }else{ pExpr->u.zToken = "false"; ExprSetProperty(pExpr, EP_IsFalse); |
︙ | ︙ |
Changes to src/select.c.
︙ | ︙ | |||
374 375 376 377 378 379 380 | return 1; } } return 0; } /* | | | | 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 | return 1; } } return 0; } /* ** Set the EP_OuterON property on all terms of the given expression. ** And set the Expr.w.iJoin to iTable for every term in the ** expression. ** ** The EP_OuterON property is used on terms of an expression to tell ** the OUTER JOIN processing logic that this term is part of the ** join restriction specified in the ON or USING clause and not a part ** of the more general WHERE clause. These terms are moved over to the ** WHERE clause during join processing but we need to remember that they ** originated in the ON or USING clause. ** ** The Expr.w.iJoin tells the WHERE clause processing that the |
︙ | ︙ | |||
400 401 402 403 404 405 406 | ** term until after the t2 loop of the join. In that way, a ** NULL t2 row will be inserted whenever t1.x!=5. If we do not ** defer the handling of t1.x=5, it will be processed immediately ** after the t1 loop and rows with t1.x!=5 will never appear in ** the output, which is incorrect. */ void sqlite3SetJoinExpr(Expr *p, int iTable, u32 joinFlag){ | | | | | | | | 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 | ** term until after the t2 loop of the join. In that way, a ** NULL t2 row will be inserted whenever t1.x!=5. If we do not ** defer the handling of t1.x=5, it will be processed immediately ** after the t1 loop and rows with t1.x!=5 will never appear in ** the output, which is incorrect. */ void sqlite3SetJoinExpr(Expr *p, int iTable, u32 joinFlag){ assert( joinFlag==EP_OuterON || joinFlag==EP_InnerON ); while( p ){ ExprSetProperty(p, joinFlag); assert( !ExprHasProperty(p, EP_TokenOnly|EP_Reduced) ); ExprSetVVAProperty(p, EP_NoReduce); p->w.iJoin = iTable; if( p->op==TK_FUNCTION ){ assert( ExprUseXList(p) ); if( p->x.pList ){ int i; for(i=0; i<p->x.pList->nExpr; i++){ sqlite3SetJoinExpr(p->x.pList->a[i].pExpr, iTable, joinFlag); } } } sqlite3SetJoinExpr(p->pLeft, iTable, joinFlag); p = p->pRight; } } /* Undo the work of sqlite3SetJoinExpr(). In the expression p, convert every ** term that is marked with EP_OuterON and w.iJoin==iTable into ** an ordinary term that omits the EP_OuterON mark. ** ** This happens when a LEFT JOIN is simplified into an ordinary JOIN. */ static void unsetJoinExpr(Expr *p, int iTable){ while( p ){ if( ExprHasProperty(p, EP_OuterON) && (iTable<0 || p->w.iJoin==iTable) ){ ExprClearProperty(p, EP_OuterON); ExprSetProperty(p, EP_InnerON); } if( p->op==TK_COLUMN && p->iTable==iTable ){ ExprClearProperty(p, EP_CanBeNull); } if( p->op==TK_FUNCTION ){ assert( ExprUseXList(p) ); if( p->x.pList ){ |
︙ | ︙ | |||
459 460 461 462 463 464 465 | ** ** * A NATURAL join is converted into a USING join. After that, we ** do not need to be concerned with NATURAL joins and we only have ** think about USING joins. ** ** * ON and USING clauses result in extra terms being added to the ** WHERE clause to enforce the specified constraints. The extra | | | | 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 | ** ** * A NATURAL join is converted into a USING join. After that, we ** do not need to be concerned with NATURAL joins and we only have ** think about USING joins. ** ** * ON and USING clauses result in extra terms being added to the ** WHERE clause to enforce the specified constraints. The extra ** WHERE clause terms will be tagged with EP_OuterON or ** EP_InnerON so that we know that they originated in ON/USING. ** ** The terms of a FROM clause are contained in the Select.pSrc structure. ** The left most table is the first entry in Select.pSrc. The right-most ** table is the last entry. The join operator is held in the entry to ** the right. Thus entry 1 contains the join operator for the join between ** entries 0 and 1. Any ON or USING clauses associated with the join are ** also attached to the right entry. |
︙ | ︙ | |||
485 486 487 488 489 490 491 | pLeft = &pSrc->a[0]; pRight = &pLeft[1]; for(i=0; i<pSrc->nSrc-1; i++, pRight++, pLeft++){ Table *pRightTab = pRight->pTab; u32 joinType; if( NEVER(pLeft->pTab==0 || pRightTab==0) ) continue; | | | 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 | pLeft = &pSrc->a[0]; pRight = &pLeft[1]; for(i=0; i<pSrc->nSrc-1; i++, pRight++, pLeft++){ Table *pRightTab = pRight->pTab; u32 joinType; if( NEVER(pLeft->pTab==0 || pRightTab==0) ) continue; joinType = (pRight->fg.jointype & JT_OUTER)!=0 ? EP_OuterON : EP_InnerON; /* If this is a NATURAL join, synthesize an approprate USING clause ** to specify which columns should be joined. */ if( pRight->fg.jointype & JT_NATURAL ){ IdList *pUsing = 0; if( pRight->fg.isUsing || pRight->u3.pOn ){ |
︙ | ︙ | |||
2189 2190 2191 2192 2193 2194 2195 | Expr *pColExpr = sqlite3ExprSkipCollateAndLikely(pX->pExpr); while( ALWAYS(pColExpr!=0) && pColExpr->op==TK_DOT ){ pColExpr = pColExpr->pRight; assert( pColExpr!=0 ); } if( pColExpr->op==TK_COLUMN && ALWAYS( ExprUseYTab(pColExpr) ) | | > | 2189 2190 2191 2192 2193 2194 2195 2196 2197 2198 2199 2200 2201 2202 2203 2204 2205 2206 2207 | Expr *pColExpr = sqlite3ExprSkipCollateAndLikely(pX->pExpr); while( ALWAYS(pColExpr!=0) && pColExpr->op==TK_DOT ){ pColExpr = pColExpr->pRight; assert( pColExpr!=0 ); } if( pColExpr->op==TK_COLUMN && ALWAYS( ExprUseYTab(pColExpr) ) && ALWAYS( pColExpr->y.pTab!=0 ) ){ /* For columns use the column name name */ int iCol = pColExpr->iColumn; pTab = pColExpr->y.pTab; if( iCol<0 ) iCol = pTab->iPKey; zName = iCol>=0 ? pTab->aCol[iCol].zCnName : "rowid"; }else if( pColExpr->op==TK_ID ){ assert( !ExprHasProperty(pColExpr, EP_IntValue) ); zName = pColExpr->u.zToken; }else{ /* Use the original text of the column expression as its name */ |
︙ | ︙ | |||
3758 3759 3760 3761 3762 3763 3764 | ** of the subquery rather the result set of the subquery. */ static Expr *substExpr( SubstContext *pSubst, /* Description of the substitution */ Expr *pExpr /* Expr in which substitution occurs */ ){ if( pExpr==0 ) return 0; | | | 3759 3760 3761 3762 3763 3764 3765 3766 3767 3768 3769 3770 3771 3772 3773 | ** of the subquery rather the result set of the subquery. */ static Expr *substExpr( SubstContext *pSubst, /* Description of the substitution */ Expr *pExpr /* Expr in which substitution occurs */ ){ if( pExpr==0 ) return 0; if( ExprHasProperty(pExpr, EP_OuterON) && pExpr->w.iJoin==pSubst->iTable ){ pExpr->w.iJoin = pSubst->iNewTable; } if( pExpr->op==TK_COLUMN && pExpr->iTable==pSubst->iTable && !ExprHasProperty(pExpr, EP_FixedCol) |
︙ | ︙ | |||
3799 3800 3801 3802 3803 3804 3805 | if( db->mallocFailed ){ sqlite3ExprDelete(db, pNew); return pExpr; } if( pSubst->isOuterJoin ){ ExprSetProperty(pNew, EP_CanBeNull); } | | | | 3800 3801 3802 3803 3804 3805 3806 3807 3808 3809 3810 3811 3812 3813 3814 3815 3816 | if( db->mallocFailed ){ sqlite3ExprDelete(db, pNew); return pExpr; } if( pSubst->isOuterJoin ){ ExprSetProperty(pNew, EP_CanBeNull); } if( ExprHasProperty(pExpr,EP_OuterON|EP_InnerON) ){ sqlite3SetJoinExpr(pNew, pExpr->w.iJoin, pExpr->flags & (EP_OuterON|EP_InnerON)); } sqlite3ExprDelete(db, pExpr); pExpr = pNew; /* 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. */ if( pExpr->op!=TK_COLUMN && pExpr->op!=TK_COLLATE ){ |
︙ | ︙ | |||
3965 3966 3967 3968 3969 3970 3971 | ** Expr objects to match newly assigned cursor numbers. */ static int renumberCursorsCb(Walker *pWalker, Expr *pExpr){ int op = pExpr->op; if( op==TK_COLUMN || op==TK_IF_NULL_ROW ){ renumberCursorDoMapping(pWalker, &pExpr->iTable); } | | | 3966 3967 3968 3969 3970 3971 3972 3973 3974 3975 3976 3977 3978 3979 3980 | ** Expr objects to match newly assigned cursor numbers. */ static int renumberCursorsCb(Walker *pWalker, Expr *pExpr){ int op = pExpr->op; if( op==TK_COLUMN || op==TK_IF_NULL_ROW ){ renumberCursorDoMapping(pWalker, &pExpr->iTable); } if( ExprHasProperty(pExpr, EP_OuterON) ){ renumberCursorDoMapping(pWalker, &pExpr->w.iJoin); } return WRC_Continue; } /* ** Assign a new cursor number to each cursor in the FROM clause (Select.pSrc) |
︙ | ︙ | |||
4543 4544 4545 4546 4547 4548 4549 | assert( pParent->pOrderBy==0 ); pParent->pOrderBy = pOrderBy; pSub->pOrderBy = 0; } pWhere = pSub->pWhere; pSub->pWhere = 0; if( isOuterJoin>0 ){ | | | 4544 4545 4546 4547 4548 4549 4550 4551 4552 4553 4554 4555 4556 4557 4558 | assert( pParent->pOrderBy==0 ); pParent->pOrderBy = pOrderBy; pSub->pOrderBy = 0; } pWhere = pSub->pWhere; pSub->pWhere = 0; if( isOuterJoin>0 ){ sqlite3SetJoinExpr(pWhere, iNewParent, EP_OuterON); } if( pWhere ){ if( pParent->pWhere ){ pParent->pWhere = sqlite3PExpr(pParse, TK_AND, pWhere, pParent->pWhere); }else{ pParent->pWhere = pWhere; } |
︙ | ︙ | |||
4676 4677 4678 4679 4680 4681 4682 | ** is a constant expression and where the term must be true because it ** is part of the AND-connected terms of the expression. For each term ** found, add it to the pConst structure. */ static void findConstInWhere(WhereConst *pConst, Expr *pExpr){ Expr *pRight, *pLeft; if( NEVER(pExpr==0) ) return; | | | 4677 4678 4679 4680 4681 4682 4683 4684 4685 4686 4687 4688 4689 4690 4691 | ** is a constant expression and where the term must be true because it ** is part of the AND-connected terms of the expression. For each term ** found, add it to the pConst structure. */ static void findConstInWhere(WhereConst *pConst, Expr *pExpr){ Expr *pRight, *pLeft; if( NEVER(pExpr==0) ) return; if( ExprHasProperty(pExpr, EP_OuterON) ) return; if( pExpr->op==TK_AND ){ findConstInWhere(pConst, pExpr->pRight); findConstInWhere(pConst, pExpr->pLeft); return; } if( pExpr->op!=TK_EQ ) return; pRight = pExpr->pRight; |
︙ | ︙ | |||
4712 4713 4714 4715 4716 4717 4718 | WhereConst *pConst, Expr *pExpr, int bIgnoreAffBlob ){ int i; if( pConst->pOomFault[0] ) return WRC_Prune; if( pExpr->op!=TK_COLUMN ) return WRC_Continue; | | | | 4713 4714 4715 4716 4717 4718 4719 4720 4721 4722 4723 4724 4725 4726 4727 4728 4729 | WhereConst *pConst, Expr *pExpr, int bIgnoreAffBlob ){ int i; if( pConst->pOomFault[0] ) return WRC_Prune; if( pExpr->op!=TK_COLUMN ) return WRC_Continue; if( ExprHasProperty(pExpr, EP_FixedCol|EP_OuterON) ){ testcase( ExprHasProperty(pExpr, EP_FixedCol) ); testcase( ExprHasProperty(pExpr, EP_OuterON) ); return WRC_Continue; } for(i=0; i<pConst->nConst; i++){ Expr *pColumn = pConst->apExpr[i*2]; if( pColumn==pExpr ) continue; if( pColumn->iTable!=pExpr->iTable ) continue; if( pColumn->iColumn!=pExpr->iColumn ) continue; |
︙ | ︙ | |||
4999 5000 5001 5002 5003 5004 5005 | while( pWhere->op==TK_AND ){ nChng += pushDownWhereTerms(pParse, pSubq, pWhere->pRight, pSrc); pWhere = pWhere->pLeft; } #if 0 /* Legacy code. Checks now done by sqlite3ExprIsTableConstraint() */ if( isLeftJoin | | | | 5000 5001 5002 5003 5004 5005 5006 5007 5008 5009 5010 5011 5012 5013 5014 5015 5016 5017 5018 5019 | while( pWhere->op==TK_AND ){ nChng += pushDownWhereTerms(pParse, pSubq, pWhere->pRight, pSrc); pWhere = pWhere->pLeft; } #if 0 /* Legacy code. Checks now done by sqlite3ExprIsTableConstraint() */ if( isLeftJoin && (ExprHasProperty(pWhere,EP_OuterON)==0 || pWhere->w.iJoin!=iCursor) ){ return 0; /* restriction (4) */ } if( ExprHasProperty(pWhere,EP_OuterON) && pWhere->w.iJoin!=iCursor ){ return 0; /* restriction (5) */ } #endif if( sqlite3ExprIsTableConstraint(pWhere, pSrc) ){ |
︙ | ︙ | |||
6513 6514 6515 6516 6517 6518 6519 6520 6521 6522 6523 6524 6525 6526 | SELECTTRACE(0x400,pParse,p,("After count-of-view optimization:\n")); sqlite3TreeViewSelect(0, p, 0); } #endif return 1; } #endif /* SQLITE_COUNTOFVIEW_OPTIMIZATION */ /* ** Generate code for the SELECT statement given in the p argument. ** ** The results are returned according to the SelectDest structure. ** See comments in sqliteInt.h for further information. ** | > > > > > > > > > > > > > > > > > > > > > > > | 6514 6515 6516 6517 6518 6519 6520 6521 6522 6523 6524 6525 6526 6527 6528 6529 6530 6531 6532 6533 6534 6535 6536 6537 6538 6539 6540 6541 6542 6543 6544 6545 6546 6547 6548 6549 6550 | SELECTTRACE(0x400,pParse,p,("After count-of-view optimization:\n")); sqlite3TreeViewSelect(0, p, 0); } #endif return 1; } #endif /* SQLITE_COUNTOFVIEW_OPTIMIZATION */ /* ** If any term of pSrc, or any SF_NestedFrom sub-query, is not the same ** as pSrcItem but has the same alias as p0, then return true. ** Otherwise return false. */ static int sameSrcAlias(SrcItem *p0, SrcList *pSrc){ int i; for(i=0; i<pSrc->nSrc; i++){ SrcItem *p1 = &pSrc->a[i]; if( p1==p0 ) continue; if( p0->pTab==p1->pTab && 0==sqlite3_stricmp(p0->zAlias, p1->zAlias) ){ return 1; } if( p1->pSelect && (p1->pSelect->selFlags & SF_NestedFrom)!=0 && sameSrcAlias(p0, p1->pSelect->pSrc) ){ return 1; } } return 0; } /* ** Generate code for the SELECT statement given in the p argument. ** ** The results are returned according to the SelectDest structure. ** See comments in sqliteInt.h for further information. ** |
︙ | ︙ | |||
6618 6619 6620 6621 6622 6623 6624 | ** ** Postgres disallows this case too. The reason is that some other ** systems handle this case differently, and not all the same way, ** which is just confusing. To avoid this, we follow PG's lead and ** disallow it altogether. */ if( p->selFlags & SF_UFSrcCheck ){ SrcItem *p0 = &p->pSrc->a[0]; | | < < | | | | | < | 6642 6643 6644 6645 6646 6647 6648 6649 6650 6651 6652 6653 6654 6655 6656 6657 6658 6659 6660 6661 | ** ** Postgres disallows this case too. The reason is that some other ** systems handle this case differently, and not all the same way, ** which is just confusing. To avoid this, we follow PG's lead and ** disallow it altogether. */ if( p->selFlags & SF_UFSrcCheck ){ SrcItem *p0 = &p->pSrc->a[0]; if( sameSrcAlias(p0, p->pSrc) ){ sqlite3ErrorMsg(pParse, "target object/alias may not appear in FROM clause: %s", p0->zAlias ? p0->zAlias : p0->pTab->zName ); goto select_end; } /* Clear the SF_UFSrcCheck flag. The check has already been performed, ** and leaving this flag set can cause errors if a compound sub-query ** in p->pSrc is flattened into this query and this function called ** again as part of compound SELECT processing. */ p->selFlags &= ~SF_UFSrcCheck; |
︙ | ︙ | |||
6901 6902 6903 6904 6905 6906 6907 | } zSavedAuthContext = pParse->zAuthContext; pParse->zAuthContext = pItem->zName; /* Generate code to implement the subquery ** | | | 6922 6923 6924 6925 6926 6927 6928 6929 6930 6931 6932 6933 6934 6935 6936 | } zSavedAuthContext = pParse->zAuthContext; pParse->zAuthContext = pItem->zName; /* Generate code to implement the subquery ** ** The subquery is implemented as a co-routine all if the following are ** true: ** ** (1) the subquery is guaranteed to be the outer loop (so that ** it does not need to be computed more than once), and ** (2) the subquery is not a CTE that should be materialized ** (3) the subquery is not part of a left operand for a RIGHT JOIN */ |
︙ | ︙ | |||
6959 6960 6961 6962 6963 6964 6965 | pSub->nSelectRow = pPrior->pSelect->nSelectRow; }else{ /* Materialize the view. If the view is not correlated, generate a ** subroutine to do the materialization so that subsequent uses of ** the same view can reuse the materialization. */ int topAddr; int onceAddr = 0; | < | > | | | 6980 6981 6982 6983 6984 6985 6986 6987 6988 6989 6990 6991 6992 6993 6994 6995 6996 6997 6998 6999 7000 7001 7002 7003 7004 7005 7006 7007 7008 7009 7010 7011 7012 7013 7014 7015 | pSub->nSelectRow = pPrior->pSelect->nSelectRow; }else{ /* Materialize the view. If the view is not correlated, generate a ** subroutine to do the materialization so that subsequent uses of ** the same view can reuse the materialization. */ int topAddr; int onceAddr = 0; pItem->regReturn = ++pParse->nMem; topAddr = sqlite3VdbeAddOp0(v, OP_Goto); pItem->addrFillSub = topAddr+1; pItem->fg.isMaterialized = 1; if( pItem->fg.isCorrelated==0 ){ /* If the subquery is not correlated and if we are not inside of ** a trigger, then we only need to compute the value of the subquery ** once. */ onceAddr = sqlite3VdbeAddOp0(v, OP_Once); VdbeCoverage(v); VdbeComment((v, "materialize %!S", pItem)); }else{ VdbeNoopComment((v, "materialize %!S", pItem)); } sqlite3SelectDestInit(&dest, SRT_EphemTab, pItem->iCursor); ExplainQueryPlan((pParse, 1, "MATERIALIZE %!S", pItem)); sqlite3Select(pParse, pSub, &dest); pItem->pTab->nRowLogEst = pSub->nSelectRow; if( onceAddr ) sqlite3VdbeJumpHere(v, onceAddr); sqlite3VdbeAddOp2(v, OP_Return, pItem->regReturn, topAddr+1); VdbeComment((v, "end %!S", pItem)); sqlite3VdbeJumpHere(v, topAddr); sqlite3ClearTempRegCache(pParse); if( pItem->fg.isCte && pItem->fg.isCorrelated==0 ){ CteUse *pCteUse = pItem->u2.pCteUse; pCteUse->addrM9e = pItem->addrFillSub; pCteUse->regRtn = pItem->regReturn; pCteUse->iCur = pItem->iCursor; pCteUse->nRowEst = pSub->nSelectRow; |
︙ | ︙ | |||
7703 7704 7705 7706 7707 7708 7709 | if( pWInfo==0 ){ goto select_end; } SELECTTRACE(1,pParse,p,("WhereBegin returns\n")); eDist = sqlite3WhereIsDistinct(pWInfo); updateAccumulator(pParse, regAcc, pAggInfo, eDist); if( eDist!=WHERE_DISTINCT_NOOP ){ | | | 7724 7725 7726 7727 7728 7729 7730 7731 7732 7733 7734 7735 7736 7737 7738 | if( pWInfo==0 ){ goto select_end; } SELECTTRACE(1,pParse,p,("WhereBegin returns\n")); eDist = sqlite3WhereIsDistinct(pWInfo); updateAccumulator(pParse, regAcc, pAggInfo, eDist); if( eDist!=WHERE_DISTINCT_NOOP ){ struct AggInfo_func *pF = pAggInfo->aFunc; if( pF ){ fixDistinctOpenEph(pParse, eDist, pF->iDistinct, pF->iDistAddr); } } if( regAcc ) sqlite3VdbeAddOp2(v, OP_Integer, 1, regAcc); if( minMaxFlag ){ |
︙ | ︙ |
Changes to src/shell.c.in.
︙ | ︙ | |||
225 226 227 228 229 230 231 232 233 234 235 236 237 238 | _setmode(_fileno(file), _O_TEXT); } #else # define setBinaryMode(X,Y) # define setTextMode(X,Y) #endif /* True if the timer is enabled */ static int enableTimer = 0; /* Return the current wall-clock time */ static sqlite3_int64 timeOfDay(void){ static sqlite3_vfs *clockVfs = 0; | > > > > > > > > > > | 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 | _setmode(_fileno(file), _O_TEXT); } #else # define setBinaryMode(X,Y) # define setTextMode(X,Y) #endif /* ** When compiling with emcc (a.k.a. emscripten), we're building a ** WebAssembly (WASM) bundle and need to disable and rewire a few ** things. */ #ifdef __EMSCRIPTEN__ #define SQLITE_SHELL_WASM_MODE #else #undef SQLITE_SHELL_WASM_MODE #endif /* True if the timer is enabled */ static int enableTimer = 0; /* Return the current wall-clock time */ static sqlite3_int64 timeOfDay(void){ static sqlite3_vfs *clockVfs = 0; |
︙ | ︙ | |||
687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 | ** If zPrior is not NULL then it is a buffer from a prior call to this ** routine that can be reused. ** ** The result is stored in space obtained from malloc() and must either ** be freed by the caller or else passed back into this routine via the ** zPrior argument for reuse. */ static char *one_input_line(FILE *in, char *zPrior, int isContinuation){ char *zPrompt; char *zResult; if( in!=0 ){ zResult = local_getline(zPrior, in); }else{ zPrompt = isContinuation ? continuePrompt : mainPrompt; #if SHELL_USE_LOCAL_GETLINE printf("%s", zPrompt); fflush(stdout); zResult = local_getline(zPrior, stdin); #else free(zPrior); zResult = shell_readline(zPrompt); if( zResult && *zResult ) shell_add_history(zResult); #endif } return zResult; } | > | | 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 | ** If zPrior is not NULL then it is a buffer from a prior call to this ** routine that can be reused. ** ** The result is stored in space obtained from malloc() and must either ** be freed by the caller or else passed back into this routine via the ** zPrior argument for reuse. */ #ifndef SQLITE_SHELL_WASM_MODE static char *one_input_line(FILE *in, char *zPrior, int isContinuation){ char *zPrompt; char *zResult; if( in!=0 ){ zResult = local_getline(zPrior, in); }else{ zPrompt = isContinuation ? continuePrompt : mainPrompt; #if SHELL_USE_LOCAL_GETLINE printf("%s", zPrompt); fflush(stdout); zResult = local_getline(zPrior, stdin); #else free(zPrior); zResult = shell_readline(zPrompt); if( zResult && *zResult ) shell_add_history(zResult); #endif } return zResult; } #endif /* !SQLITE_SHELL_WASM_MODE */ /* ** Return the value of a hexadecimal digit. Return -1 if the input ** is not a hex digit. */ static int hexDigitValue(char c){ if( c>='0' && c<='9' ) return c - '0'; |
︙ | ︙ | |||
794 795 796 797 798 799 800 | ** from malloc(), or a NULL pointer. The string pointed to by zAppend is ** added to zIn, and the result returned in memory obtained from malloc(). ** zIn, if it was not NULL, is freed. ** ** If the third argument, quote, is not '\0', then it is used as a ** quote character for zAppend. */ | | | 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 | ** from malloc(), or a NULL pointer. The string pointed to by zAppend is ** added to zIn, and the result returned in memory obtained from malloc(). ** zIn, if it was not NULL, is freed. ** ** If the third argument, quote, is not '\0', then it is used as a ** quote character for zAppend. */ static void appendText(ShellText *p, const char *zAppend, char quote){ int len; int i; int nAppend = strlen30(zAppend); len = nAppend+p->n+1; if( quote ){ len += 2; |
︙ | ︙ | |||
1005 1006 1007 1008 1009 1010 1011 | #define SQLITE_EXTENSION_INIT2(X) (void)(X) #if defined(_WIN32) && defined(_MSC_VER) INCLUDE test_windirent.h INCLUDE test_windirent.c #define dirent DIRENT #endif | | | < < < > > > > > | 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 | #define SQLITE_EXTENSION_INIT2(X) (void)(X) #if defined(_WIN32) && defined(_MSC_VER) INCLUDE test_windirent.h INCLUDE test_windirent.c #define dirent DIRENT #endif INCLUDE ../ext/misc/memtrace.c INCLUDE ../ext/misc/shathree.c INCLUDE ../ext/misc/uint.c INCLUDE ../ext/misc/decimal.c INCLUDE ../ext/misc/ieee754.c INCLUDE ../ext/misc/series.c INCLUDE ../ext/misc/regexp.c #ifndef SQLITE_SHELL_WASM_MODE INCLUDE ../ext/misc/fileio.c INCLUDE ../ext/misc/completion.c INCLUDE ../ext/misc/appendvfs.c #endif #ifdef SQLITE_HAVE_ZLIB INCLUDE ../ext/misc/zipfile.c INCLUDE ../ext/misc/sqlar.c #endif INCLUDE ../ext/expert/sqlite3expert.h INCLUDE ../ext/expert/sqlite3expert.c |
︙ | ︙ | |||
1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 | *pAuxDb; /* Currently active database connection */ int *aiIndent; /* Array of indents used in MODE_Explain */ int nIndent; /* Size of array aiIndent[] */ int iIndent; /* Index of current op in aiIndent[] */ char *zNonce; /* Nonce for temporary safe-mode excapes */ EQPGraph sGraph; /* Information for the graphical EXPLAIN QUERY PLAN */ ExpertInfo expert; /* Valid if previous command was ".expert OPT..." */ }; /* Allowed values for ShellState.autoEQP */ #define AUTOEQP_off 0 /* Automatic EXPLAIN QUERY PLAN is off */ #define AUTOEQP_on 1 /* Automatic EQP is on */ #define AUTOEQP_trigger 2 /* On and also show plans for triggers */ | > > > > > > > > > > | 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 | *pAuxDb; /* Currently active database connection */ int *aiIndent; /* Array of indents used in MODE_Explain */ int nIndent; /* Size of array aiIndent[] */ int iIndent; /* Index of current op in aiIndent[] */ char *zNonce; /* Nonce for temporary safe-mode excapes */ EQPGraph sGraph; /* Information for the graphical EXPLAIN QUERY PLAN */ ExpertInfo expert; /* Valid if previous command was ".expert OPT..." */ #ifdef SQLITE_SHELL_WASM_MODE struct { const char * zInput; /* Input string from wasm/JS proxy */ const char * zPos; /* Cursor pos into zInput */ } wasm; #endif }; #ifdef SQLITE_SHELL_WASM_MODE static ShellState shellState; #endif /* Allowed values for ShellState.autoEQP */ #define AUTOEQP_off 0 /* Automatic EXPLAIN QUERY PLAN is off */ #define AUTOEQP_on 1 /* Automatic EQP is on */ #define AUTOEQP_trigger 2 /* On and also show plans for triggers */ |
︙ | ︙ | |||
1187 1188 1189 1190 1191 1192 1193 | */ #define SHFLG_Pagecache 0x00000001 /* The --pagecache option is used */ #define SHFLG_Lookaside 0x00000002 /* Lookaside memory is used */ #define SHFLG_Backslash 0x00000004 /* The --backslash option is used */ #define SHFLG_PreserveRowid 0x00000008 /* .dump preserves rowid values */ #define SHFLG_Newlines 0x00000010 /* .dump --newline flag */ #define SHFLG_CountChanges 0x00000020 /* .changes setting */ | | | 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 | */ #define SHFLG_Pagecache 0x00000001 /* The --pagecache option is used */ #define SHFLG_Lookaside 0x00000002 /* Lookaside memory is used */ #define SHFLG_Backslash 0x00000004 /* The --backslash option is used */ #define SHFLG_PreserveRowid 0x00000008 /* .dump preserves rowid values */ #define SHFLG_Newlines 0x00000010 /* .dump --newline flag */ #define SHFLG_CountChanges 0x00000020 /* .changes setting */ #define SHFLG_Echo 0x00000040 /* .echo on/off, or --echo setting */ #define SHFLG_HeaderSet 0x00000080 /* showHeader has been specified */ #define SHFLG_DumpDataOnly 0x00000100 /* .dump show data only */ #define SHFLG_DumpNoSys 0x00000200 /* .dump omits system tables */ /* ** Macros for testing and setting shellFlgs */ |
︙ | ︙ | |||
3816 3817 3818 3819 3820 3821 3822 | /* save off the prepared statment handle and reset row count */ if( pArg ){ pArg->pStmt = pStmt; pArg->cnt = 0; } | < < < < < | 3839 3840 3841 3842 3843 3844 3845 3846 3847 3848 3849 3850 3851 3852 | /* save off the prepared statment handle and reset row count */ if( pArg ){ pArg->pStmt = pStmt; pArg->cnt = 0; } /* Show the EXPLAIN QUERY PLAN if .eqp is on */ if( pArg && pArg->autoEQP && sqlite3_stmt_isexplain(pStmt)==0 ){ sqlite3_stmt *pExplain; char *zEQP; int triggerEQP = 0; disable_debug_trace_modes(); sqlite3_db_config(db, SQLITE_DBCONFIG_TRIGGER_EQP, -1, &triggerEQP); |
︙ | ︙ | |||
4219 4220 4221 4222 4223 4224 4225 | return rc; } /* ** Text of help messages. ** ** The help text for each individual command begins with a line that starts | | | > | 4237 4238 4239 4240 4241 4242 4243 4244 4245 4246 4247 4248 4249 4250 4251 4252 4253 4254 4255 4256 4257 4258 | return rc; } /* ** Text of help messages. ** ** The help text for each individual command begins with a line that starts ** with ".". Subsequent lines are supplemental information. ** ** There must be two or more spaces between the end of the command and the ** start of the description of what that command does. */ static const char *(azHelp[]) = { #if defined(SQLITE_HAVE_ZLIB) && !defined(SQLITE_OMIT_VIRTUALTABLE) \ && !defined(SQLITE_SHELL_WASM_MODE) ".archive ... Manage SQL archives", " Each command must have exactly one of the following options:", " -c, --create Create a new archive", " -u, --update Add or update files with changed mtime", " -i, --insert Like -u but always add even if unchanged", " -r, --remove Remove files from archive", " -t, --list List contents of archive", |
︙ | ︙ | |||
4251 4252 4253 4254 4255 4256 4257 4258 4259 4260 4261 4262 4263 4264 4265 4266 4267 4268 4269 4270 4271 4272 4273 4274 4275 4276 4277 4278 4279 4280 4281 4282 4283 4284 4285 4286 4287 4288 4289 4290 4291 4292 4293 4294 4295 4296 4297 4298 4299 4300 4301 4302 4303 4304 4305 4306 4307 4308 4309 4310 4311 4312 4313 4314 4315 4316 4317 4318 4319 4320 4321 4322 4323 4324 4325 | " .ar -xvf ARCHIVE # Verbosely extract files from ARCHIVE", " See also:", " http://sqlite.org/cli.html#sqlite_archive_support", #endif #ifndef SQLITE_OMIT_AUTHORIZATION ".auth ON|OFF Show authorizer callbacks", #endif ".backup ?DB? FILE Backup DB (default \"main\") to FILE", " Options:", " --append Use the appendvfs", " --async Write to FILE without journal and fsync()", ".bail on|off Stop after hitting an error. Default OFF", ".binary on|off Turn binary output on or off. Default OFF", ".cd DIRECTORY Change the working directory to DIRECTORY", ".changes on|off Show number of rows changed by SQL", ".check GLOB Fail if output since .testcase does not match", ".clone NEWDB Clone data into NEWDB from the existing database", ".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", ".dbinfo ?DB? Show status information about the database", ".dump ?OBJECTS? Render database content as SQL", " Options:", " --data-only Output only INSERT statements", " --newlines Allow unescaped newline characters in output", " --nosys Omit system tables (ex: \"sqlite_stat1\")", " --preserve-rowids Include ROWID values in the output", " OBJECTS is a LIKE pattern for tables, indexes, triggers or views to dump", " Additional LIKE patterns can be given in subsequent arguments", ".echo on|off Turn command echo on or off", ".eqp on|off|full|... Enable or disable automatic EXPLAIN QUERY PLAN", " Other Modes:", #ifdef SQLITE_DEBUG " test Show raw EXPLAIN QUERY PLAN output", " trace Like \"full\" but enable \"PRAGMA vdbe_trace\"", #endif " trigger Like \"full\" but also show trigger bytecode", ".excel Display the output of next command in spreadsheet", " --bom Put a UTF8 byte-order mark on intermediate file", ".exit ?CODE? Exit this program with return-code CODE", ".expert EXPERIMENTAL. Suggest indexes for queries", ".explain ?on|off|auto? Change the EXPLAIN formatting mode. Default: auto", ".filectrl CMD ... Run various sqlite3_file_control() operations", " --schema SCHEMA Use SCHEMA instead of \"main\"", " --help Show CMD details", ".fullschema ?--indent? Show schema and the content of sqlite_stat tables", ".headers on|off Turn display of headers on or off", ".help ?-all? ?PATTERN? Show help text for PATTERN", ".import FILE TABLE Import data from FILE into TABLE", " Options:", " --ascii Use \\037 and \\036 as column and row separators", " --csv Use , and \\n as column and row separators", " --skip N Skip the first N rows of input", " --schema S Target table to be S.TABLE", " -v \"Verbose\" - increase auxiliary output", " Notes:", " * If TABLE does not exist, it is created. The first row of input", " determines the column names.", " * If neither --csv or --ascii are used, the input mode is derived", " from the \".mode\" output mode", " * If FILE begins with \"|\" then it is a command that generates the", " input text.", #ifndef SQLITE_OMIT_TEST_CONTROL ".imposter INDEX TABLE Create imposter table TABLE on index INDEX", #endif ".indexes ?TABLE? Show names of indexes", " If TABLE is specified, only show indexes for", " tables matching TABLE using the LIKE operator.", #ifdef SQLITE_ENABLE_IOTRACE ".iotrace FILE Enable I/O diagnostic logging to FILE", #endif ".limit ?LIMIT? ?VAL? Display or change the value of an SQLITE_LIMIT", ".lint OPTIONS Report potential schema issues.", " Options:", " fkey-indexes Find missing foreign key indexes", | > > > > > > > > > > > > | > > | 4270 4271 4272 4273 4274 4275 4276 4277 4278 4279 4280 4281 4282 4283 4284 4285 4286 4287 4288 4289 4290 4291 4292 4293 4294 4295 4296 4297 4298 4299 4300 4301 4302 4303 4304 4305 4306 4307 4308 4309 4310 4311 4312 4313 4314 4315 4316 4317 4318 4319 4320 4321 4322 4323 4324 4325 4326 4327 4328 4329 4330 4331 4332 4333 4334 4335 4336 4337 4338 4339 4340 4341 4342 4343 4344 4345 4346 4347 4348 4349 4350 4351 4352 4353 4354 4355 4356 4357 4358 4359 4360 4361 4362 4363 4364 4365 4366 4367 4368 4369 | " .ar -xvf ARCHIVE # Verbosely extract files from ARCHIVE", " See also:", " http://sqlite.org/cli.html#sqlite_archive_support", #endif #ifndef SQLITE_OMIT_AUTHORIZATION ".auth ON|OFF Show authorizer callbacks", #endif #ifndef SQLITE_SHELL_WASM_MODE ".backup ?DB? FILE Backup DB (default \"main\") to FILE", " Options:", " --append Use the appendvfs", " --async Write to FILE without journal and fsync()", #endif ".bail on|off Stop after hitting an error. Default OFF", ".binary on|off Turn binary output on or off. Default OFF", #ifndef SQLITE_SHELL_WASM_MODE ".cd DIRECTORY Change the working directory to DIRECTORY", #endif ".changes on|off Show number of rows changed by SQL", #ifndef SQLITE_SHELL_WASM_MODE ".check GLOB Fail if output since .testcase does not match", ".clone NEWDB Clone data into NEWDB from the existing database", #endif ".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", ".dbinfo ?DB? Show status information about the database", ".dump ?OBJECTS? Render database content as SQL", " Options:", " --data-only Output only INSERT statements", " --newlines Allow unescaped newline characters in output", " --nosys Omit system tables (ex: \"sqlite_stat1\")", " --preserve-rowids Include ROWID values in the output", " OBJECTS is a LIKE pattern for tables, indexes, triggers or views to dump", " Additional LIKE patterns can be given in subsequent arguments", ".echo on|off Turn command echo on or off", ".eqp on|off|full|... Enable or disable automatic EXPLAIN QUERY PLAN", " Other Modes:", #ifdef SQLITE_DEBUG " test Show raw EXPLAIN QUERY PLAN output", " trace Like \"full\" but enable \"PRAGMA vdbe_trace\"", #endif " trigger Like \"full\" but also show trigger bytecode", #ifndef SQLITE_SHELL_WASM_MODE ".excel Display the output of next command in spreadsheet", " --bom Put a UTF8 byte-order mark on intermediate file", #endif #ifndef SQLITE_SHELL_WASM_MODE ".exit ?CODE? Exit this program with return-code CODE", #endif ".expert EXPERIMENTAL. Suggest indexes for queries", ".explain ?on|off|auto? Change the EXPLAIN formatting mode. Default: auto", ".filectrl CMD ... Run various sqlite3_file_control() operations", " --schema SCHEMA Use SCHEMA instead of \"main\"", " --help Show CMD details", ".fullschema ?--indent? Show schema and the content of sqlite_stat tables", ".headers on|off Turn display of headers on or off", ".help ?-all? ?PATTERN? Show help text for PATTERN", #ifndef SQLITE_SHELL_WASM_MODE ".import FILE TABLE Import data from FILE into TABLE", " Options:", " --ascii Use \\037 and \\036 as column and row separators", " --csv Use , and \\n as column and row separators", " --skip N Skip the first N rows of input", " --schema S Target table to be S.TABLE", " -v \"Verbose\" - increase auxiliary output", " Notes:", " * If TABLE does not exist, it is created. The first row of input", " determines the column names.", " * If neither --csv or --ascii are used, the input mode is derived", " from the \".mode\" output mode", " * If FILE begins with \"|\" then it is a command that generates the", " input text.", #endif #ifndef SQLITE_OMIT_TEST_CONTROL ".imposter INDEX TABLE Create imposter table TABLE on index INDEX", #endif ".indexes ?TABLE? Show names of indexes", " If TABLE is specified, only show indexes for", " tables matching TABLE using the LIKE operator.", #ifdef SQLITE_ENABLE_IOTRACE ".iotrace FILE Enable I/O diagnostic logging to FILE", #endif ".limit ?LIMIT? ?VAL? Display or change the value of an SQLITE_LIMIT", ".lint OPTIONS Report potential schema issues.", " Options:", " fkey-indexes Find missing foreign key indexes", #if !defined(SQLITE_OMIT_LOAD_EXTENSION) && !defined(SQLITE_SHELL_WASM_MODE) ".load FILE ?ENTRY? Load an extension library", #endif #ifndef SQLITE_SHELL_WASM_MODE ".log FILE|off Turn logging on or off. FILE can be stderr/stdout", #endif ".mode MODE ?OPTIONS? Set output mode", " MODE is one of:", " ascii Columns/rows delimited by 0x1F and 0x1E", " box Tables using unicode box-drawing characters", " csv Comma-separated values", " column Output in columns. (See .width)", " html HTML <table> code", |
︙ | ︙ | |||
4347 4348 4349 4350 4351 4352 4353 4354 4355 4356 4357 4358 4359 4360 4361 4362 4363 4364 4365 4366 4367 4368 4369 4370 4371 4372 4373 4374 4375 4376 4377 4378 4379 4380 4381 4382 4383 4384 4385 4386 4387 4388 4389 4390 4391 4392 4393 4394 4395 4396 4397 4398 4399 4400 4401 4402 4403 4404 4405 4406 4407 4408 4409 4410 4411 4412 4413 4414 | " OPTIONS: (for columnar modes or insert mode):", " --wrap N Wrap output lines to no longer than N characters", " --wordwrap B Wrap or not at word boundaries per B (on/off)", " --ww Shorthand for \"--wordwrap 1\"", " --quote Quote output text as SQL literals", " --noquote Do not quote output text", " TABLE The name of SQL table used for \"insert\" mode", ".nonce STRING Suspend safe mode for one command if nonce matches", ".nullvalue STRING Use STRING in place of NULL values", ".once ?OPTIONS? ?FILE? Output for the next SQL command only to FILE", " If FILE begins with '|' then open as a pipe", " --bom Put a UTF8 byte-order mark at the beginning", " -e Send output to the system text editor", " -x Send output as CSV to a spreadsheet (same as \".excel\")", ".open ?OPTIONS? ?FILE? Close existing database and reopen FILE", " Options:", " --append Use appendvfs to append database to the end of FILE", #ifndef SQLITE_OMIT_DESERIALIZE " --deserialize Load into memory using sqlite3_deserialize()", " --hexdb Load the output of \"dbtotxt\" as an in-memory db", " --maxsize N Maximum size for --hexdb or --deserialized database", #endif " --new Initialize FILE to an empty database", " --nofollow Do not follow symbolic links", " --readonly Open FILE readonly", " --zip FILE is a ZIP archive", ".output ?FILE? Send output to FILE or stdout if FILE is omitted", " If FILE begins with '|' then open it as a pipe.", " Options:", " --bom Prefix output with a UTF8 byte-order mark", " -e Send output to the system text editor", " -x Send output as CSV to a spreadsheet", ".parameter CMD ... Manage SQL parameter bindings", " clear Erase all bindings", " init Initialize the TEMP table that holds bindings", " list List the current parameter bindings", " set PARAMETER VALUE Given SQL parameter PARAMETER a value of VALUE", " PARAMETER should start with one of: $ : @ ?", " unset PARAMETER Remove PARAMETER from the binding table", ".print STRING... Print literal STRING", #ifndef SQLITE_OMIT_PROGRESS_CALLBACK ".progress N Invoke progress handler after every N opcodes", " --limit N Interrupt after N progress callbacks", " --once Do no more than one progress interrupt", " --quiet|-q No output except at interrupts", " --reset Reset the count for each input and interrupt", #endif ".prompt MAIN CONTINUE Replace the standard prompts", ".quit Exit this program", ".read FILE Read input from FILE or command output", " If FILE begins with \"|\", it is a command that generates the input.", #if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_ENABLE_DBPAGE_VTAB) ".recover Recover as much data as possible from corrupt db.", " --freelist-corrupt Assume the freelist is corrupt", " --recovery-db NAME Store recovery metadata in database file NAME", " --lost-and-found TABLE Alternative name for the lost-and-found table", " --no-rowids Do not attempt to recover rowid values", " that are not also INTEGER PRIMARY KEYs", #endif ".restore ?DB? FILE Restore content of DB (default \"main\") from FILE", ".save ?OPTIONS? FILE Write database to FILE (an alias for .backup ...)", ".scanstats on|off Turn sqlite3_stmt_scanstatus() metrics on or off", ".schema ?PATTERN? Show the CREATE statements matching PATTERN", " Options:", " --indent Try to pretty-print the schema", " --nosys Omit objects whose names start with \"sqlite_\"", ".selftest ?OPTIONS? Run tests defined in the SELFTEST table", " Options:", | > > > > > > > > > > > > > | 4380 4381 4382 4383 4384 4385 4386 4387 4388 4389 4390 4391 4392 4393 4394 4395 4396 4397 4398 4399 4400 4401 4402 4403 4404 4405 4406 4407 4408 4409 4410 4411 4412 4413 4414 4415 4416 4417 4418 4419 4420 4421 4422 4423 4424 4425 4426 4427 4428 4429 4430 4431 4432 4433 4434 4435 4436 4437 4438 4439 4440 4441 4442 4443 4444 4445 4446 4447 4448 4449 4450 4451 4452 4453 4454 4455 4456 4457 4458 4459 4460 | " OPTIONS: (for columnar modes or insert mode):", " --wrap N Wrap output lines to no longer than N characters", " --wordwrap B Wrap or not at word boundaries per B (on/off)", " --ww Shorthand for \"--wordwrap 1\"", " --quote Quote output text as SQL literals", " --noquote Do not quote output text", " TABLE The name of SQL table used for \"insert\" mode", #ifndef SQLITE_SHELL_WASM_MODE ".nonce STRING Suspend safe mode for one command if nonce matches", #endif ".nullvalue STRING Use STRING in place of NULL values", #ifndef SQLITE_SHELL_WASM_MODE ".once ?OPTIONS? ?FILE? Output for the next SQL command only to FILE", " If FILE begins with '|' then open as a pipe", " --bom Put a UTF8 byte-order mark at the beginning", " -e Send output to the system text editor", " -x Send output as CSV to a spreadsheet (same as \".excel\")", /* Note that .open is (partially) available in WASM builds but is ** currently only intended to be used by the fiddle tool, not ** end users, so is "undocumented." */ ".open ?OPTIONS? ?FILE? Close existing database and reopen FILE", " Options:", " --append Use appendvfs to append database to the end of FILE", #endif #ifndef SQLITE_OMIT_DESERIALIZE " --deserialize Load into memory using sqlite3_deserialize()", " --hexdb Load the output of \"dbtotxt\" as an in-memory db", " --maxsize N Maximum size for --hexdb or --deserialized database", #endif " --new Initialize FILE to an empty database", " --nofollow Do not follow symbolic links", " --readonly Open FILE readonly", " --zip FILE is a ZIP archive", #ifndef SQLITE_SHELL_WASM_MODE ".output ?FILE? Send output to FILE or stdout if FILE is omitted", " If FILE begins with '|' then open it as a pipe.", " Options:", " --bom Prefix output with a UTF8 byte-order mark", " -e Send output to the system text editor", " -x Send output as CSV to a spreadsheet", #endif ".parameter CMD ... Manage SQL parameter bindings", " clear Erase all bindings", " init Initialize the TEMP table that holds bindings", " list List the current parameter bindings", " set PARAMETER VALUE Given SQL parameter PARAMETER a value of VALUE", " PARAMETER should start with one of: $ : @ ?", " unset PARAMETER Remove PARAMETER from the binding table", ".print STRING... Print literal STRING", #ifndef SQLITE_OMIT_PROGRESS_CALLBACK ".progress N Invoke progress handler after every N opcodes", " --limit N Interrupt after N progress callbacks", " --once Do no more than one progress interrupt", " --quiet|-q No output except at interrupts", " --reset Reset the count for each input and interrupt", #endif ".prompt MAIN CONTINUE Replace the standard prompts", #ifndef SQLITE_SHELL_WASM_MODE ".quit Exit this program", ".read FILE Read input from FILE or command output", " If FILE begins with \"|\", it is a command that generates the input.", #endif #if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_ENABLE_DBPAGE_VTAB) ".recover Recover as much data as possible from corrupt db.", " --freelist-corrupt Assume the freelist is corrupt", " --recovery-db NAME Store recovery metadata in database file NAME", " --lost-and-found TABLE Alternative name for the lost-and-found table", " --no-rowids Do not attempt to recover rowid values", " that are not also INTEGER PRIMARY KEYs", #endif #ifndef SQLITE_SHELL_WASM_MODE ".restore ?DB? FILE Restore content of DB (default \"main\") from FILE", ".save ?OPTIONS? FILE Write database to FILE (an alias for .backup ...)", #endif ".scanstats on|off Turn sqlite3_stmt_scanstatus() metrics on or off", ".schema ?PATTERN? Show the CREATE statements matching PATTERN", " Options:", " --indent Try to pretty-print the schema", " --nosys Omit objects whose names start with \"sqlite_\"", ".selftest ?OPTIONS? Run tests defined in the SELFTEST table", " Options:", |
︙ | ︙ | |||
4434 4435 4436 4437 4438 4439 4440 | " Options:", " --schema Also hash the sqlite_schema table", " --sha3-224 Use the sha3-224 algorithm", " --sha3-256 Use the sha3-256 algorithm (default)", " --sha3-384 Use the sha3-384 algorithm", " --sha3-512 Use the sha3-512 algorithm", " Any other argument is a LIKE pattern for tables to hash", | | | > > | 4480 4481 4482 4483 4484 4485 4486 4487 4488 4489 4490 4491 4492 4493 4494 4495 4496 4497 4498 4499 4500 4501 4502 4503 4504 4505 4506 4507 4508 4509 | " Options:", " --schema Also hash the sqlite_schema table", " --sha3-224 Use the sha3-224 algorithm", " --sha3-256 Use the sha3-256 algorithm (default)", " --sha3-384 Use the sha3-384 algorithm", " --sha3-512 Use the sha3-512 algorithm", " Any other argument is a LIKE pattern for tables to hash", #if !defined(SQLITE_NOHAVE_SYSTEM) && !defined(SQLITE_SHELL_WASM_MODE) ".shell CMD ARGS... Run CMD ARGS... in a system shell", #endif ".show Show the current values for various settings", ".stats ?ARG? Show stats or turn stats on or off", " off Turn off automatic stat display", " on Turn on automatic stat display", " stmt Show statement stats", " vmstep Show the virtual machine step count only", #if !defined(SQLITE_NOHAVE_SYSTEM) && !defined(SQLITE_SHELL_WASM_MODE) ".system CMD ARGS... Run CMD ARGS... in a system shell", #endif ".tables ?TABLE? List names of tables matching LIKE pattern TABLE", #ifndef SQLITE_SHELL_WASM_MODE ".testcase NAME Begin redirecting output to 'testcase-out.txt'", #endif ".testctrl CMD ... Run various sqlite3_test_control() operations", " Run \".testctrl\" with no arguments for details", ".timeout MS Try opening locked tables for MS milliseconds", ".timer on|off Turn SQL timer on or off", #ifndef SQLITE_OMIT_TRACE ".trace ?OPTIONS? Output each SQL statement as it is run", " FILE Send output to FILE", |
︙ | ︙ | |||
4992 4993 4994 4995 4996 4997 4998 | return; } exit(1); } #ifndef SQLITE_OMIT_LOAD_EXTENSION sqlite3_enable_load_extension(p->db, 1); #endif | < < > > > > | 5040 5041 5042 5043 5044 5045 5046 5047 5048 5049 5050 5051 5052 5053 5054 5055 5056 5057 5058 5059 5060 5061 5062 5063 | return; } exit(1); } #ifndef SQLITE_OMIT_LOAD_EXTENSION sqlite3_enable_load_extension(p->db, 1); #endif sqlite3_shathree_init(p->db, 0, 0); sqlite3_uint_init(p->db, 0, 0); sqlite3_decimal_init(p->db, 0, 0); sqlite3_regexp_init(p->db, 0, 0); sqlite3_ieee_init(p->db, 0, 0); sqlite3_series_init(p->db, 0, 0); #ifndef SQLITE_SHELL_WASM_MODE sqlite3_fileio_init(p->db, 0, 0); sqlite3_completion_init(p->db, 0, 0); #endif #if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_ENABLE_DBPAGE_VTAB) sqlite3_dbdata_init(p->db, 0, 0); #endif #ifdef SQLITE_HAVE_ZLIB if( !p->bSafeModePersist ){ sqlite3_zipfile_init(p->db, 0, 0); sqlite3_sqlar_init(p->db, 0, 0); |
︙ | ︙ | |||
8122 8123 8124 8125 8126 8127 8128 | sqlite3_set_authorizer(p->db, safeModeAuth, p); }else{ sqlite3_set_authorizer(p->db, 0, 0); } }else #endif | | > > | 8172 8173 8174 8175 8176 8177 8178 8179 8180 8181 8182 8183 8184 8185 8186 8187 8188 8189 8190 8191 8192 8193 8194 8195 | sqlite3_set_authorizer(p->db, safeModeAuth, p); }else{ sqlite3_set_authorizer(p->db, 0, 0); } }else #endif #if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_HAVE_ZLIB) \ && !defined(SQLITE_SHELL_WASM_MODE) if( c=='a' && strncmp(azArg[0], "archive", n)==0 ){ open_db(p, 0); failIfSafeMode(p, "cannot run .archive in safe mode"); rc = arDotCommand(p, 0, azArg, nArg); }else #endif #ifndef SQLITE_SHELL_WASM_MODE if( (c=='b' && n>=3 && strncmp(azArg[0], "backup", n)==0) || (c=='s' && n>=3 && strncmp(azArg[0], "save", n)==0) ){ const char *zDestFile = 0; const char *zDb = 0; sqlite3 *pDest; sqlite3_backup *pBackup; |
︙ | ︙ | |||
8198 8199 8200 8201 8202 8203 8204 8205 8206 8207 8208 8209 8210 8211 | rc = 0; }else{ utf8_printf(stderr, "Error: %s\n", sqlite3_errmsg(pDest)); rc = 1; } close_db(pDest); }else if( c=='b' && n>=3 && strncmp(azArg[0], "bail", n)==0 ){ if( nArg==2 ){ bail_on_error = booleanValue(azArg[1]); }else{ raw_printf(stderr, "Usage: .bail on|off\n"); rc = 1; | > | 8250 8251 8252 8253 8254 8255 8256 8257 8258 8259 8260 8261 8262 8263 8264 | rc = 0; }else{ utf8_printf(stderr, "Error: %s\n", sqlite3_errmsg(pDest)); rc = 1; } close_db(pDest); }else #endif /* !defined(SQLITE_SHELL_WASM_MODE) */ if( c=='b' && n>=3 && strncmp(azArg[0], "bail", n)==0 ){ if( nArg==2 ){ bail_on_error = booleanValue(azArg[1]); }else{ raw_printf(stderr, "Usage: .bail on|off\n"); rc = 1; |
︙ | ︙ | |||
8228 8229 8230 8231 8232 8233 8234 8235 8236 8237 8238 8239 8240 8241 8242 8243 8244 8245 8246 8247 8248 8249 8250 8251 8252 8253 8254 8255 8256 8257 8258 8259 8260 8261 8262 8263 8264 8265 8266 8267 8268 8269 8270 | /* The undocumented ".breakpoint" command causes a call to the no-op ** routine named test_breakpoint(). */ if( c=='b' && n>=3 && strncmp(azArg[0], "breakpoint", n)==0 ){ test_breakpoint(); }else if( c=='c' && strcmp(azArg[0],"cd")==0 ){ failIfSafeMode(p, "cannot run .cd in safe mode"); if( nArg==2 ){ #if defined(_WIN32) || defined(WIN32) wchar_t *z = sqlite3_win32_utf8_to_unicode(azArg[1]); rc = !SetCurrentDirectoryW(z); sqlite3_free(z); #else rc = chdir(azArg[1]); #endif if( rc ){ utf8_printf(stderr, "Cannot change to directory \"%s\"\n", azArg[1]); rc = 1; } }else{ raw_printf(stderr, "Usage: .cd DIRECTORY\n"); rc = 1; } }else if( c=='c' && n>=3 && strncmp(azArg[0], "changes", n)==0 ){ if( nArg==2 ){ setOrClearFlag(p, SHFLG_CountChanges, azArg[1]); }else{ raw_printf(stderr, "Usage: .changes on|off\n"); rc = 1; } }else /* Cancel output redirection, if it is currently set (by .testcase) ** Then read the content of the testcase-out.txt file and compare against ** azArg[1]. If there are differences, report an error and exit. */ if( c=='c' && n>=3 && strncmp(azArg[0], "check", n)==0 ){ char *zRes = 0; output_reset(p); | > > > | 8281 8282 8283 8284 8285 8286 8287 8288 8289 8290 8291 8292 8293 8294 8295 8296 8297 8298 8299 8300 8301 8302 8303 8304 8305 8306 8307 8308 8309 8310 8311 8312 8313 8314 8315 8316 8317 8318 8319 8320 8321 8322 8323 8324 8325 8326 | /* The undocumented ".breakpoint" command causes a call to the no-op ** routine named test_breakpoint(). */ if( c=='b' && n>=3 && strncmp(azArg[0], "breakpoint", n)==0 ){ test_breakpoint(); }else #ifndef SQLITE_SHELL_WASM_MODE if( c=='c' && strcmp(azArg[0],"cd")==0 ){ failIfSafeMode(p, "cannot run .cd in safe mode"); if( nArg==2 ){ #if defined(_WIN32) || defined(WIN32) wchar_t *z = sqlite3_win32_utf8_to_unicode(azArg[1]); rc = !SetCurrentDirectoryW(z); sqlite3_free(z); #else rc = chdir(azArg[1]); #endif if( rc ){ utf8_printf(stderr, "Cannot change to directory \"%s\"\n", azArg[1]); rc = 1; } }else{ raw_printf(stderr, "Usage: .cd DIRECTORY\n"); rc = 1; } }else #endif /* !defined(SQLITE_SHELL_WASM_MODE) */ if( c=='c' && n>=3 && strncmp(azArg[0], "changes", n)==0 ){ if( nArg==2 ){ setOrClearFlag(p, SHFLG_CountChanges, azArg[1]); }else{ raw_printf(stderr, "Usage: .changes on|off\n"); rc = 1; } }else #ifndef SQLITE_SHELL_WASM_MODE /* Cancel output redirection, if it is currently set (by .testcase) ** Then read the content of the testcase-out.txt file and compare against ** azArg[1]. If there are differences, report an error and exit. */ if( c=='c' && n>=3 && strncmp(azArg[0], "check", n)==0 ){ char *zRes = 0; output_reset(p); |
︙ | ︙ | |||
8281 8282 8283 8284 8285 8286 8287 8288 8289 8290 8291 8292 8293 8294 8295 8296 8297 8298 8299 8300 8301 8302 8303 8304 | rc = 1; }else{ utf8_printf(stdout, "testcase-%s ok\n", p->zTestcase); p->nCheck++; } sqlite3_free(zRes); }else if( c=='c' && strncmp(azArg[0], "clone", n)==0 ){ failIfSafeMode(p, "cannot run .clone in safe mode"); if( nArg==2 ){ tryToClone(p, azArg[1]); }else{ raw_printf(stderr, "Usage: .clone FILENAME\n"); rc = 1; } }else if( c=='c' && strncmp(azArg[0], "connection", n)==0 ){ if( nArg==1 ){ /* List available connections */ int i; for(i=0; i<ArraySize(p->aAuxDb); i++){ const char *zFile = p->aAuxDb[i].zDbFilename; | > > > | 8337 8338 8339 8340 8341 8342 8343 8344 8345 8346 8347 8348 8349 8350 8351 8352 8353 8354 8355 8356 8357 8358 8359 8360 8361 8362 8363 | rc = 1; }else{ utf8_printf(stdout, "testcase-%s ok\n", p->zTestcase); p->nCheck++; } sqlite3_free(zRes); }else #endif /* !defined(SQLITE_SHELL_WASM_MODE) */ #ifndef SQLITE_SHELL_WASM_MODE if( c=='c' && strncmp(azArg[0], "clone", n)==0 ){ failIfSafeMode(p, "cannot run .clone in safe mode"); if( nArg==2 ){ tryToClone(p, azArg[1]); }else{ raw_printf(stderr, "Usage: .clone FILENAME\n"); rc = 1; } }else #endif /* !defined(SQLITE_SHELL_WASM_MODE) */ if( c=='c' && strncmp(azArg[0], "connection", n)==0 ){ if( nArg==1 ){ /* List available connections */ int i; for(i=0; i<ArraySize(p->aAuxDb); i++){ const char *zFile = p->aAuxDb[i].zDbFilename; |
︙ | ︙ | |||
8433 8434 8435 8436 8437 8438 8439 | if( c=='d' && strncmp(azArg[0], "dump", n)==0 ){ char *zLike = 0; char *zSql; int i; int savedShowHeader = p->showHeader; int savedShellFlags = p->shellFlgs; | | | 8492 8493 8494 8495 8496 8497 8498 8499 8500 8501 8502 8503 8504 8505 8506 | if( c=='d' && strncmp(azArg[0], "dump", n)==0 ){ char *zLike = 0; char *zSql; int i; int savedShowHeader = p->showHeader; int savedShellFlags = p->shellFlgs; ShellClearFlag(p, SHFLG_PreserveRowid|SHFLG_Newlines|SHFLG_Echo |SHFLG_DumpDataOnly|SHFLG_DumpNoSys); for(i=1; i<nArg; i++){ if( azArg[i][0]=='-' ){ const char *z = azArg[i]+1; if( z[0]=='-' ) z++; if( strcmp(z,"preserve-rowids")==0 ){ |
︙ | ︙ | |||
8579 8580 8581 8582 8583 8584 8585 8586 8587 8588 8589 8590 8591 8592 8593 8594 8595 8596 | } }else{ raw_printf(stderr, "Usage: .eqp off|on|trace|trigger|full\n"); rc = 1; } }else if( c=='e' && strncmp(azArg[0], "exit", n)==0 ){ if( nArg>1 && (rc = (int)integerValue(azArg[1]))!=0 ) exit(rc); rc = 2; }else /* The ".explain" command is automatic now. It is largely pointless. It ** retained purely for backwards compatibility */ if( c=='e' && strncmp(azArg[0], "explain", n)==0 ){ int val = 1; if( nArg>=2 ){ if( strcmp(azArg[1],"auto")==0 ){ | > > | 8638 8639 8640 8641 8642 8643 8644 8645 8646 8647 8648 8649 8650 8651 8652 8653 8654 8655 8656 8657 | } }else{ raw_printf(stderr, "Usage: .eqp off|on|trace|trigger|full\n"); rc = 1; } }else #ifndef SQLITE_SHELL_WASM_MODE if( c=='e' && strncmp(azArg[0], "exit", n)==0 ){ if( nArg>1 && (rc = (int)integerValue(azArg[1]))!=0 ) exit(rc); rc = 2; }else #endif /* The ".explain" command is automatic now. It is largely pointless. It ** retained purely for backwards compatibility */ if( c=='e' && strncmp(azArg[0], "explain", n)==0 ){ int val = 1; if( nArg>=2 ){ if( strcmp(azArg[1],"auto")==0 ){ |
︙ | ︙ | |||
8837 8838 8839 8840 8841 8842 8843 8844 8845 8846 8847 8848 8849 8850 | utf8_printf(p->out, "Nothing matches '%s'\n", azArg[1]); } }else{ showHelp(p->out, 0); } }else if( c=='i' && strncmp(azArg[0], "import", n)==0 ){ char *zTable = 0; /* Insert data into this table */ char *zSchema = 0; /* within this schema (may default to "main") */ char *zFile = 0; /* Name of file to extra content from */ sqlite3_stmt *pStmt = NULL; /* A statement */ int nCol; /* Number of columns in the table */ int nByte; /* Number of bytes in an SQL string */ | > | 8898 8899 8900 8901 8902 8903 8904 8905 8906 8907 8908 8909 8910 8911 8912 | utf8_printf(p->out, "Nothing matches '%s'\n", azArg[1]); } }else{ showHelp(p->out, 0); } }else #ifndef SQLITE_SHELL_WASM_MODE if( c=='i' && strncmp(azArg[0], "import", n)==0 ){ char *zTable = 0; /* Insert data into this table */ char *zSchema = 0; /* within this schema (may default to "main") */ char *zFile = 0; /* Name of file to extra content from */ sqlite3_stmt *pStmt = NULL; /* A statement */ int nCol; /* Number of columns in the table */ int nByte; /* Number of bytes in an SQL string */ |
︙ | ︙ | |||
8858 8859 8860 8861 8862 8863 8864 | int eVerbose = 0; /* Larger for more console output */ int nSkip = 0; /* Initial lines to skip */ int useOutputMode = 1; /* Use output mode to determine separators */ char *zCreate = 0; /* CREATE TABLE statement text */ failIfSafeMode(p, "cannot run .import in safe mode"); memset(&sCtx, 0, sizeof(sCtx)); | < < < < < > < | 8920 8921 8922 8923 8924 8925 8926 8927 8928 8929 8930 8931 8932 8933 8934 8935 8936 8937 8938 8939 8940 8941 8942 8943 8944 8945 8946 8947 8948 8949 8950 | int eVerbose = 0; /* Larger for more console output */ int nSkip = 0; /* Initial lines to skip */ int useOutputMode = 1; /* Use output mode to determine separators */ char *zCreate = 0; /* CREATE TABLE statement text */ failIfSafeMode(p, "cannot run .import in safe mode"); memset(&sCtx, 0, sizeof(sCtx)); if( p->mode==MODE_Ascii ){ xRead = ascii_read_one_field; }else{ xRead = csv_read_one_field; } rc = 1; for(i=1; i<nArg; i++){ char *z = azArg[i]; if( z[0]=='-' && z[1]=='-' ) z++; if( z[0]!='-' ){ if( zFile==0 ){ zFile = z; }else if( zTable==0 ){ zTable = z; }else{ utf8_printf(p->out, "ERROR: extra argument: \"%s\". Usage:\n", z); showHelp(p->out, "import"); goto meta_command_exit; } }else if( strcmp(z,"-v")==0 ){ eVerbose++; }else if( strcmp(z,"-schema")==0 && i<nArg-1 ){ zSchema = azArg[++i]; }else if( strcmp(z,"-skip")==0 && i<nArg-1 ){ |
︙ | ︙ | |||
8901 8902 8903 8904 8905 8906 8907 | sCtx.cColSep = ','; sCtx.cRowSep = '\n'; xRead = csv_read_one_field; useOutputMode = 0; }else{ utf8_printf(p->out, "ERROR: unknown option: \"%s\". Usage:\n", z); showHelp(p->out, "import"); | < < < | < < < < < < > > > > > | 8958 8959 8960 8961 8962 8963 8964 8965 8966 8967 8968 8969 8970 8971 8972 8973 8974 8975 8976 8977 8978 8979 8980 8981 8982 8983 8984 8985 8986 8987 8988 8989 8990 8991 8992 8993 8994 8995 8996 8997 8998 8999 9000 9001 9002 9003 9004 9005 9006 9007 9008 9009 9010 9011 9012 9013 9014 9015 9016 9017 9018 9019 9020 9021 9022 9023 9024 9025 9026 9027 9028 9029 9030 9031 9032 9033 9034 9035 9036 9037 9038 9039 9040 9041 9042 9043 9044 9045 9046 9047 9048 9049 9050 9051 9052 9053 | sCtx.cColSep = ','; sCtx.cRowSep = '\n'; xRead = csv_read_one_field; useOutputMode = 0; }else{ utf8_printf(p->out, "ERROR: unknown option: \"%s\". Usage:\n", z); showHelp(p->out, "import"); goto meta_command_exit; } } if( zTable==0 ){ utf8_printf(p->out, "ERROR: missing %s argument. Usage:\n", zFile==0 ? "FILE" : "TABLE"); showHelp(p->out, "import"); goto meta_command_exit; } seenInterrupt = 0; open_db(p, 0); if( useOutputMode ){ /* If neither the --csv or --ascii options are specified, then set ** the column and row separator characters from the output mode. */ nSep = strlen30(p->colSeparator); if( nSep==0 ){ raw_printf(stderr, "Error: non-null column separator required for import\n"); goto meta_command_exit; } if( nSep>1 ){ raw_printf(stderr, "Error: multi-character column separators not allowed" " for import\n"); goto meta_command_exit; } nSep = strlen30(p->rowSeparator); if( nSep==0 ){ raw_printf(stderr, "Error: non-null row separator required for import\n"); goto meta_command_exit; } if( nSep==2 && p->mode==MODE_Csv && strcmp(p->rowSeparator,SEP_CrLf)==0 ){ /* When importing CSV (only), if the row separator is set to the ** default output row separator, change it to the default input ** row separator. This avoids having to maintain different input ** and output row separators. */ sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, SEP_Row); nSep = strlen30(p->rowSeparator); } if( nSep>1 ){ raw_printf(stderr, "Error: multi-character row separators not allowed" " for import\n"); goto meta_command_exit; } sCtx.cColSep = p->colSeparator[0]; sCtx.cRowSep = p->rowSeparator[0]; } sCtx.zFile = zFile; sCtx.nLine = 1; if( sCtx.zFile[0]=='|' ){ #ifdef SQLITE_OMIT_POPEN raw_printf(stderr, "Error: pipes are not supported in this OS\n"); goto meta_command_exit; #else sCtx.in = popen(sCtx.zFile+1, "r"); sCtx.zFile = "<pipe>"; sCtx.xCloser = pclose; #endif }else{ sCtx.in = fopen(sCtx.zFile, "rb"); sCtx.xCloser = fclose; } if( sCtx.in==0 ){ utf8_printf(stderr, "Error: cannot open \"%s\"\n", zFile); goto meta_command_exit; } if( eVerbose>=2 || (eVerbose>=1 && useOutputMode) ){ char zSep[2]; zSep[1] = 0; zSep[0] = sCtx.cColSep; utf8_printf(p->out, "Column separator "); output_c_string(p->out, zSep); utf8_printf(p->out, ", row separator "); zSep[0] = sCtx.cRowSep; output_c_string(p->out, zSep); utf8_printf(p->out, "\n"); } sCtx.z = sqlite3_malloc64(120); if( sCtx.z==0 ){ import_cleanup(&sCtx); shell_out_of_memory(); } /* Below, resources must be freed before exit. */ while( (nSkip--)>0 ){ while( xRead(&sCtx) && sCtx.cTerm==sCtx.cColSep ){} } if( zSchema!=0 ){ zFullTabName = sqlite3_mprintf("\"%w\".\"%w\"", zSchema, zTable); |
︙ | ︙ | |||
9136 9137 9138 9139 9140 9141 9142 9143 9144 9145 9146 9147 9148 9149 | if( needCommit ) sqlite3_exec(p->db, "COMMIT", 0, 0, 0); if( eVerbose>0 ){ utf8_printf(p->out, "Added %d rows with %d errors using %d lines of input\n", sCtx.nRow, sCtx.nErr, sCtx.nLine-1); } }else #ifndef SQLITE_UNTESTABLE if( c=='i' && strncmp(azArg[0], "imposter", n)==0 ){ char *zSql; char *zCollist = 0; sqlite3_stmt *pStmt; int tnum = 0; | > | 9189 9190 9191 9192 9193 9194 9195 9196 9197 9198 9199 9200 9201 9202 9203 | if( needCommit ) sqlite3_exec(p->db, "COMMIT", 0, 0, 0); if( eVerbose>0 ){ utf8_printf(p->out, "Added %d rows with %d errors using %d lines of input\n", sCtx.nRow, sCtx.nErr, sCtx.nLine-1); } }else #endif /* !defined(SQLITE_SHELL_WASM_MODE) */ #ifndef SQLITE_UNTESTABLE if( c=='i' && strncmp(azArg[0], "imposter", n)==0 ){ char *zSql; char *zCollist = 0; sqlite3_stmt *pStmt; int tnum = 0; |
︙ | ︙ | |||
9325 9326 9327 9328 9329 9330 9331 | }else if( c=='l' && n>2 && strncmp(azArg[0], "lint", n)==0 ){ open_db(p, 0); lintDotCommand(p, azArg, nArg); }else | | | 9379 9380 9381 9382 9383 9384 9385 9386 9387 9388 9389 9390 9391 9392 9393 | }else if( c=='l' && n>2 && strncmp(azArg[0], "lint", n)==0 ){ open_db(p, 0); lintDotCommand(p, azArg, nArg); }else #if !defined(SQLITE_OMIT_LOAD_EXTENSION) && !defined(SQLITE_SHELL_WASM_MODE) if( c=='l' && strncmp(azArg[0], "load", n)==0 ){ const char *zFile, *zProc; char *zErrMsg = 0; failIfSafeMode(p, "cannot run .load in safe mode"); if( nArg<2 ){ raw_printf(stderr, "Usage: .load FILE ?ENTRYPOINT?\n"); rc = 1; |
︙ | ︙ | |||
9347 9348 9349 9350 9351 9352 9353 9354 9355 9356 9357 9358 9359 9360 9361 9362 9363 9364 9365 9366 9367 9368 9369 9370 9371 | utf8_printf(stderr, "Error: %s\n", zErrMsg); sqlite3_free(zErrMsg); rc = 1; } }else #endif if( c=='l' && strncmp(azArg[0], "log", n)==0 ){ failIfSafeMode(p, "cannot run .log in safe mode"); if( nArg!=2 ){ raw_printf(stderr, "Usage: .log FILENAME\n"); rc = 1; }else{ const char *zFile = azArg[1]; output_file_close(p->pLog); p->pLog = output_file_open(zFile, 0); } }else if( c=='m' && strncmp(azArg[0], "mode", n)==0 ){ const char *zMode = 0; const char *zTabname = 0; int i, n2; ColModeOpts cmOpts = ColModeOpts_default; for(i=1; i<nArg; i++){ | > > | 9401 9402 9403 9404 9405 9406 9407 9408 9409 9410 9411 9412 9413 9414 9415 9416 9417 9418 9419 9420 9421 9422 9423 9424 9425 9426 9427 | utf8_printf(stderr, "Error: %s\n", zErrMsg); sqlite3_free(zErrMsg); rc = 1; } }else #endif #ifndef SQLITE_SHELL_WASM_MODE if( c=='l' && strncmp(azArg[0], "log", n)==0 ){ failIfSafeMode(p, "cannot run .log in safe mode"); if( nArg!=2 ){ raw_printf(stderr, "Usage: .log FILENAME\n"); rc = 1; }else{ const char *zFile = azArg[1]; output_file_close(p->pLog); p->pLog = output_file_open(zFile, 0); } }else #endif if( c=='m' && strncmp(azArg[0], "mode", n)==0 ){ const char *zMode = 0; const char *zTabname = 0; int i, n2; ColModeOpts cmOpts = ColModeOpts_default; for(i=1; i<nArg; i++){ |
︙ | ︙ | |||
9482 9483 9484 9485 9486 9487 9488 9489 9490 9491 9492 9493 9494 9495 9496 9497 9498 9499 9500 9501 9502 9503 9504 9505 9506 9507 9508 9509 | "ascii box column csv html insert json line list markdown " "qbox quote table tabs tcl\n"); rc = 1; } p->cMode = p->mode; }else if( c=='n' && strcmp(azArg[0], "nonce")==0 ){ if( nArg!=2 ){ raw_printf(stderr, "Usage: .nonce NONCE\n"); rc = 1; }else if( p->zNonce==0 || strcmp(azArg[1],p->zNonce)!=0 ){ raw_printf(stderr, "line %d: incorrect nonce: \"%s\"\n", p->lineno, azArg[1]); exit(1); }else{ p->bSafeMode = 0; return 0; /* Return immediately to bypass the safe mode reset ** at the end of this procedure */ } }else if( c=='n' && strncmp(azArg[0], "nullvalue", n)==0 ){ if( nArg==2 ){ sqlite3_snprintf(sizeof(p->nullValue), p->nullValue, "%.*s", (int)ArraySize(p->nullValue)-1, azArg[1]); }else{ raw_printf(stderr, "Usage: .nullvalue STRING\n"); | > > | 9538 9539 9540 9541 9542 9543 9544 9545 9546 9547 9548 9549 9550 9551 9552 9553 9554 9555 9556 9557 9558 9559 9560 9561 9562 9563 9564 9565 9566 9567 | "ascii box column csv html insert json line list markdown " "qbox quote table tabs tcl\n"); rc = 1; } p->cMode = p->mode; }else #ifndef SQLITE_SHELL_WASM_MODE if( c=='n' && strcmp(azArg[0], "nonce")==0 ){ if( nArg!=2 ){ raw_printf(stderr, "Usage: .nonce NONCE\n"); rc = 1; }else if( p->zNonce==0 || strcmp(azArg[1],p->zNonce)!=0 ){ raw_printf(stderr, "line %d: incorrect nonce: \"%s\"\n", p->lineno, azArg[1]); exit(1); }else{ p->bSafeMode = 0; return 0; /* Return immediately to bypass the safe mode reset ** at the end of this procedure */ } }else #endif /* !defined(SQLITE_SHELL_WASM_MODE) */ if( c=='n' && strncmp(azArg[0], "nullvalue", n)==0 ){ if( nArg==2 ){ sqlite3_snprintf(sizeof(p->nullValue), p->nullValue, "%.*s", (int)ArraySize(p->nullValue)-1, azArg[1]); }else{ raw_printf(stderr, "Usage: .nullvalue STRING\n"); |
︙ | ︙ | |||
9517 9518 9519 9520 9521 9522 9523 9524 9525 9526 9527 9528 9529 9530 9531 9532 9533 9534 9535 9536 9537 9538 9539 9540 9541 9542 9543 | int iName = 1; /* Index in azArg[] of the filename */ int newFlag = 0; /* True to delete file before opening */ int openMode = SHELL_OPEN_UNSPEC; /* Check for command-line arguments */ for(iName=1; iName<nArg; iName++){ const char *z = azArg[iName]; if( optionMatch(z,"new") ){ newFlag = 1; #ifdef SQLITE_HAVE_ZLIB }else if( optionMatch(z, "zip") ){ openMode = SHELL_OPEN_ZIPFILE; #endif }else if( optionMatch(z, "append") ){ openMode = SHELL_OPEN_APPENDVFS; }else if( optionMatch(z, "readonly") ){ openMode = SHELL_OPEN_READONLY; }else if( optionMatch(z, "nofollow") ){ p->openFlags |= SQLITE_OPEN_NOFOLLOW; #ifndef SQLITE_OMIT_DESERIALIZE }else if( optionMatch(z, "deserialize") ){ openMode = SHELL_OPEN_DESERIALIZE; }else if( optionMatch(z, "hexdb") ){ openMode = SHELL_OPEN_HEXDB; }else if( optionMatch(z, "maxsize") && iName+1<nArg ){ p->szMax = integerValue(azArg[++iName]); #endif /* SQLITE_OMIT_DESERIALIZE */ | > > > | | 9575 9576 9577 9578 9579 9580 9581 9582 9583 9584 9585 9586 9587 9588 9589 9590 9591 9592 9593 9594 9595 9596 9597 9598 9599 9600 9601 9602 9603 9604 9605 9606 9607 9608 9609 9610 9611 9612 | int iName = 1; /* Index in azArg[] of the filename */ int newFlag = 0; /* True to delete file before opening */ int openMode = SHELL_OPEN_UNSPEC; /* Check for command-line arguments */ for(iName=1; iName<nArg; iName++){ const char *z = azArg[iName]; #ifndef SQLITE_SHELL_WASM_MODE if( optionMatch(z,"new") ){ newFlag = 1; #ifdef SQLITE_HAVE_ZLIB }else if( optionMatch(z, "zip") ){ openMode = SHELL_OPEN_ZIPFILE; #endif }else if( optionMatch(z, "append") ){ openMode = SHELL_OPEN_APPENDVFS; }else if( optionMatch(z, "readonly") ){ openMode = SHELL_OPEN_READONLY; }else if( optionMatch(z, "nofollow") ){ p->openFlags |= SQLITE_OPEN_NOFOLLOW; #ifndef SQLITE_OMIT_DESERIALIZE }else if( optionMatch(z, "deserialize") ){ openMode = SHELL_OPEN_DESERIALIZE; }else if( optionMatch(z, "hexdb") ){ openMode = SHELL_OPEN_HEXDB; }else if( optionMatch(z, "maxsize") && iName+1<nArg ){ p->szMax = integerValue(azArg[++iName]); #endif /* SQLITE_OMIT_DESERIALIZE */ }else #endif /* !SQLITE_SHELL_WASM_MODE */ if( z[0]=='-' ){ utf8_printf(stderr, "unknown option: %s\n", z); rc = 1; goto meta_command_exit; }else if( zFN ){ utf8_printf(stderr, "extra argument: \"%s\"\n", z); rc = 1; goto meta_command_exit; |
︙ | ︙ | |||
9564 9565 9566 9567 9568 9569 9570 9571 9572 9573 9574 9575 9576 9577 9578 9579 9580 9581 9582 9583 9584 | p->openMode = openMode; p->openFlags = 0; p->szMax = 0; /* If a filename is specified, try to open it first */ if( zFN || p->openMode==SHELL_OPEN_HEXDB ){ if( newFlag && zFN && !p->bSafeMode ) shellDeleteFile(zFN); if( p->bSafeMode && p->openMode!=SHELL_OPEN_HEXDB && zFN && strcmp(zFN,":memory:")!=0 ){ failIfSafeMode(p, "cannot open disk-based database files in safe mode"); } if( zFN ){ zNewFilename = sqlite3_mprintf("%s", zFN); shell_check_oom(zNewFilename); }else{ zNewFilename = 0; } p->pAuxDb->zDbFilename = zNewFilename; | > > > > | 9625 9626 9627 9628 9629 9630 9631 9632 9633 9634 9635 9636 9637 9638 9639 9640 9641 9642 9643 9644 9645 9646 9647 9648 9649 | p->openMode = openMode; p->openFlags = 0; p->szMax = 0; /* If a filename is specified, try to open it first */ if( zFN || p->openMode==SHELL_OPEN_HEXDB ){ if( newFlag && zFN && !p->bSafeMode ) shellDeleteFile(zFN); #ifndef SQLITE_SHELL_WASM_MODE if( p->bSafeMode && p->openMode!=SHELL_OPEN_HEXDB && zFN && strcmp(zFN,":memory:")!=0 ){ failIfSafeMode(p, "cannot open disk-based database files in safe mode"); } #else /* WASM mode has its own sandboxed pseudo-filesystem. */ #endif if( zFN ){ zNewFilename = sqlite3_mprintf("%s", zFN); shell_check_oom(zNewFilename); }else{ zNewFilename = 0; } p->pAuxDb->zDbFilename = zNewFilename; |
︙ | ︙ | |||
9593 9594 9595 9596 9597 9598 9599 9600 9601 9602 9603 9604 9605 9606 | if( p->db==0 ){ /* As a fall-back open a TEMP database */ p->pAuxDb->zDbFilename = 0; open_db(p, 0); } }else if( (c=='o' && (strncmp(azArg[0], "output", n)==0||strncmp(azArg[0], "once", n)==0)) || (c=='e' && n==5 && strcmp(azArg[0],"excel")==0) ){ char *zFile = 0; int bTxtMode = 0; int i; | > | 9658 9659 9660 9661 9662 9663 9664 9665 9666 9667 9668 9669 9670 9671 9672 | if( p->db==0 ){ /* As a fall-back open a TEMP database */ p->pAuxDb->zDbFilename = 0; open_db(p, 0); } }else #ifndef SQLITE_SHELL_WASM_MODE if( (c=='o' && (strncmp(azArg[0], "output", n)==0||strncmp(azArg[0], "once", n)==0)) || (c=='e' && n==5 && strcmp(azArg[0],"excel")==0) ){ char *zFile = 0; int bTxtMode = 0; int i; |
︙ | ︙ | |||
9708 9709 9710 9711 9712 9713 9714 9715 9716 9717 9718 9719 9720 9721 | } else { if( zBOM[0] ) fwrite(zBOM, 1, 3, p->out); sqlite3_snprintf(sizeof(p->outfile), p->outfile, "%s", zFile); } } sqlite3_free(zFile); }else if( c=='p' && n>=3 && strncmp(azArg[0], "parameter", n)==0 ){ open_db(p,0); if( nArg<=1 ) goto parameter_syntax_error; /* .parameter clear ** Clear all bind parameters by dropping the TEMP table that holds them. | > | 9774 9775 9776 9777 9778 9779 9780 9781 9782 9783 9784 9785 9786 9787 9788 | } else { if( zBOM[0] ) fwrite(zBOM, 1, 3, p->out); sqlite3_snprintf(sizeof(p->outfile), p->outfile, "%s", zFile); } } sqlite3_free(zFile); }else #endif /* !defined(SQLITE_SHELL_WASM_MODE) */ if( c=='p' && n>=3 && strncmp(azArg[0], "parameter", n)==0 ){ open_db(p,0); if( nArg<=1 ) goto parameter_syntax_error; /* .parameter clear ** Clear all bind parameters by dropping the TEMP table that holds them. |
︙ | ︙ | |||
9877 9878 9879 9880 9881 9882 9883 9884 9885 9886 9887 9888 9889 9890 9891 9892 9893 9894 | strncpy(mainPrompt,azArg[1],(int)ArraySize(mainPrompt)-1); } if( nArg >= 3) { strncpy(continuePrompt,azArg[2],(int)ArraySize(continuePrompt)-1); } }else if( c=='q' && strncmp(azArg[0], "quit", n)==0 ){ rc = 2; }else if( c=='r' && n>=3 && strncmp(azArg[0], "read", n)==0 ){ FILE *inSaved = p->in; int savedLineno = p->lineno; failIfSafeMode(p, "cannot run .read in safe mode"); if( nArg!=2 ){ raw_printf(stderr, "Usage: .read FILE\n"); rc = 1; | > > > | 9944 9945 9946 9947 9948 9949 9950 9951 9952 9953 9954 9955 9956 9957 9958 9959 9960 9961 9962 9963 9964 | strncpy(mainPrompt,azArg[1],(int)ArraySize(mainPrompt)-1); } if( nArg >= 3) { strncpy(continuePrompt,azArg[2],(int)ArraySize(continuePrompt)-1); } }else #ifndef SQLITE_SHELL_WASM_MODE if( c=='q' && strncmp(azArg[0], "quit", n)==0 ){ rc = 2; }else #endif #ifndef SQLITE_SHELL_WASM_MODE if( c=='r' && n>=3 && strncmp(azArg[0], "read", n)==0 ){ FILE *inSaved = p->in; int savedLineno = p->lineno; failIfSafeMode(p, "cannot run .read in safe mode"); if( nArg!=2 ){ raw_printf(stderr, "Usage: .read FILE\n"); rc = 1; |
︙ | ︙ | |||
9915 9916 9917 9918 9919 9920 9921 9922 9923 9924 9925 9926 9927 9928 9929 | }else{ rc = process_input(p); fclose(p->in); } p->in = inSaved; p->lineno = savedLineno; }else if( c=='r' && n>=3 && strncmp(azArg[0], "restore", n)==0 ){ const char *zSrcFile; const char *zDb; sqlite3 *pSrc; sqlite3_backup *pBackup; int nTimeout = 0; | > > | 9985 9986 9987 9988 9989 9990 9991 9992 9993 9994 9995 9996 9997 9998 9999 10000 10001 | }else{ rc = process_input(p); fclose(p->in); } p->in = inSaved; p->lineno = savedLineno; }else #endif /* !defined(SQLITE_SHELL_WASM_MODE) */ #ifndef SQLITE_SHELL_WASM_MODE if( c=='r' && n>=3 && strncmp(azArg[0], "restore", n)==0 ){ const char *zSrcFile; const char *zDb; sqlite3 *pSrc; sqlite3_backup *pBackup; int nTimeout = 0; |
︙ | ︙ | |||
9967 9968 9969 9970 9971 9972 9973 9974 9975 9976 9977 9978 9979 9980 | rc = 1; }else{ utf8_printf(stderr, "Error: %s\n", sqlite3_errmsg(p->db)); rc = 1; } close_db(pSrc); }else if( c=='s' && strncmp(azArg[0], "scanstats", n)==0 ){ if( nArg==2 ){ p->scanstatsOn = (u8)booleanValue(azArg[1]); #ifndef SQLITE_ENABLE_STMT_SCANSTATUS raw_printf(stderr, "Warning: .scanstats not available in this build.\n"); #endif | > | 10039 10040 10041 10042 10043 10044 10045 10046 10047 10048 10049 10050 10051 10052 10053 | rc = 1; }else{ utf8_printf(stderr, "Error: %s\n", sqlite3_errmsg(p->db)); rc = 1; } close_db(pSrc); }else #endif /* !defined(SQLITE_SHELL_WASM_MODE) */ if( c=='s' && strncmp(azArg[0], "scanstats", n)==0 ){ if( nArg==2 ){ p->scanstatsOn = (u8)booleanValue(azArg[1]); #ifndef SQLITE_ENABLE_STMT_SCANSTATUS raw_printf(stderr, "Warning: .scanstats not available in this build.\n"); #endif |
︙ | ︙ | |||
10592 10593 10594 10595 10596 10597 10598 | utf8_printf(p->out, "%s\n", zSql); }else{ shell_exec(p, zSql, 0); } sqlite3_free(zSql); }else | | | | | 10665 10666 10667 10668 10669 10670 10671 10672 10673 10674 10675 10676 10677 10678 10679 10680 10681 10682 10683 10684 10685 10686 10687 10688 10689 10690 10691 10692 10693 10694 10695 10696 10697 10698 10699 10700 10701 10702 10703 10704 10705 10706 10707 10708 10709 10710 10711 10712 | utf8_printf(p->out, "%s\n", zSql); }else{ shell_exec(p, zSql, 0); } sqlite3_free(zSql); }else #if !defined(SQLITE_NOHAVE_SYSTEM) && !defined(SQLITE_SHELL_WASM_MODE) if( c=='s' && (strncmp(azArg[0], "shell", n)==0 || strncmp(azArg[0],"system",n)==0) ){ char *zCmd; int i, x; failIfSafeMode(p, "cannot run .%s in safe mode", azArg[0]); if( nArg<2 ){ raw_printf(stderr, "Usage: .system COMMAND\n"); rc = 1; goto meta_command_exit; } zCmd = sqlite3_mprintf(strchr(azArg[1],' ')==0?"%s":"\"%s\"", azArg[1]); for(i=2; i<nArg && zCmd!=0; i++){ zCmd = sqlite3_mprintf(strchr(azArg[i],' ')==0?"%z %s":"%z \"%s\"", zCmd, azArg[i]); } x = zCmd!=0 ? system(zCmd) : 1; sqlite3_free(zCmd); if( x ) raw_printf(stderr, "System command returns %d\n", x); }else #endif /* !defined(SQLITE_NOHAVE_SYSTEM) && !defined(SQLITE_SHELL_WASM_MODE) */ if( c=='s' && strncmp(azArg[0], "show", n)==0 ){ static const char *azBool[] = { "off", "on", "trigger", "full"}; const char *zOut; int i; if( nArg!=1 ){ raw_printf(stderr, "Usage: .show\n"); rc = 1; goto meta_command_exit; } utf8_printf(p->out, "%12.12s: %s\n","echo", azBool[ShellHasFlag(p, SHFLG_Echo)]); utf8_printf(p->out, "%12.12s: %s\n","eqp", azBool[p->autoEQP&3]); utf8_printf(p->out, "%12.12s: %s\n","explain", p->mode==MODE_Explain ? "on" : p->autoExplain ? "auto" : "off"); utf8_printf(p->out,"%12.12s: %s\n","headers", azBool[p->showHeader!=0]); if( p->mode==MODE_Column || (p->mode>=MODE_Markdown && p->mode<=MODE_Box) ){ |
︙ | ︙ | |||
10793 10794 10795 10796 10797 10798 10799 10800 10801 10802 10803 10804 10805 10806 10807 10808 10809 10810 10811 10812 10813 10814 10815 10816 10817 10818 10819 | } } for(ii=0; ii<nRow; ii++) sqlite3_free(azResult[ii]); sqlite3_free(azResult); }else /* Begin redirecting output to the file "testcase-out.txt" */ if( c=='t' && strcmp(azArg[0],"testcase")==0 ){ output_reset(p); p->out = output_file_open("testcase-out.txt", 0); if( p->out==0 ){ raw_printf(stderr, "Error: cannot open 'testcase-out.txt'\n"); } if( nArg>=2 ){ sqlite3_snprintf(sizeof(p->zTestcase), p->zTestcase, "%s", azArg[1]); }else{ sqlite3_snprintf(sizeof(p->zTestcase), p->zTestcase, "?"); } }else #ifndef SQLITE_UNTESTABLE if( c=='t' && n>=8 && strncmp(azArg[0], "testctrl", n)==0 ){ static const struct { const char *zCtrlName; /* Name of a test-control option */ int ctrlCode; /* Integer code for that option */ int unSafe; /* Not valid for --safe mode */ | > > | 10866 10867 10868 10869 10870 10871 10872 10873 10874 10875 10876 10877 10878 10879 10880 10881 10882 10883 10884 10885 10886 10887 10888 10889 10890 10891 10892 10893 10894 | } } for(ii=0; ii<nRow; ii++) sqlite3_free(azResult[ii]); sqlite3_free(azResult); }else #ifndef SQLITE_SHELL_WASM_MODE /* Begin redirecting output to the file "testcase-out.txt" */ if( c=='t' && strcmp(azArg[0],"testcase")==0 ){ output_reset(p); p->out = output_file_open("testcase-out.txt", 0); if( p->out==0 ){ raw_printf(stderr, "Error: cannot open 'testcase-out.txt'\n"); } if( nArg>=2 ){ sqlite3_snprintf(sizeof(p->zTestcase), p->zTestcase, "%s", azArg[1]); }else{ sqlite3_snprintf(sizeof(p->zTestcase), p->zTestcase, "?"); } }else #endif /* !defined(SQLITE_SHELL_WASM_MODE) */ #ifndef SQLITE_UNTESTABLE if( c=='t' && n>=8 && strncmp(azArg[0], "testctrl", n)==0 ){ static const struct { const char *zCtrlName; /* Name of a test-control option */ int ctrlCode; /* Integer code for that option */ int unSafe; /* Not valid for --safe mode */ |
︙ | ︙ | |||
11375 11376 11377 11378 11379 11380 11381 | continue; } /* fall thru */ case ']': cWait = 0; qss = QSS_SETV(qss, 0); goto PlainScan; | | | | 11450 11451 11452 11453 11454 11455 11456 11457 11458 11459 11460 11461 11462 11463 11464 11465 11466 11467 11468 11469 11470 11471 11472 11473 11474 11475 11476 11477 11478 11479 11480 11481 11482 11483 11484 11485 | continue; } /* fall thru */ case ']': cWait = 0; qss = QSS_SETV(qss, 0); goto PlainScan; default: assert(0); } } } } return qss; } /* ** Return TRUE if the line typed in is an SQL command terminator other ** than a semi-colon. The SQL Server style "go" command is understood ** as is the Oracle "/". */ static int line_is_command_terminator(char *zLine){ while( IsSpace(zLine[0]) ){ zLine++; }; if( zLine[0]=='/' ) zLine += 1; /* Oracle */ else if ( ToLower(zLine[0])=='g' && ToLower(zLine[1])=='o' ) zLine += 2; /* SQL Server */ else return 0; return quickscan(zLine, QSS_Start)==QSS_Start; } /* ** We need a default sqlite3_complete() implementation to use in case ** the shell is compiled with SQLITE_OMIT_COMPLETE. The default assumes ** any arbitrary text is a complete SQL statement. This is not very ** user-friendly, but it does seem to work. |
︙ | ︙ | |||
11473 11474 11475 11476 11477 11478 11479 11480 11481 11482 11483 11484 11485 11486 | "changes: %lld total_changes: %lld", sqlite3_changes64(p->db), sqlite3_total_changes64(p->db)); raw_printf(p->out, "%s\n", zLineBuf); } return 0; } /* ** Read input from *in and process it. If *in==0 then input ** is interactive - the user is typing it it. Otherwise, input ** is coming from a file or device. A prompt is issued and history ** is saved only if input is interactive. An interrupt signal will ** cause this routine to exit immediately, unless input is interactive. | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 11548 11549 11550 11551 11552 11553 11554 11555 11556 11557 11558 11559 11560 11561 11562 11563 11564 11565 11566 11567 11568 11569 11570 11571 11572 11573 11574 11575 11576 11577 11578 11579 11580 11581 11582 11583 11584 11585 11586 11587 11588 11589 11590 11591 11592 11593 11594 11595 11596 11597 | "changes: %lld total_changes: %lld", sqlite3_changes64(p->db), sqlite3_total_changes64(p->db)); raw_printf(p->out, "%s\n", zLineBuf); } return 0; } static void echo_group_input(ShellState *p, const char *zDo){ if( ShellHasFlag(p, SHFLG_Echo) ) utf8_printf(p->out, "%s\n", zDo); } #ifdef SQLITE_SHELL_WASM_MODE /* ** Alternate one_input_line() impl for wasm mode. This is not in the primary impl ** because we need the global shellState and cannot access it from that function ** without moving lots of code around (creating a larger/messier diff). */ static char *one_input_line(FILE *in, char *zPrior, int isContinuation){ /* Parse the next line from shellState.wasm.zInput. */ const char *zBegin = shellState.wasm.zPos; const char *z = zBegin; char *zLine = 0; int nZ = 0; UNUSED_PARAMETER(in); UNUSED_PARAMETER(isContinuation); if(!z || !*z){ return 0; } while(*z && isspace(*z)) ++z; zBegin = z; for(; *z && '\n'!=*z; ++nZ, ++z){} if(nZ>0 && '\r'==zBegin[nZ-1]){ --nZ; } shellState.wasm.zPos = z; zLine = realloc(zPrior, nZ+1); shell_check_oom(zLine); memcpy(zLine, zBegin, (size_t)nZ); zLine[nZ] = 0; return zLine; } #endif /* SQLITE_SHELL_WASM_MODE */ /* ** Read input from *in and process it. If *in==0 then input ** is interactive - the user is typing it it. Otherwise, input ** is coming from a file or device. A prompt is issued and history ** is saved only if input is interactive. An interrupt signal will ** cause this routine to exit immediately, unless input is interactive. |
︙ | ︙ | |||
11522 11523 11524 11525 11526 11527 11528 | if( QSS_INPLAIN(qss) && line_is_command_terminator(zLine) && line_is_complete(zSql, nSql) ){ memcpy(zLine,";",2); } qss = quickscan(zLine, qss); if( QSS_PLAINWHITE(qss) && nSql==0 ){ | < < > | | 11633 11634 11635 11636 11637 11638 11639 11640 11641 11642 11643 11644 11645 11646 11647 11648 11649 11650 11651 11652 11653 | if( QSS_INPLAIN(qss) && line_is_command_terminator(zLine) && line_is_complete(zSql, nSql) ){ memcpy(zLine,";",2); } qss = quickscan(zLine, qss); if( QSS_PLAINWHITE(qss) && nSql==0 ){ /* Just swallow single-line whitespace */ echo_group_input(p, zLine); qss = QSS_Start; continue; } if( zLine && (zLine[0]=='.' || zLine[0]=='#') && nSql==0 ){ echo_group_input(p, zLine); if( zLine[0]=='.' ){ rc = do_meta_command(zLine, p); if( rc==2 ){ /* exit requested */ break; }else if( rc ){ errCnt++; } |
︙ | ︙ | |||
11562 11563 11564 11565 11566 11567 11568 11569 11570 11571 11572 11573 11574 11575 11576 11577 11578 11579 | nSql = nLine-i; }else{ zSql[nSql++] = '\n'; memcpy(zSql+nSql, zLine, nLine+1); nSql += nLine; } if( nSql && QSS_SEMITERM(qss) && sqlite3_complete(zSql) ){ errCnt += runOneSqlLine(p, zSql, p->in, startline); nSql = 0; if( p->outCount ){ output_reset(p); p->outCount = 0; }else{ clearTempFile(p); } p->bSafeMode = p->bSafeModePersist; qss = QSS_Start; }else if( nSql && QSS_PLAINWHITE(qss) ){ | > | > | 11672 11673 11674 11675 11676 11677 11678 11679 11680 11681 11682 11683 11684 11685 11686 11687 11688 11689 11690 11691 11692 11693 11694 11695 11696 11697 11698 11699 11700 11701 11702 11703 11704 11705 | nSql = nLine-i; }else{ zSql[nSql++] = '\n'; memcpy(zSql+nSql, zLine, nLine+1); nSql += nLine; } if( nSql && QSS_SEMITERM(qss) && sqlite3_complete(zSql) ){ echo_group_input(p, zSql); errCnt += runOneSqlLine(p, zSql, p->in, startline); nSql = 0; if( p->outCount ){ output_reset(p); p->outCount = 0; }else{ clearTempFile(p); } p->bSafeMode = p->bSafeModePersist; qss = QSS_Start; }else if( nSql && QSS_PLAINWHITE(qss) ){ echo_group_input(p, zSql); nSql = 0; qss = QSS_Start; } } if( nSql ){ /* This may be incomplete. Let the SQL parser deal with that. */ echo_group_input(p, zSql); errCnt += runOneSqlLine(p, zSql, p->in, startline); } free(zSql); free(zLine); --p->inputNesting; return errCnt>0; } |
︙ | ︙ | |||
11718 11719 11720 11721 11722 11723 11724 | " -box set output mode to 'box'\n" " -column set output mode to 'column'\n" " -cmd COMMAND run \"COMMAND\" before reading stdin\n" " -csv set output mode to 'csv'\n" #if !defined(SQLITE_OMIT_DESERIALIZE) " -deserialize open the database using sqlite3_deserialize()\n" #endif | | | 11830 11831 11832 11833 11834 11835 11836 11837 11838 11839 11840 11841 11842 11843 11844 | " -box set output mode to 'box'\n" " -column set output mode to 'column'\n" " -cmd COMMAND run \"COMMAND\" before reading stdin\n" " -csv set output mode to 'csv'\n" #if !defined(SQLITE_OMIT_DESERIALIZE) " -deserialize open the database using sqlite3_deserialize()\n" #endif " -echo print inputs before execution\n" " -init FILENAME read/process named file\n" " -[no]header turn headers on or off\n" #if defined(SQLITE_ENABLE_MEMSYS3) || defined(SQLITE_ENABLE_MEMSYS5) " -heap SIZE Size of heap for memsys3 or memsys5\n" #endif " -help show this message\n" " -html set output mode to HTML\n" |
︙ | ︙ | |||
11853 11854 11855 11856 11857 11858 11859 11860 11861 11862 11863 11864 11865 11866 11867 11868 11869 11870 11871 11872 11873 11874 11875 11876 11877 11878 11879 11880 11881 11882 11883 11884 11885 11886 11887 11888 11889 11890 11891 11892 | # if (defined(_WIN32) || defined(WIN32)) \ && (defined(_MSC_VER) || (defined(UNICODE) && defined(__GNUC__))) # define SQLITE_SHELL_IS_UTF8 (0) # else # define SQLITE_SHELL_IS_UTF8 (1) # endif #endif #if SQLITE_SHELL_IS_UTF8 int SQLITE_CDECL main(int argc, char **argv){ #else int SQLITE_CDECL wmain(int argc, wchar_t **wargv){ char **argv; #endif char *zErrMsg = 0; ShellState data; const char *zInitFile = 0; int i; int rc = 0; int warnInmemoryDb = 0; int readStdin = 1; int nCmd = 0; char **azCmd = 0; const char *zVfs = 0; /* Value of -vfs command-line option */ #if !SQLITE_SHELL_IS_UTF8 char **argvToFree = 0; int argcToFree = 0; #endif setBinaryMode(stdin, 0); setvbuf(stderr, 0, _IONBF, 0); /* Make sure stderr is unbuffered */ stdin_is_interactive = isatty(0); stdout_is_console = isatty(1); #if !defined(_WIN32_WCE) if( getenv("SQLITE_DEBUG_BREAK") ){ if( isatty(0) && isatty(2) ){ fprintf(stderr, "attach debugger to process %d and press any key to continue.\n", GETPID()); | > > > > > > > > > > > > > > > > | 11965 11966 11967 11968 11969 11970 11971 11972 11973 11974 11975 11976 11977 11978 11979 11980 11981 11982 11983 11984 11985 11986 11987 11988 11989 11990 11991 11992 11993 11994 11995 11996 11997 11998 11999 12000 12001 12002 12003 12004 12005 12006 12007 12008 12009 12010 12011 12012 12013 12014 12015 12016 12017 12018 12019 12020 | # if (defined(_WIN32) || defined(WIN32)) \ && (defined(_MSC_VER) || (defined(UNICODE) && defined(__GNUC__))) # define SQLITE_SHELL_IS_UTF8 (0) # else # define SQLITE_SHELL_IS_UTF8 (1) # endif #endif #ifdef SQLITE_SHELL_WASM_MODE # define main fiddle_main #endif #if SQLITE_SHELL_IS_UTF8 int SQLITE_CDECL main(int argc, char **argv){ #else int SQLITE_CDECL wmain(int argc, wchar_t **wargv){ char **argv; #endif #ifdef SQLITE_DEBUG sqlite3_uint64 mem_main_enter = sqlite3_memory_used(); #endif char *zErrMsg = 0; #ifdef SQLITE_SHELL_WASM_MODE # define data shellState #else ShellState data; #endif const char *zInitFile = 0; int i; int rc = 0; int warnInmemoryDb = 0; int readStdin = 1; int nCmd = 0; char **azCmd = 0; const char *zVfs = 0; /* Value of -vfs command-line option */ #if !SQLITE_SHELL_IS_UTF8 char **argvToFree = 0; int argcToFree = 0; #endif setBinaryMode(stdin, 0); setvbuf(stderr, 0, _IONBF, 0); /* Make sure stderr is unbuffered */ #ifdef SQLITE_SHELL_WASM_MODE stdin_is_interactive = 0; stdout_is_console = 1; #else stdin_is_interactive = isatty(0); stdout_is_console = isatty(1); #endif #if !defined(_WIN32_WCE) if( getenv("SQLITE_DEBUG_BREAK") ){ if( isatty(0) && isatty(2) ){ fprintf(stderr, "attach debugger to process %d and press any key to continue.\n", GETPID()); |
︙ | ︙ | |||
12125 12126 12127 12128 12129 12130 12131 12132 12133 12134 12135 12136 12137 12138 12139 | warnInmemoryDb = argc==1; #else utf8_printf(stderr,"%s: Error: no database filename specified\n", Argv0); return 1; #endif } data.out = stdout; sqlite3_appendvfs_init(0,0,0); /* Go ahead and open the database file if it already exists. If the ** file does not exist, delay opening it. This prevents empty database ** files from being created if a user mistypes the database name argument ** to the sqlite command-line tool. */ if( access(data.pAuxDb->zDbFilename, 0)==0 ){ | > > | 12253 12254 12255 12256 12257 12258 12259 12260 12261 12262 12263 12264 12265 12266 12267 12268 12269 | warnInmemoryDb = argc==1; #else utf8_printf(stderr,"%s: Error: no database filename specified\n", Argv0); return 1; #endif } data.out = stdout; #ifndef SQLITE_SHELL_WASM_MODE sqlite3_appendvfs_init(0,0,0); #endif /* Go ahead and open the database file if it already exists. If the ** file does not exist, delay opening it. This prevents empty database ** files from being created if a user mistypes the database name argument ** to the sqlite command-line tool. */ if( access(data.pAuxDb->zDbFilename, 0)==0 ){ |
︙ | ︙ | |||
12391 12392 12393 12394 12395 12396 12397 12398 12399 12400 12401 12402 12403 12404 | free(zHistory); } }else{ data.in = stdin; rc = process_input(&data); } } free(azCmd); set_table_name(&data, 0); if( data.db ){ session_close_all(&data, -1); close_db(data.db); } for(i=0; i<ArraySize(data.aAuxDb); i++){ | > > > | 12521 12522 12523 12524 12525 12526 12527 12528 12529 12530 12531 12532 12533 12534 12535 12536 12537 | free(zHistory); } }else{ data.in = stdin; rc = process_input(&data); } } #ifndef SQLITE_SHELL_WASM_MODE /* In WASM mode we have to leave the db state in place so that ** client code can "push" SQL into it after this call returns. */ free(azCmd); set_table_name(&data, 0); if( data.db ){ session_close_all(&data, -1); close_db(data.db); } for(i=0; i<ArraySize(data.aAuxDb); i++){ |
︙ | ︙ | |||
12417 12418 12419 12420 12421 12422 12423 12424 12425 | free(argvToFree); #endif free(data.colWidth); free(data.zNonce); /* Clear the global data structure so that valgrind will detect memory ** leaks */ memset(&data, 0, sizeof(data)); return rc; } | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 12550 12551 12552 12553 12554 12555 12556 12557 12558 12559 12560 12561 12562 12563 12564 12565 12566 12567 12568 12569 12570 12571 12572 12573 12574 12575 12576 12577 12578 12579 12580 12581 12582 12583 12584 12585 12586 12587 12588 12589 12590 12591 12592 12593 12594 12595 12596 12597 12598 12599 12600 12601 12602 12603 12604 12605 12606 12607 12608 12609 12610 12611 12612 12613 12614 12615 12616 12617 12618 12619 12620 12621 12622 12623 12624 12625 12626 12627 12628 12629 12630 12631 12632 12633 12634 12635 12636 12637 12638 12639 12640 12641 12642 12643 12644 12645 12646 12647 12648 12649 12650 12651 12652 12653 12654 12655 12656 12657 12658 12659 12660 12661 12662 12663 12664 12665 12666 12667 12668 12669 12670 12671 12672 12673 12674 12675 12676 12677 12678 12679 12680 12681 12682 12683 12684 12685 12686 12687 12688 12689 | free(argvToFree); #endif free(data.colWidth); free(data.zNonce); /* Clear the global data structure so that valgrind will detect memory ** leaks */ memset(&data, 0, sizeof(data)); #ifdef SQLITE_DEBUG if( sqlite3_memory_used()>mem_main_enter ){ utf8_printf(stderr, "Memory leaked: %u bytes\n", (unsigned int)(sqlite3_memory_used()-mem_main_enter)); } #endif #endif /* !SQLITE_SHELL_WASM_MODE */ return rc; } #ifdef SQLITE_SHELL_WASM_MODE /* Only for emcc experimentation purposes. */ int fiddle_experiment(int a,int b){ return a + b; } /* Only for emcc experimentation purposes. Define this function in JS using: emcc ... --js-library somefile.js containing: mergeInto(LibraryManager.library, { my_foo: function(){ console.debug("my_foo()",arguments); } }); */ /*extern void my_foo(sqlite3 *);*/ /* Only for emcc experimentation purposes. */ sqlite3 * fiddle_the_db(){ printf("fiddle_the_db(%p)\n", (const void*)globalDb); /*my_foo(globalDb);*/ return globalDb; } /* Only for emcc experimentation purposes. */ sqlite3 * fiddle_db_arg(sqlite3 *arg){ printf("fiddle_db_arg(%p)\n", (const void*)arg); return arg; } /* ** Intended to be called via a SharedWorker() while a separate ** SharedWorker() (which manages the wasm module) is performing work ** which should be interrupted. Unfortunately, SharedWorker is not ** portable enough to make real use of. */ void fiddle_interrupt(void){ if(globalDb) sqlite3_interrupt(globalDb); } /* ** Returns the filename of the given db name, assuming "main" if ** zDbName is NULL. Returns NULL if globalDb is not opened. */ const char * fiddle_db_filename(const char * zDbName){ return globalDb ? sqlite3_db_filename(globalDb, zDbName ? zDbName : "main") : NULL; } /* ** Closes, unlinks, and reopens the db using its current filename (or ** the default if the db is currently closed). It is assumed, for ** purposes of the fiddle build, that the file is in a transient ** virtual filesystem within the browser. */ void fiddle_reset_db(void){ char *zFilename = 0; if(0==globalDb){ shellState.pAuxDb->zDbFilename = "/fiddle.sqlite3"; }else{ zFilename = sqlite3_mprintf("%s", sqlite3_db_filename(globalDb, "main")); shell_check_oom(zFilename); close_db(globalDb); shellDeleteFile(zFilename); shellState.db = 0; shellState.pAuxDb->zDbFilename = zFilename; } open_db(&shellState, 0); sqlite3_free(zFilename); } /* ** Trivial exportable function for emscripten. Needs to be exported using: ** ** emcc ..flags... -sEXPORTED_FUNCTIONS=_fiddle_exec -sEXPORTED_RUNTIME_METHODS=ccall,cwrap ** ** (Note the underscore before the function name.) It processes zSql ** as if it were input to the sqlite3 shell and redirects all output ** to the wasm binding. */ void fiddle_exec(const char * zSql){ static int once = 0; int rc = 0; if(!once){ /* Simulate an argv array for main() */ static char * argv[] = {"fiddle", "-bail", "-safe"}; rc = fiddle_main((int)(sizeof(argv)/sizeof(argv[0])), argv); once = rc ? -1 : 1; memset(&shellState.wasm, 0, sizeof(shellState.wasm)); printf( "SQLite version %s %.19s\n" /*extra-version-info*/, sqlite3_libversion(), sqlite3_sourceid() ); puts("WASM shell"); puts("Enter \".help\" for usage hints."); if(once>0){ fiddle_reset_db(); } if(shellState.db){ printf("Connected to %s.\n", fiddle_db_filename(NULL)); }else{ fprintf(stderr,"ERROR initializing db!\n"); return; } } if(once<0){ puts("DB init failed. Not executing SQL."); }else if(zSql && *zSql){ shellState.wasm.zInput = zSql; shellState.wasm.zPos = zSql; process_input(&shellState); memset(&shellState.wasm, 0, sizeof(shellState.wasm)); } } #endif /* SQLITE_SHELL_WASM_MODE */ |
Changes to src/sqlite.h.in.
︙ | ︙ | |||
6272 6273 6274 6275 6276 6277 6278 6279 6280 6281 6282 6283 6284 6285 | ** returned by sqlite3_db_handle is the same [database connection] ** that was the first argument ** to the [sqlite3_prepare_v2()] call (or its variants) that was used to ** create the statement in the first place. */ sqlite3 *sqlite3_db_handle(sqlite3_stmt*); /* ** CAPI3REF: Return The Filename For A Database Connection ** METHOD: sqlite3 ** ** ^The sqlite3_db_filename(D,N) interface returns a pointer to the filename ** associated with database N of connection D. ** ^If there is no attached database N on the database | > > > > > > > > > > > > > > > > > > > > > > | 6272 6273 6274 6275 6276 6277 6278 6279 6280 6281 6282 6283 6284 6285 6286 6287 6288 6289 6290 6291 6292 6293 6294 6295 6296 6297 6298 6299 6300 6301 6302 6303 6304 6305 6306 6307 | ** returned by sqlite3_db_handle is the same [database connection] ** that was the first argument ** to the [sqlite3_prepare_v2()] call (or its variants) that was used to ** create the statement in the first place. */ sqlite3 *sqlite3_db_handle(sqlite3_stmt*); /* ** CAPI3REF: Return The Schema Name For A Database Connection ** METHOD: sqlite3 ** ** ^The sqlite3_db_name(D,N) interface returns a pointer to the schema name ** for the N-th database on database connection D, or a NULL pointer of N is ** out of range. An N alue of 0 means the main database file. An N of 1 is ** the "temp" schema. Larger values of N correspond to various ATTACH-ed ** databases. ** ** Space to hold the string that is returned by sqlite3_db_name() is managed ** by SQLite itself. The string might be deallocated by any operation that ** changes the schema, including [ATTACH] or [DETACH] or calls to ** [sqlite3_serialize()] or [sqlite3_deserialize()], even operations that ** occur on a different thread. Applications that need to ** remember the string long-term should make their own copy. Applications that ** are accessing the same database connection simultaneously on multiple ** threads should mutex-protect calls to this API and should make their own ** private copy of the result prior to releasing the mutex. */ const char *sqlite3_db_name(sqlite3 *db, int N); /* ** CAPI3REF: Return The Filename For A Database Connection ** METHOD: sqlite3 ** ** ^The sqlite3_db_filename(D,N) interface returns a pointer to the filename ** associated with database N of connection D. ** ^If there is no attached database N on the database |
︙ | ︙ |
Changes to src/sqlite3ext.h.
︙ | ︙ | |||
352 353 354 355 356 357 358 359 360 361 362 363 364 365 | int (*vtab_in_first)(sqlite3_value*,sqlite3_value**); int (*vtab_in_next)(sqlite3_value*,sqlite3_value**); /* Version 3.39.0 and later */ int (*deserialize)(sqlite3*,const char*,unsigned char*, sqlite3_int64,sqlite3_int64,unsigned); unsigned char *(*serialize)(sqlite3*,const char *,sqlite3_int64*, unsigned int); }; /* ** This is the function signature used for all extension entry points. It ** is also defined in the file "loadext.c". */ typedef int (*sqlite3_loadext_entry)( | > | 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 | int (*vtab_in_first)(sqlite3_value*,sqlite3_value**); int (*vtab_in_next)(sqlite3_value*,sqlite3_value**); /* Version 3.39.0 and later */ int (*deserialize)(sqlite3*,const char*,unsigned char*, sqlite3_int64,sqlite3_int64,unsigned); unsigned char *(*serialize)(sqlite3*,const char *,sqlite3_int64*, unsigned int); const char *(*db_name)(sqlite3*,int); }; /* ** This is the function signature used for all extension entry points. It ** is also defined in the file "loadext.c". */ typedef int (*sqlite3_loadext_entry)( |
︙ | ︙ | |||
670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 | /* Version 3.38.0 and later */ #define sqlite3_error_offset sqlite3_api->error_offset #define sqlite3_vtab_rhs_value sqlite3_api->vtab_rhs_value #define sqlite3_vtab_distinct sqlite3_api->vtab_distinct #define sqlite3_vtab_in sqlite3_api->vtab_in #define sqlite3_vtab_in_first sqlite3_api->vtab_in_first #define sqlite3_vtab_in_next sqlite3_api->vtab_in_next #ifndef SQLITE_OMIT_DESERIALIZE #define sqlite3_deserialize sqlite3_api->deserialize #define sqlite3_serialize sqlite3_api->serialize #endif #endif /* !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) */ #if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) /* This case when the file really is being compiled as a loadable ** extension */ # define SQLITE_EXTENSION_INIT1 const sqlite3_api_routines *sqlite3_api=0; # define SQLITE_EXTENSION_INIT2(v) sqlite3_api=v; | > > | 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 | /* Version 3.38.0 and later */ #define sqlite3_error_offset sqlite3_api->error_offset #define sqlite3_vtab_rhs_value sqlite3_api->vtab_rhs_value #define sqlite3_vtab_distinct sqlite3_api->vtab_distinct #define sqlite3_vtab_in sqlite3_api->vtab_in #define sqlite3_vtab_in_first sqlite3_api->vtab_in_first #define sqlite3_vtab_in_next sqlite3_api->vtab_in_next /* Version 3.39.0 and later */ #ifndef SQLITE_OMIT_DESERIALIZE #define sqlite3_deserialize sqlite3_api->deserialize #define sqlite3_serialize sqlite3_api->serialize #endif #define sqlite3_db_name sqlite3_api->db_name #endif /* !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) */ #if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) /* This case when the file really is being compiled as a loadable ** extension */ # define SQLITE_EXTENSION_INIT1 const sqlite3_api_routines *sqlite3_api=0; # define SQLITE_EXTENSION_INIT2(v) sqlite3_api=v; |
︙ | ︙ |
Changes to src/sqliteInt.h.
︙ | ︙ | |||
2864 2865 2866 2867 2868 2869 2870 | ** TK_SELECT_COLUMN: Number of columns on the LHS ** TK_SELECT: 1st register of result vector */ ynVar iColumn; /* TK_COLUMN: column index. -1 for rowid. ** TK_VARIABLE: variable number (always >= 1). ** TK_SELECT_COLUMN: column of the result vector */ i16 iAgg; /* Which entry in pAggInfo->aCol[] or ->aFunc[] */ union { | | | > | | < > | | | | | | | | | < > | | | | | | < | 2864 2865 2866 2867 2868 2869 2870 2871 2872 2873 2874 2875 2876 2877 2878 2879 2880 2881 2882 2883 2884 2885 2886 2887 2888 2889 2890 2891 2892 2893 2894 2895 2896 2897 2898 2899 2900 2901 2902 2903 2904 2905 2906 2907 2908 2909 2910 2911 2912 2913 2914 2915 2916 2917 2918 2919 2920 2921 | ** TK_SELECT_COLUMN: Number of columns on the LHS ** TK_SELECT: 1st register of result vector */ ynVar iColumn; /* TK_COLUMN: column index. -1 for rowid. ** TK_VARIABLE: variable number (always >= 1). ** TK_SELECT_COLUMN: column of the result vector */ i16 iAgg; /* Which entry in pAggInfo->aCol[] or ->aFunc[] */ union { int iJoin; /* If EP_OuterON or EP_InnerON, the right table */ int iOfst; /* else: start of token from start of statement */ } w; AggInfo *pAggInfo; /* Used by TK_AGG_COLUMN and TK_AGG_FUNCTION */ union { Table *pTab; /* TK_COLUMN: Table containing column. Can be NULL ** for a column of an index on an expression */ Window *pWin; /* EP_WinFunc: Window/Filter defn for a function */ struct { /* TK_IN, TK_SELECT, and TK_EXISTS */ int iAddr; /* Subroutine entry address */ int regReturn; /* Register used to hold return address */ } sub; } y; }; /* The following are the meanings of bits in the Expr.flags field. ** Value restrictions: ** ** EP_Agg == NC_HasAgg == SF_HasAgg ** EP_Win == NC_HasWin */ #define EP_OuterON 0x000001 /* Originates in ON/USING clause of outer join */ #define EP_InnerON 0x000002 /* Originates in ON/USING of an inner join */ #define EP_Distinct 0x000004 /* Aggregate function with DISTINCT keyword */ #define EP_HasFunc 0x000008 /* Contains one or more functions of any kind */ #define EP_Agg 0x000010 /* Contains one or more aggregate functions */ #define EP_FixedCol 0x000020 /* TK_Column with a known fixed value */ #define EP_VarSelect 0x000040 /* pSelect is correlated, not constant */ #define EP_DblQuoted 0x000080 /* token.z was originally in "..." */ #define EP_InfixFunc 0x000100 /* True for an infix function: LIKE, GLOB, etc */ #define EP_Collate 0x000200 /* Tree contains a TK_COLLATE operator */ #define EP_Commuted 0x000400 /* Comparison operator has been commuted */ #define EP_IntValue 0x000800 /* Integer value contained in u.iValue */ #define EP_xIsSelect 0x001000 /* x.pSelect is valid (otherwise x.pList is) */ #define EP_Skip 0x002000 /* Operator does not contribute to affinity */ #define EP_Reduced 0x004000 /* Expr struct EXPR_REDUCEDSIZE bytes only */ #define EP_Win 0x008000 /* Contains window functions */ #define EP_TokenOnly 0x010000 /* Expr struct EXPR_TOKENONLYSIZE bytes only */ #define EP_MemToken 0x020000 /* Need to sqlite3DbFree() Expr.zToken */ #define EP_IfNullRow 0x040000 /* The TK_IF_NULL_ROW opcode */ #define EP_Unlikely 0x080000 /* unlikely() or likelihood() function */ #define EP_ConstFunc 0x100000 /* A SQLITE_FUNC_CONSTANT or _SLOCHNG function */ #define EP_CanBeNull 0x200000 /* Can be null despite NOT NULL constraint */ #define EP_Subquery 0x400000 /* Tree contains a TK_SELECT operator */ #define EP_Leaf 0x800000 /* Expr.pLeft, .pRight, .u.pSelect all NULL */ #define EP_WinFunc 0x1000000 /* TK_FUNCTION with Expr.y.pWin set */ #define EP_Subrtn 0x2000000 /* Uses Expr.y.sub. TK_IN, _SELECT, or _EXISTS */ #define EP_Quoted 0x4000000 /* TK_ID was originally quoted */ #define EP_Static 0x8000000 /* Held in memory not obtained from malloc() */ #define EP_IsTrue 0x10000000 /* Always has boolean value of TRUE */ #define EP_IsFalse 0x20000000 /* Always has boolean value of FALSE */ |
︙ | ︙ | |||
2930 2931 2932 2933 2934 2935 2936 | /* Macros can be used to test, set, or clear bits in the ** Expr.flags field. */ #define ExprHasProperty(E,P) (((E)->flags&(P))!=0) #define ExprHasAllProperty(E,P) (((E)->flags&(P))==(P)) #define ExprSetProperty(E,P) (E)->flags|=(P) #define ExprClearProperty(E,P) (E)->flags&=~(P) | | | | 2930 2931 2932 2933 2934 2935 2936 2937 2938 2939 2940 2941 2942 2943 2944 2945 | /* Macros can be used to test, set, or clear bits in the ** Expr.flags field. */ #define ExprHasProperty(E,P) (((E)->flags&(P))!=0) #define ExprHasAllProperty(E,P) (((E)->flags&(P))==(P)) #define ExprSetProperty(E,P) (E)->flags|=(P) #define ExprClearProperty(E,P) (E)->flags&=~(P) #define ExprAlwaysTrue(E) (((E)->flags&(EP_OuterON|EP_IsTrue))==EP_IsTrue) #define ExprAlwaysFalse(E) (((E)->flags&(EP_OuterON|EP_IsFalse))==EP_IsFalse) /* Macros used to ensure that the correct members of unions are accessed ** in Expr. */ #define ExprUseUToken(E) (((E)->flags&EP_IntValue)==0) #define ExprUseUValue(E) (((E)->flags&EP_IntValue)!=0) #define ExprUseXList(E) (((E)->flags&EP_xIsSelect)==0) |
︙ | ︙ | |||
3110 3111 3112 3113 3114 3115 3116 3117 3118 3119 3120 3121 3122 3123 | int regResult; /* Registers holding results of a co-routine */ struct { u8 jointype; /* Type of join between this table and the previous */ unsigned notIndexed :1; /* True if there is a NOT INDEXED clause */ unsigned isIndexedBy :1; /* True if there is an INDEXED BY clause */ unsigned isTabFunc :1; /* True if table-valued-function syntax */ unsigned isCorrelated :1; /* True if sub-query is correlated */ unsigned viaCoroutine :1; /* Implemented as a co-routine */ unsigned isRecursive :1; /* True for recursive reference in WITH */ unsigned fromDDL :1; /* Comes from sqlite_schema */ 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 isSynthUsing :1; /* u3.pUsing is synthensized from NATURAL */ | > | 3110 3111 3112 3113 3114 3115 3116 3117 3118 3119 3120 3121 3122 3123 3124 | int regResult; /* Registers holding results of a co-routine */ struct { u8 jointype; /* Type of join between this table and the previous */ unsigned notIndexed :1; /* True if there is a NOT INDEXED clause */ unsigned isIndexedBy :1; /* True if there is an INDEXED BY clause */ unsigned isTabFunc :1; /* True if table-valued-function syntax */ unsigned isCorrelated :1; /* True if sub-query is correlated */ unsigned isMaterialized:1; /* This is a materialized view */ unsigned viaCoroutine :1; /* Implemented as a co-routine */ unsigned isRecursive :1; /* True for recursive reference in WITH */ unsigned fromDDL :1; /* Comes from sqlite_schema */ 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 isSynthUsing :1; /* u3.pUsing is synthensized from NATURAL */ |
︙ | ︙ | |||
5341 5342 5343 5344 5345 5346 5347 | #define IN_INDEX_NOOP 5 /* No table available. Use comparisons */ /* ** Allowed flags for the 3rd parameter to sqlite3FindInIndex(). */ #define IN_INDEX_NOOP_OK 0x0001 /* OK to return IN_INDEX_NOOP */ #define IN_INDEX_MEMBERSHIP 0x0002 /* IN operator used for membership test */ #define IN_INDEX_LOOP 0x0004 /* IN operator used as a loop */ | < | 5342 5343 5344 5345 5346 5347 5348 5349 5350 5351 5352 5353 5354 5355 | #define IN_INDEX_NOOP 5 /* No table available. Use comparisons */ /* ** Allowed flags for the 3rd parameter to sqlite3FindInIndex(). */ #define IN_INDEX_NOOP_OK 0x0001 /* OK to return IN_INDEX_NOOP */ #define IN_INDEX_MEMBERSHIP 0x0002 /* IN operator used for membership test */ #define IN_INDEX_LOOP 0x0004 /* IN operator used as a loop */ int sqlite3FindInIndex(Parse *, Expr *, u32, int*, int*, int*); int sqlite3JournalOpen(sqlite3_vfs *, const char *, sqlite3_file *, int, int); int sqlite3JournalSize(sqlite3_vfs *); #if defined(SQLITE_ENABLE_ATOMIC_WRITE) \ || defined(SQLITE_ENABLE_BATCH_ATOMIC_WRITE) int sqlite3JournalCreate(sqlite3_file *); |
︙ | ︙ |
Changes to src/test4.c.
︙ | ︙ | |||
56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 | /* ** There can be as many as 26 threads running at once. Each is named ** by a capital letter: A, B, C, ..., Y, Z. */ #define N_THREAD 26 static Thread threadset[N_THREAD]; /* ** The main loop for a thread. Threads use busy waiting. */ static void *test_thread_main(void *pArg){ Thread *p = (Thread*)pArg; if( p->db ){ sqlite3_close(p->db); } sqlite3_open(p->zFilename, &p->db); if( SQLITE_OK!=sqlite3_errcode(p->db) ){ p->zErr = strdup(sqlite3_errmsg(p->db)); sqlite3_close(p->db); p->db = 0; } p->pStmt = 0; p->completed = 1; while( p->opnum<=p->completed ) sched_yield(); while( p->xOp ){ if( p->zErr && p->zErr!=p->zStaticErr ){ sqlite3_free(p->zErr); p->zErr = 0; } (*p->xOp)(p); p->completed++; while( p->opnum<=p->completed ) sched_yield(); } if( p->pStmt ){ sqlite3_finalize(p->pStmt); p->pStmt = 0; } if( p->db ){ sqlite3_close(p->db); p->db = 0; } if( p->zErr && p->zErr!=p->zStaticErr ){ sqlite3_free(p->zErr); p->zErr = 0; } p->completed++; #ifndef SQLITE_OMIT_DEPRECATED sqlite3_thread_cleanup(); #endif return 0; } | > > > > > > > > > > | 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 | /* ** There can be as many as 26 threads running at once. Each is named ** by a capital letter: A, B, C, ..., Y, Z. */ #define N_THREAD 26 static Thread threadset[N_THREAD]; static void test_barrier(){ sqlite3_mutex *pMutex = sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_APP1); sqlite3_mutex_enter(pMutex); sqlite3_mutex_leave(pMutex); } /* ** The main loop for a thread. Threads use busy waiting. */ static void *test_thread_main(void *pArg){ Thread *p = (Thread*)pArg; if( p->db ){ sqlite3_close(p->db); } sqlite3_open(p->zFilename, &p->db); if( SQLITE_OK!=sqlite3_errcode(p->db) ){ p->zErr = strdup(sqlite3_errmsg(p->db)); sqlite3_close(p->db); p->db = 0; } p->pStmt = 0; test_barrier(); p->completed = 1; while( p->opnum<=p->completed ) sched_yield(); test_barrier(); while( p->xOp ){ if( p->zErr && p->zErr!=p->zStaticErr ){ sqlite3_free(p->zErr); p->zErr = 0; } (*p->xOp)(p); test_barrier(); p->completed++; while( p->opnum<=p->completed ) sched_yield(); test_barrier(); } if( p->pStmt ){ sqlite3_finalize(p->pStmt); p->pStmt = 0; } if( p->db ){ sqlite3_close(p->db); p->db = 0; } if( p->zErr && p->zErr!=p->zStaticErr ){ sqlite3_free(p->zErr); p->zErr = 0; } test_barrier(); p->completed++; #ifndef SQLITE_OMIT_DEPRECATED sqlite3_thread_cleanup(); #endif return 0; } |
︙ | ︙ | |||
162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 | return TCL_OK; } /* ** Wait for a thread to reach its idle state. */ static void test_thread_wait(Thread *p){ while( p->opnum>p->completed ) sched_yield(); } /* ** Usage: thread_wait ID ** ** Wait on thread ID to reach its idle state. */ | > > | 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 | return TCL_OK; } /* ** Wait for a thread to reach its idle state. */ static void test_thread_wait(Thread *p){ test_barrier(); while( p->opnum>p->completed ) sched_yield(); test_barrier(); } /* ** Usage: thread_wait ID ** ** Wait on thread ID to reach its idle state. */ |
︙ | ︙ | |||
452 453 454 455 456 457 458 459 460 461 462 463 464 465 | Tcl_AppendResult(interp, "no such thread", 0); return TCL_ERROR; } test_thread_wait(&threadset[i]); threadset[i].xOp = do_compile; sqlite3_free(threadset[i].zArg); threadset[i].zArg = sqlite3_mprintf("%s", argv[2]); threadset[i].opnum++; return TCL_OK; } /* ** This procedure runs in the thread to step the virtual machine. */ | > | 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 | Tcl_AppendResult(interp, "no such thread", 0); return TCL_ERROR; } test_thread_wait(&threadset[i]); threadset[i].xOp = do_compile; sqlite3_free(threadset[i].zArg); threadset[i].zArg = sqlite3_mprintf("%s", argv[2]); test_barrier(); threadset[i].opnum++; return TCL_OK; } /* ** This procedure runs in the thread to step the virtual machine. */ |
︙ | ︙ | |||
503 504 505 506 507 508 509 510 511 512 513 514 515 516 | if( i<0 ) return TCL_ERROR; if( !threadset[i].busy ){ Tcl_AppendResult(interp, "no such thread", 0); return TCL_ERROR; } test_thread_wait(&threadset[i]); threadset[i].xOp = do_step; threadset[i].opnum++; return TCL_OK; } /* ** This procedure runs in the thread to finalize a virtual machine. */ | > | 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 | if( i<0 ) return TCL_ERROR; if( !threadset[i].busy ){ Tcl_AppendResult(interp, "no such thread", 0); return TCL_ERROR; } test_thread_wait(&threadset[i]); threadset[i].xOp = do_step; test_barrier(); threadset[i].opnum++; return TCL_OK; } /* ** This procedure runs in the thread to finalize a virtual machine. */ |
︙ | ︙ | |||
547 548 549 550 551 552 553 554 555 556 557 558 559 560 | Tcl_AppendResult(interp, "no such thread", 0); return TCL_ERROR; } test_thread_wait(&threadset[i]); threadset[i].xOp = do_finalize; sqlite3_free(threadset[i].zArg); threadset[i].zArg = 0; threadset[i].opnum++; return TCL_OK; } /* ** Usage: thread_swap ID ID ** | > | 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 | Tcl_AppendResult(interp, "no such thread", 0); return TCL_ERROR; } test_thread_wait(&threadset[i]); threadset[i].xOp = do_finalize; sqlite3_free(threadset[i].zArg); threadset[i].zArg = 0; test_barrier(); threadset[i].opnum++; return TCL_OK; } /* ** Usage: thread_swap ID ID ** |
︙ | ︙ |
Changes to src/treeview.c.
︙ | ︙ | |||
485 486 487 488 489 490 491 | return; } if( pExpr->flags || pExpr->affExpr || pExpr->vvaFlags ){ StrAccum x; sqlite3StrAccumInit(&x, 0, zFlgs, sizeof(zFlgs), 0); sqlite3_str_appendf(&x, " fg.af=%x.%c", pExpr->flags, pExpr->affExpr ? pExpr->affExpr : 'n'); | | > > > | | 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 | return; } if( pExpr->flags || pExpr->affExpr || pExpr->vvaFlags ){ StrAccum x; sqlite3StrAccumInit(&x, 0, zFlgs, sizeof(zFlgs), 0); sqlite3_str_appendf(&x, " fg.af=%x.%c", pExpr->flags, pExpr->affExpr ? pExpr->affExpr : 'n'); if( ExprHasProperty(pExpr, EP_OuterON) ){ sqlite3_str_appendf(&x, " outer.iJoin=%d", pExpr->w.iJoin); } if( ExprHasProperty(pExpr, EP_InnerON) ){ sqlite3_str_appendf(&x, " inner.iJoin=%d", pExpr->w.iJoin); } if( ExprHasProperty(pExpr, EP_FromDDL) ){ sqlite3_str_appendf(&x, " DDL"); } if( ExprHasVVAProperty(pExpr, EP_Immutable) ){ sqlite3_str_appendf(&x, " IMMUTABLE"); } |
︙ | ︙ |
Changes to src/trigger.c.
︙ | ︙ | |||
510 511 512 513 514 515 516 | ** Construct a trigger step that implements an UPDATE statement and return ** a pointer to that trigger step. The parser calls this routine when it ** sees an UPDATE statement inside the body of a CREATE TRIGGER. */ TriggerStep *sqlite3TriggerUpdateStep( Parse *pParse, /* Parser */ Token *pTableName, /* Name of the table to be updated */ | | | 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 | ** Construct a trigger step that implements an UPDATE statement and return ** a pointer to that trigger step. The parser calls this routine when it ** sees an UPDATE statement inside the body of a CREATE TRIGGER. */ TriggerStep *sqlite3TriggerUpdateStep( Parse *pParse, /* Parser */ Token *pTableName, /* Name of the table to be updated */ SrcList *pFrom, /* FROM clause for an UPDATE-FROM, or NULL */ ExprList *pEList, /* The SET clause: list of column and new values */ Expr *pWhere, /* The WHERE clause */ u8 orconf, /* The conflict algorithm. (OE_Abort, OE_Ignore, etc) */ const char *zStart, /* Start of SQL text */ const char *zEnd /* End of SQL text */ ){ sqlite3 *db = pParse->db; |
︙ | ︙ | |||
846 847 848 849 850 851 852 853 854 855 856 857 858 859 | Schema *pSchema = pStep->pTrig->pSchema; pSrc->a[0].zName = zName; if( pSchema!=db->aDb[1].pSchema ){ pSrc->a[0].pSchema = pSchema; } if( pStep->pFrom ){ SrcList *pDup = sqlite3SrcListDup(db, pStep->pFrom, 0); pSrc = sqlite3SrcListAppendList(pParse, pSrc, pDup); } }else{ sqlite3DbFree(db, zName); } return pSrc; } | > > > > > > > > | 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 | Schema *pSchema = pStep->pTrig->pSchema; pSrc->a[0].zName = zName; if( pSchema!=db->aDb[1].pSchema ){ pSrc->a[0].pSchema = pSchema; } if( pStep->pFrom ){ SrcList *pDup = sqlite3SrcListDup(db, pStep->pFrom, 0); if( pDup && pDup->nSrc>1 && !IN_RENAME_OBJECT ){ Select *pSubquery; Token as; pSubquery = sqlite3SelectNew(pParse,0,pDup,0,0,0,0,SF_NestedFrom,0); as.n = 0; as.z = 0; pDup = sqlite3SrcListAppendFromTerm(pParse,0,0,0,&as,pSubquery,0); } pSrc = sqlite3SrcListAppendList(pParse, pSrc, pDup); } }else{ sqlite3DbFree(db, zName); } return pSrc; } |
︙ | ︙ |
Changes to src/vdbeaux.c.
︙ | ︙ | |||
1572 1573 1574 1575 1576 1577 1578 | if( c=='P' ){ c = zSynopsis[++ii]; if( c=='4' ){ sqlite3_str_appendall(&x, zP4); }else if( c=='X' ){ if( pOp->zComment && pOp->zComment[0] ){ sqlite3_str_appendall(&x, pOp->zComment); | < < < | | > | 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 | if( c=='P' ){ c = zSynopsis[++ii]; if( c=='4' ){ sqlite3_str_appendall(&x, zP4); }else if( c=='X' ){ if( pOp->zComment && pOp->zComment[0] ){ sqlite3_str_appendall(&x, pOp->zComment); seenCom = 1; break; } }else{ int v1 = translateP(c, pOp); int v2; if( strncmp(zSynopsis+ii+1, "@P", 2)==0 ){ ii += 3; v2 = translateP(zSynopsis[ii], pOp); if( strncmp(zSynopsis+ii+1,"+1",2)==0 ){ |
︙ | ︙ |
Changes to src/where.c.
︙ | ︙ | |||
326 327 328 329 330 331 332 | for(pTerm=pWC->a+k; k<pWC->nTerm; k++, pTerm++){ assert( (pTerm->eOperator & (WO_OR|WO_AND))==0 || pTerm->leftCursor<0 ); if( pTerm->leftCursor==iCur && pTerm->u.x.leftColumn==iColumn && (iColumn!=XN_EXPR || sqlite3ExprCompareSkip(pTerm->pExpr->pLeft, pScan->pIdxExpr,iCur)==0) | | | 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 | for(pTerm=pWC->a+k; k<pWC->nTerm; k++, pTerm++){ assert( (pTerm->eOperator & (WO_OR|WO_AND))==0 || pTerm->leftCursor<0 ); if( pTerm->leftCursor==iCur && pTerm->u.x.leftColumn==iColumn && (iColumn!=XN_EXPR || sqlite3ExprCompareSkip(pTerm->pExpr->pLeft, pScan->pIdxExpr,iCur)==0) && (pScan->iEquiv<=1 || !ExprHasProperty(pTerm->pExpr, EP_OuterON)) ){ if( (pTerm->eOperator & WO_EQUIV)!=0 && pScan->nEquiv<ArraySize(pScan->aiCur) && (pX = whereRightSubexprIsColumn(pTerm->pExpr))!=0 ){ int j; for(j=0; j<pScan->nEquiv; j++){ |
︙ | ︙ | |||
753 754 755 756 757 758 759 | const SrcItem *pSrc, /* Table we are trying to access */ const Bitmask notReady /* Tables in outer loops of the join */ ){ 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 | | | 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 | const SrcItem *pSrc, /* Table we are trying to access */ const Bitmask notReady /* Tables in outer loops of the join */ ){ 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; } |
︙ | ︙ | |||
1177 1178 1179 1180 1181 1182 1183 | 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 JOIN. See tag-20191211-001 for the ** equivalent restriction for ordinary tables. */ if( (pSrc->fg.jointype & (JT_LEFT|JT_LTORJ))!=0 | | | 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 | 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 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; } nTerm++; pTerm->wtFlags |= TERM_OK; } |
︙ | ︙ | |||
2055 2056 2057 2058 2059 2060 2061 | sqlite3DebugPrintf("TERM-%-3d NULL\n", iTerm); }else{ char zType[8]; char zLeft[50]; memcpy(zType, "....", 5); if( pTerm->wtFlags & TERM_VIRTUAL ) zType[0] = 'V'; if( pTerm->eOperator & WO_EQUIV ) zType[1] = 'E'; | | | 2055 2056 2057 2058 2059 2060 2061 2062 2063 2064 2065 2066 2067 2068 2069 | sqlite3DebugPrintf("TERM-%-3d NULL\n", iTerm); }else{ char zType[8]; char zLeft[50]; memcpy(zType, "....", 5); if( pTerm->wtFlags & TERM_VIRTUAL ) zType[0] = 'V'; if( pTerm->eOperator & WO_EQUIV ) zType[1] = 'E'; if( ExprHasProperty(pTerm->pExpr, EP_OuterON) ) zType[2] = 'L'; if( pTerm->wtFlags & TERM_CODED ) zType[3] = 'C'; if( pTerm->eOperator & WO_SINGLE ){ assert( (pTerm->eOperator & (WO_OR|WO_AND))==0 ); sqlite3_snprintf(sizeof(zLeft),zLeft,"left={%d:%d}", pTerm->leftCursor, pTerm->u.x.leftColumn); }else if( (pTerm->eOperator & WO_OR)!=0 && pTerm->u.pOrInfo!=0 ){ sqlite3_snprintf(sizeof(zLeft),zLeft,"indexable=0x%llx", |
︙ | ︙ | |||
2832 2833 2834 2835 2836 2837 2838 | if( pTerm->wtFlags & TERM_LIKEOPT && pTerm->eOperator==WO_LT ) continue; /* 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 | | | 2832 2833 2834 2835 2836 2837 2838 2839 2840 2841 2842 2843 2844 2845 2846 | if( pTerm->wtFlags & TERM_LIKEOPT && pTerm->eOperator==WO_LT ) continue; /* 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; } if( IsUniqueIndex(pProbe) && saved_nEq==pProbe->nKeyCol-1 ){ pBuilder->bldFlags1 |= SQLITE_BLDF1_UNIQUE; }else{ |
︙ | ︙ | |||
3201 3202 3203 3204 3205 3206 3207 | if( !whereUsablePartialIndex(iTab,isLeft,pWC,pWhere->pLeft) ) return 0; pWhere = pWhere->pRight; } if( pParse->db->flags & SQLITE_EnableQPSG ) pParse = 0; for(i=0, pTerm=pWC->a; i<pWC->nTerm; i++, pTerm++){ Expr *pExpr; pExpr = pTerm->pExpr; | | | | 3201 3202 3203 3204 3205 3206 3207 3208 3209 3210 3211 3212 3213 3214 3215 3216 | if( !whereUsablePartialIndex(iTab,isLeft,pWC,pWhere->pLeft) ) return 0; pWhere = pWhere->pRight; } if( pParse->db->flags & SQLITE_EnableQPSG ) pParse = 0; for(i=0, pTerm=pWC->a; i<pWC->nTerm; i++, pTerm++){ Expr *pExpr; pExpr = pTerm->pExpr; if( (!ExprHasProperty(pExpr, EP_OuterON) || pExpr->w.iJoin==iTab) && (isLeft==0 || ExprHasProperty(pExpr, EP_OuterON)) && sqlite3ExprImpliesExpr(pParse, pExpr, pWhere, iTab) && (pTerm->wtFlags & TERM_VNULL)==0 ){ return 1; } } return 0; |
︙ | ︙ | |||
3478 3479 3480 3481 3482 3483 3484 | } } pNew->rRun = sqlite3LogEstAdd(pNew->rRun, nLookup); } ApplyCostMultiplier(pNew->rRun, pTab->costMult); whereLoopOutputAdjust(pWC, pNew, rSize); | > > > > > > | > | 3478 3479 3480 3481 3482 3483 3484 3485 3486 3487 3488 3489 3490 3491 3492 3493 3494 3495 3496 3497 3498 3499 | } } pNew->rRun = sqlite3LogEstAdd(pNew->rRun, nLookup); } ApplyCostMultiplier(pNew->rRun, pTab->costMult); whereLoopOutputAdjust(pWC, pNew, rSize); if( (pSrc->fg.jointype & JT_RIGHT)!=0 && pProbe->aColExpr ){ /* Do not do an SCAN of a index-on-expression in a RIGHT JOIN ** because the cursor used to access the index might not be ** positioned to the correct row during the right-join no-match ** loop. */ }else{ rc = whereLoopInsert(pBuilder, pNew); } pNew->nOut = rSize; if( rc ) break; } } pBuilder->bldFlags1 = 0; rc = whereLoopAddBtreeIndex(pBuilder, pSrc, pProbe, 0); |
︙ | ︙ | |||
3653 3654 3655 3656 3657 3658 3659 3660 3661 3662 3663 3664 3665 3666 | ** (2) Multiple outputs from a single IN value will not merge ** together. */ pIdxInfo->orderByConsumed = 0; pIdxInfo->idxFlags &= ~SQLITE_INDEX_SCAN_UNIQUE; *pbIn = 1; assert( (mExclude & WO_IN)==0 ); } if( isLimitTerm(pTerm) && *pbIn ){ /* If there is an IN(...) term handled as an == (separate call to ** xFilter for each value on the RHS of the IN) and a LIMIT or ** OFFSET term handled as well, the plan is unusable. Set output ** variable *pbRetryLimit to true to tell the caller to retry with ** LIMIT and OFFSET disabled. */ if( pIdxInfo->needToFreeIdxStr ){ | > | 3660 3661 3662 3663 3664 3665 3666 3667 3668 3669 3670 3671 3672 3673 3674 | ** (2) Multiple outputs from a single IN value will not merge ** together. */ pIdxInfo->orderByConsumed = 0; pIdxInfo->idxFlags &= ~SQLITE_INDEX_SCAN_UNIQUE; *pbIn = 1; assert( (mExclude & WO_IN)==0 ); } assert( pbRetryLimit || !isLimitTerm(pTerm) ); if( isLimitTerm(pTerm) && *pbIn ){ /* If there is an IN(...) term handled as an == (separate call to ** xFilter for each value on the RHS of the IN) and a LIMIT or ** OFFSET term handled as well, the plan is unusable. Set output ** variable *pbRetryLimit to true to tell the caller to retry with ** LIMIT and OFFSET disabled. */ if( pIdxInfo->needToFreeIdxStr ){ |
︙ | ︙ | |||
4126 4127 4128 4129 4130 4131 4132 4133 4134 4135 4136 4137 4138 4139 4140 4141 4142 4143 | Bitmask mPrior = 0; int iTab; SrcList *pTabList = pWInfo->pTabList; SrcItem *pItem; SrcItem *pEnd = &pTabList->a[pWInfo->nLevel]; sqlite3 *db = pWInfo->pParse->db; int rc = SQLITE_OK; WhereLoop *pNew; /* Loop over the tables in the join, from left to right */ pNew = pBuilder->pNew; whereLoopInit(pNew); pBuilder->iPlanLimit = SQLITE_QUERY_PLANNER_LIMIT; for(iTab=0, pItem=pTabList->a; pItem<pEnd; iTab++, pItem++){ Bitmask mUnusable = 0; pNew->iTab = iTab; pBuilder->iPlanLimit += SQLITE_QUERY_PLANNER_LIMIT_INCR; pNew->maskSelf = sqlite3WhereGetMask(&pWInfo->sMaskSet, pItem->iCursor); | > > | | | > > > | 4134 4135 4136 4137 4138 4139 4140 4141 4142 4143 4144 4145 4146 4147 4148 4149 4150 4151 4152 4153 4154 4155 4156 4157 4158 4159 4160 4161 4162 4163 4164 4165 4166 4167 | Bitmask mPrior = 0; int iTab; SrcList *pTabList = pWInfo->pTabList; SrcItem *pItem; SrcItem *pEnd = &pTabList->a[pWInfo->nLevel]; sqlite3 *db = pWInfo->pParse->db; int rc = SQLITE_OK; int bFirstPastRJ = 0; WhereLoop *pNew; /* Loop over the tables in the join, from left to right */ pNew = pBuilder->pNew; whereLoopInit(pNew); pBuilder->iPlanLimit = SQLITE_QUERY_PLANNER_LIMIT; for(iTab=0, pItem=pTabList->a; pItem<pEnd; iTab++, pItem++){ Bitmask mUnusable = 0; pNew->iTab = iTab; pBuilder->iPlanLimit += SQLITE_QUERY_PLANNER_LIMIT_INCR; pNew->maskSelf = sqlite3WhereGetMask(&pWInfo->sMaskSet, pItem->iCursor); if( bFirstPastRJ || (pItem->fg.jointype & (JT_OUTER|JT_CROSS))!=0 ){ /* Add prerequisites to prevent reordering of FROM clause terms ** across CROSS joins and outer joins. The bFirstPastRJ boolean ** prevents the right operand of a RIGHT JOIN from being swapped with ** other elements even further to the right. */ mPrereq |= mPrior; bFirstPastRJ = (pItem->fg.jointype & JT_RIGHT)!=0; } #ifndef SQLITE_OMIT_VIRTUALTABLE if( IsVirtual(pItem->pTab) ){ SrcItem *p; for(p=&pItem[1]; p<pEnd; p++){ if( mUnusable || (p->fg.jointype & (JT_OUTER|JT_CROSS)) ){ mUnusable |= sqlite3WhereGetMask(&pWInfo->sMaskSet, p->iCursor); |
︙ | ︙ | |||
5210 5211 5212 5213 5214 5215 5216 | ){ continue; } if( (tabUsed & pLoop->maskSelf)!=0 ) continue; pEnd = pWInfo->sWC.a + pWInfo->sWC.nTerm; for(pTerm=pWInfo->sWC.a; pTerm<pEnd; pTerm++){ if( (pTerm->prereqAll & pLoop->maskSelf)!=0 ){ | | | 5223 5224 5225 5226 5227 5228 5229 5230 5231 5232 5233 5234 5235 5236 5237 | ){ continue; } if( (tabUsed & pLoop->maskSelf)!=0 ) continue; pEnd = pWInfo->sWC.a + pWInfo->sWC.nTerm; for(pTerm=pWInfo->sWC.a; pTerm<pEnd; pTerm++){ if( (pTerm->prereqAll & pLoop->maskSelf)!=0 ){ if( !ExprHasProperty(pTerm->pExpr, EP_OuterON) || pTerm->pExpr->w.iJoin!=pItem->iCursor ){ break; } } } if( pTerm<pEnd ) continue; |
︙ | ︙ | |||
5836 5837 5838 5839 5840 5841 5842 5843 5844 5845 5846 5847 5848 5849 | }else if( iAuxArg && (wctrlFlags & WHERE_OR_SUBCLAUSE)!=0 ){ iIndexCur = iAuxArg; op = OP_ReopenIdx; }else{ iIndexCur = pParse->nTab++; } pLevel->iIdxCur = iIndexCur; assert( pIx->pSchema==pTab->pSchema ); assert( iIndexCur>=0 ); if( op ){ sqlite3VdbeAddOp3(v, op, iIndexCur, pIx->tnum, iDb); sqlite3VdbeSetP4KeyInfo(pParse, pIx); if( (pLoop->wsFlags & WHERE_CONSTRAINT)!=0 && (pLoop->wsFlags & (WHERE_COLUMN_RANGE|WHERE_SKIPSCAN))==0 | > | 5849 5850 5851 5852 5853 5854 5855 5856 5857 5858 5859 5860 5861 5862 5863 | }else if( iAuxArg && (wctrlFlags & WHERE_OR_SUBCLAUSE)!=0 ){ iIndexCur = iAuxArg; op = OP_ReopenIdx; }else{ iIndexCur = pParse->nTab++; } pLevel->iIdxCur = iIndexCur; assert( pIx!=0 ); assert( pIx->pSchema==pTab->pSchema ); assert( iIndexCur>=0 ); if( op ){ sqlite3VdbeAddOp3(v, op, iIndexCur, pIx->tnum, iDb); sqlite3VdbeSetP4KeyInfo(pParse, pIx); if( (pLoop->wsFlags & WHERE_CONSTRAINT)!=0 && (pLoop->wsFlags & (WHERE_COLUMN_RANGE|WHERE_SKIPSCAN))==0 |
︙ | ︙ | |||
5911 5912 5913 5914 5915 5916 5917 5918 5919 5920 5921 5922 5923 5924 5925 5926 5927 | /* Generate the code to do the search. Each iteration of the for ** loop below generates code for a single nested loop of the VM ** program. */ for(ii=0; ii<nTabList; ii++){ int addrExplain; int wsFlags; if( pParse->nErr ) goto whereBeginError; pLevel = &pWInfo->a[ii]; wsFlags = pLevel->pWLoop->wsFlags; if( (wsFlags & (WHERE_AUTO_INDEX|WHERE_BLOOMFILTER))!=0 ){ if( (wsFlags & WHERE_AUTO_INDEX)!=0 ){ #ifndef SQLITE_OMIT_AUTOMATIC_INDEX constructAutomaticIndex(pParse, &pWInfo->sWC, &pTabList->a[pLevel->iFrom], notReady, pLevel); #endif }else{ | > > > > > > > > > > > | 5925 5926 5927 5928 5929 5930 5931 5932 5933 5934 5935 5936 5937 5938 5939 5940 5941 5942 5943 5944 5945 5946 5947 5948 5949 5950 5951 5952 | /* Generate the code to do the search. Each iteration of the for ** loop below generates code for a single nested loop of the VM ** program. */ for(ii=0; ii<nTabList; ii++){ int addrExplain; int wsFlags; SrcItem *pSrc; if( pParse->nErr ) goto whereBeginError; pLevel = &pWInfo->a[ii]; wsFlags = pLevel->pWLoop->wsFlags; pSrc = &pTabList->a[pLevel->iFrom]; if( pSrc->fg.isMaterialized ){ if( pSrc->fg.isCorrelated ){ sqlite3VdbeAddOp2(v, OP_Gosub, pSrc->regReturn, pSrc->addrFillSub); }else{ int iOnce = sqlite3VdbeAddOp0(v, OP_Once); VdbeCoverage(v); sqlite3VdbeAddOp2(v, OP_Gosub, pSrc->regReturn, pSrc->addrFillSub); sqlite3VdbeJumpHere(v, iOnce); } } if( (wsFlags & (WHERE_AUTO_INDEX|WHERE_BLOOMFILTER))!=0 ){ if( (wsFlags & WHERE_AUTO_INDEX)!=0 ){ #ifndef SQLITE_OMIT_AUTOMATIC_INDEX constructAutomaticIndex(pParse, &pWInfo->sWC, &pTabList->a[pLevel->iFrom], notReady, pLevel); #endif }else{ |
︙ | ︙ | |||
6164 6165 6166 6167 6168 6169 6170 6171 6172 6173 6174 6175 6176 6177 | sqlite3VdbeJumpHere(v, addr); } VdbeModuleComment((v, "End WHERE-loop%d: %s", i, pWInfo->pTabList->a[pLevel->iFrom].pTab->zName)); } assert( pWInfo->nLevel<=pTabList->nSrc ); for(i=0, pLevel=pWInfo->a; i<pWInfo->nLevel; i++, pLevel++){ int k, last; VdbeOp *pOp, *pLastOp; Index *pIdx = 0; SrcItem *pTabItem = &pTabList->a[pLevel->iFrom]; Table *pTab = pTabItem->pTab; assert( pTab!=0 ); | > | 6189 6190 6191 6192 6193 6194 6195 6196 6197 6198 6199 6200 6201 6202 6203 | sqlite3VdbeJumpHere(v, addr); } VdbeModuleComment((v, "End WHERE-loop%d: %s", i, pWInfo->pTabList->a[pLevel->iFrom].pTab->zName)); } assert( pWInfo->nLevel<=pTabList->nSrc ); if( pWInfo->pExprMods ) whereUndoExprMods(pWInfo); for(i=0, pLevel=pWInfo->a; i<pWInfo->nLevel; i++, pLevel++){ int k, last; VdbeOp *pOp, *pLastOp; Index *pIdx = 0; SrcItem *pTabItem = &pTabList->a[pLevel->iFrom]; Table *pTab = pTabItem->pTab; assert( pTab!=0 ); |
︙ | ︙ | |||
6300 6301 6302 6303 6304 6305 6306 | /* The "break" point is here, just past the end of the outer loop. ** Set it. */ sqlite3VdbeResolveLabel(v, pWInfo->iBreak); /* Final cleanup */ | < | 6326 6327 6328 6329 6330 6331 6332 6333 6334 6335 6336 | /* The "break" point is here, just past the end of the outer loop. ** Set it. */ sqlite3VdbeResolveLabel(v, pWInfo->iBreak); /* Final cleanup */ pParse->nQueryLoop = pWInfo->savedNQueryLoop; whereInfoFree(db, pWInfo); return; } |
Changes to src/whereInt.h.
︙ | ︙ | |||
37 38 39 40 41 42 43 | /* ** This object is a header on a block of allocated memory that will be ** automatically freed when its WInfo oject is destructed. */ struct WhereMemBlock { WhereMemBlock *pNext; /* Next block in the chain */ | | | 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | /* ** This object is a header on a block of allocated memory that will be ** automatically freed when its WInfo oject is destructed. */ struct WhereMemBlock { WhereMemBlock *pNext; /* Next block in the chain */ u64 sz; /* Bytes of space */ }; /* ** Extra information attached to a WhereLevel that is a RIGHT JOIN. */ struct WhereRightJoin { int iMatch; /* Cursor used to determine prior matched rows */ |
︙ | ︙ |
Changes to src/wherecode.c.
︙ | ︙ | |||
346 347 348 349 350 351 352 | ** a conditional such that is only evaluated on the second pass of a ** LIKE-optimization loop, when scanning BLOBs instead of strings. */ static void disableTerm(WhereLevel *pLevel, WhereTerm *pTerm){ int nLoop = 0; assert( pTerm!=0 ); while( (pTerm->wtFlags & TERM_CODED)==0 | | | 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 | ** a conditional such that is only evaluated on the second pass of a ** LIKE-optimization loop, when scanning BLOBs instead of strings. */ static void disableTerm(WhereLevel *pLevel, WhereTerm *pTerm){ int nLoop = 0; assert( pTerm!=0 ); while( (pTerm->wtFlags & TERM_CODED)==0 && (pLevel->iLeftJoin==0 || ExprHasProperty(pTerm->pExpr, EP_OuterON)) && (pLevel->notReady & pTerm->prereqAll)==0 ){ if( nLoop && (pTerm->wtFlags & TERM_LIKE)!=0 ){ pTerm->wtFlags |= TERM_LIKECOND; }else{ pTerm->wtFlags |= TERM_CODED; } |
︙ | ︙ | |||
619 620 621 622 623 624 625 | aiMap = (int*)sqlite3DbMallocZero(pParse->db, sizeof(int)*nEq); eType = sqlite3FindInIndex(pParse, pX, IN_INDEX_LOOP, 0, aiMap,&iTab); pExpr->iTable = iTab; } sqlite3ExprDelete(db, pX); }else{ aiMap = (int*)sqlite3DbMallocZero(pParse->db, sizeof(int)*nEq); | | < | 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 | aiMap = (int*)sqlite3DbMallocZero(pParse->db, sizeof(int)*nEq); eType = sqlite3FindInIndex(pParse, pX, IN_INDEX_LOOP, 0, aiMap,&iTab); pExpr->iTable = iTab; } sqlite3ExprDelete(db, pX); }else{ aiMap = (int*)sqlite3DbMallocZero(pParse->db, sizeof(int)*nEq); eType = sqlite3FindInIndex(pParse, pX, IN_INDEX_LOOP, 0, aiMap, &iTab); } pX = pExpr; } if( eType==IN_INDEX_INDEX_DESC ){ testcase( bRev ); bRev = !bRev; |
︙ | ︙ | |||
1068 1069 1070 1071 1072 1073 1074 | ** ** WHERE 1 = (t2.c IS NULL) ** ** are also excluded. See codeCursorHintIsOrFunction() for details. */ if( pTabItem->fg.jointype & JT_LEFT ){ Expr *pExpr = pTerm->pExpr; | | | | 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 | ** ** WHERE 1 = (t2.c IS NULL) ** ** are also excluded. See codeCursorHintIsOrFunction() for details. */ if( pTabItem->fg.jointype & JT_LEFT ){ Expr *pExpr = pTerm->pExpr; if( !ExprHasProperty(pExpr, EP_OuterON) || pExpr->w.iJoin!=pTabItem->iCursor ){ sWalker.eCode = 0; sWalker.xExprCallback = codeCursorHintIsOrFunction; sqlite3WalkExpr(&sWalker, pTerm->pExpr); if( sWalker.eCode ) continue; } }else{ if( ExprHasProperty(pTerm->pExpr, EP_OuterON) ) continue; } /* All terms in pWLoop->aLTerm[] except pEndRange are used to initialize ** the cursor. These terms are not needed as hints for a pure range ** scan (that has no == terms) so omit them. */ if( pLoop->u.btree.nEq==0 && pTerm!=pEndRange ){ for(j=0; j<pLoop->nLTerm && pLoop->aLTerm[j]!=pTerm; j++){} |
︙ | ︙ | |||
2408 2409 2410 2411 2412 2413 2414 | WhereTerm *pOrTerm = &pOrWc->a[ii]; if( pOrTerm->leftCursor==iCur || (pOrTerm->eOperator & WO_AND)!=0 ){ WhereInfo *pSubWInfo; /* Info for single OR-term scan */ Expr *pOrExpr = pOrTerm->pExpr; /* Current OR clause term */ Expr *pDelete; /* Local copy of OR clause term */ int jmp1 = 0; /* Address of jump operation */ testcase( (pTabItem[0].fg.jointype & JT_LEFT)!=0 | | | 2407 2408 2409 2410 2411 2412 2413 2414 2415 2416 2417 2418 2419 2420 2421 | WhereTerm *pOrTerm = &pOrWc->a[ii]; if( pOrTerm->leftCursor==iCur || (pOrTerm->eOperator & WO_AND)!=0 ){ WhereInfo *pSubWInfo; /* Info for single OR-term scan */ Expr *pOrExpr = pOrTerm->pExpr; /* Current OR clause term */ Expr *pDelete; /* Local copy of OR clause term */ int jmp1 = 0; /* Address of jump operation */ testcase( (pTabItem[0].fg.jointype & JT_LEFT)!=0 && !ExprHasProperty(pOrExpr, EP_OuterON) ); /* See TH3 vtab25.400 and ticket 614b25314c766238 */ pDelete = pOrExpr = sqlite3ExprDup(db, pOrExpr, 0); if( db->mallocFailed ){ sqlite3ExprDelete(db, pDelete); continue; } if( pAndExpr ){ |
︙ | ︙ | |||
2616 2617 2618 2619 2620 2621 2622 | testcase( pWInfo->untestedTerms==0 && (pWInfo->wctrlFlags & WHERE_OR_SUBCLAUSE)!=0 ); pWInfo->untestedTerms = 1; continue; } pE = pTerm->pExpr; assert( pE!=0 ); | | | > > > > > | > | | | > | 2615 2616 2617 2618 2619 2620 2621 2622 2623 2624 2625 2626 2627 2628 2629 2630 2631 2632 2633 2634 2635 2636 2637 2638 2639 2640 2641 | testcase( pWInfo->untestedTerms==0 && (pWInfo->wctrlFlags & WHERE_OR_SUBCLAUSE)!=0 ); pWInfo->untestedTerms = 1; continue; } pE = pTerm->pExpr; assert( pE!=0 ); if( pTabItem->fg.jointype & (JT_LEFT|JT_LTORJ|JT_RIGHT) ){ if( !ExprHasProperty(pE,EP_OuterON|EP_InnerON) ){ /* Defer processing WHERE clause constraints until after outer ** join processing. tag-20220513a */ continue; }else{ Bitmask m = sqlite3WhereGetMask(&pWInfo->sMaskSet, pE->w.iJoin); if( m & pLevel->notReady ){ /* An ON clause that is not ripe */ continue; } } } if( iLoop==1 && !sqlite3ExprCoveredByIndex(pE, pLevel->iTabCur, pIdx) ){ iNext = 2; continue; } if( iLoop<3 && (pTerm->wtFlags & TERM_VARSELECT) ){ if( iNext==0 ) iNext = 3; continue; |
︙ | ︙ | |||
2680 2681 2682 2683 2684 2685 2686 | for(pTerm=pWC->a, j=pWC->nBase; j>0; j--, pTerm++){ Expr *pE, sEAlt; WhereTerm *pAlt; if( pTerm->wtFlags & (TERM_VIRTUAL|TERM_CODED) ) continue; if( (pTerm->eOperator & (WO_EQ|WO_IS))==0 ) continue; if( (pTerm->eOperator & WO_EQUIV)==0 ) continue; if( pTerm->leftCursor!=iCur ) continue; | | | | 2686 2687 2688 2689 2690 2691 2692 2693 2694 2695 2696 2697 2698 2699 2700 2701 2702 2703 2704 2705 2706 2707 2708 | for(pTerm=pWC->a, j=pWC->nBase; j>0; j--, pTerm++){ Expr *pE, sEAlt; WhereTerm *pAlt; if( pTerm->wtFlags & (TERM_VIRTUAL|TERM_CODED) ) continue; if( (pTerm->eOperator & (WO_EQ|WO_IS))==0 ) continue; if( (pTerm->eOperator & WO_EQUIV)==0 ) continue; if( pTerm->leftCursor!=iCur ) continue; if( pTabItem->fg.jointype & (JT_LEFT|JT_LTORJ|JT_RIGHT) ) continue; pE = pTerm->pExpr; #ifdef WHERETRACE_ENABLED /* 0x800 */ if( sqlite3WhereTrace & 0x800 ){ sqlite3DebugPrintf("Coding transitive constraint:\n"); sqlite3WhereTermPrint(pTerm, pWC->nTerm-j); } #endif assert( !ExprHasProperty(pE, EP_OuterON) ); assert( (pTerm->prereqRight & pLevel->notReady)!=0 ); assert( (pTerm->eOperator & (WO_OR|WO_AND))==0 ); pAlt = sqlite3WhereFindTerm(pWC, iCur, pTerm->u.x.leftColumn, notReady, WO_EQ|WO_IN|WO_IS, 0); if( pAlt==0 ) continue; if( pAlt->wtFlags & (TERM_CODED) ) continue; if( (pAlt->eOperator & WO_IN) |
︙ | ︙ | |||
2759 2760 2761 2762 2763 2764 2765 | /* For a LEFT OUTER JOIN, generate code that will record the fact that ** at least one row of the right table has matched the left table. */ if( pLevel->iLeftJoin ){ pLevel->addrFirst = sqlite3VdbeCurrentAddr(v); sqlite3VdbeAddOp2(v, OP_Integer, 1, pLevel->iLeftJoin); VdbeComment((v, "record LEFT JOIN hit")); | < < < < | < < < < < < < > > > > > > > > > > > > > > > > > > > > > | 2765 2766 2767 2768 2769 2770 2771 2772 2773 2774 2775 2776 2777 2778 2779 2780 2781 2782 2783 2784 2785 2786 2787 2788 2789 2790 2791 2792 2793 2794 2795 2796 2797 2798 2799 2800 2801 2802 2803 2804 2805 2806 2807 2808 2809 2810 2811 2812 2813 2814 2815 | /* For a LEFT OUTER JOIN, generate code that will record the fact that ** at least one row of the right table has matched the left table. */ if( pLevel->iLeftJoin ){ pLevel->addrFirst = sqlite3VdbeCurrentAddr(v); sqlite3VdbeAddOp2(v, OP_Integer, 1, pLevel->iLeftJoin); VdbeComment((v, "record LEFT JOIN hit")); if( pLevel->pRJ==0 ){ goto code_outer_join_constraints; /* WHERE clause constraints */ } } if( pLevel->pRJ ){ /* Create a subroutine used to process all interior loops and code ** of the RIGHT JOIN. During normal operation, the subroutine will ** be in-line with the rest of the code. But at the end, a separate ** loop will run that invokes this subroutine for unmatched rows ** of pTab, with all tables to left begin set to NULL. */ WhereRightJoin *pRJ = pLevel->pRJ; sqlite3VdbeAddOp2(v, OP_BeginSubrtn, 0, pRJ->regReturn); pRJ->addrSubrtn = sqlite3VdbeCurrentAddr(v); assert( pParse->withinRJSubrtn < 255 ); pParse->withinRJSubrtn++; /* WHERE clause constraints must be deferred until after outer join ** row elimination has completed, since WHERE clause constraints apply ** to the results of the OUTER JOIN. The following loop generates the ** appropriate WHERE clause constraint checks. tag-20220513a. */ code_outer_join_constraints: for(pTerm=pWC->a, j=0; j<pWC->nBase; j++, pTerm++){ testcase( pTerm->wtFlags & TERM_VIRTUAL ); testcase( pTerm->wtFlags & TERM_CODED ); if( pTerm->wtFlags & (TERM_VIRTUAL|TERM_CODED) ) continue; if( (pTerm->prereqAll & pLevel->notReady)!=0 ){ assert( pWInfo->untestedTerms ); continue; } if( pTabItem->fg.jointype & JT_LTORJ ) continue; assert( pTerm->pExpr ); sqlite3ExprIfFalse(pParse, pTerm->pExpr, addrCont, SQLITE_JUMPIFNULL); pTerm->wtFlags |= TERM_CODED; } } #if WHERETRACE_ENABLED /* 0x20800 */ if( sqlite3WhereTrace & 0x20000 ){ sqlite3DebugPrintf("All WHERE-clause terms after coding level %d:\n", iLevel); sqlite3WhereClausePrint(pWC); |
︙ | ︙ | |||
2841 2842 2843 2844 2845 2846 2847 | } if( (pTabItem->fg.jointype & JT_LTORJ)==0 ){ mAll |= pLoop->maskSelf; for(k=0; k<pWC->nTerm; k++){ WhereTerm *pTerm = &pWC->a[k]; if( pTerm->wtFlags & TERM_VIRTUAL ) break; if( pTerm->prereqAll & ~mAll ) continue; | | | 2857 2858 2859 2860 2861 2862 2863 2864 2865 2866 2867 2868 2869 2870 2871 | } if( (pTabItem->fg.jointype & JT_LTORJ)==0 ){ mAll |= pLoop->maskSelf; for(k=0; k<pWC->nTerm; k++){ WhereTerm *pTerm = &pWC->a[k]; if( pTerm->wtFlags & TERM_VIRTUAL ) break; if( pTerm->prereqAll & ~mAll ) continue; if( ExprHasProperty(pTerm->pExpr, EP_OuterON|EP_InnerON) ) continue; pSubWhere = sqlite3ExprAnd(pParse, pSubWhere, sqlite3ExprDup(pParse->db, pTerm->pExpr, 0)); } } sFrom.nSrc = 1; sFrom.nAlloc = 1; memcpy(&sFrom.a[0], pTabItem, sizeof(SrcItem)); |
︙ | ︙ |
Changes to src/whereexpr.c.
︙ | ︙ | |||
457 458 459 460 461 462 463 | #endif /* SQLITE_OMIT_VIRTUALTABLE */ /* ** If the pBase expression originated in the ON or USING clause of ** a join, then transfer the appropriate markings over to derived. */ static void transferJoinMarkings(Expr *pDerived, Expr *pBase){ | | | | 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 | #endif /* SQLITE_OMIT_VIRTUALTABLE */ /* ** If the pBase expression originated in the ON or USING clause of ** a join, then transfer the appropriate markings over to derived. */ static void transferJoinMarkings(Expr *pDerived, Expr *pBase){ if( pDerived && ExprHasProperty(pBase, EP_OuterON|EP_InnerON) ){ pDerived->flags |= pBase->flags & (EP_OuterON|EP_InnerON); pDerived->w.iJoin = pBase->w.iJoin; } } /* ** Mark term iChild as being a child of term iParent */ |
︙ | ︙ | |||
913 914 915 916 917 918 919 | ** returned when it should not be, then incorrect answers might result. */ static int termIsEquivalence(Parse *pParse, Expr *pExpr){ char aff1, aff2; CollSeq *pColl; if( !OptimizationEnabled(pParse->db, SQLITE_Transitive) ) return 0; if( pExpr->op!=TK_EQ && pExpr->op!=TK_IS ) return 0; | | | 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 | ** returned when it should not be, then incorrect answers might result. */ static int termIsEquivalence(Parse *pParse, Expr *pExpr){ char aff1, aff2; CollSeq *pColl; if( !OptimizationEnabled(pParse->db, SQLITE_Transitive) ) return 0; if( pExpr->op!=TK_EQ && pExpr->op!=TK_IS ) return 0; if( ExprHasProperty(pExpr, EP_OuterON) ) return 0; aff1 = sqlite3ExprAffinity(pExpr->pLeft); aff2 = sqlite3ExprAffinity(pExpr->pRight); if( aff1!=aff2 && (!sqlite3IsNumericAffinity(aff1) || !sqlite3IsNumericAffinity(aff2)) ){ return 0; } |
︙ | ︙ | |||
1105 1106 1107 1108 1109 1110 1111 | if( prereqAll!=sqlite3WhereExprUsageNN(pMaskSet, pExpr) ){ printf("\n*** Incorrect prereqAll computed for:\n"); sqlite3TreeViewExpr(0,pExpr,0); abort(); } #endif | | > | | | | | | > > > > > > > | 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 | if( prereqAll!=sqlite3WhereExprUsageNN(pMaskSet, pExpr) ){ printf("\n*** Incorrect prereqAll computed for:\n"); sqlite3TreeViewExpr(0,pExpr,0); abort(); } #endif if( ExprHasProperty(pExpr, EP_OuterON|EP_InnerON) ){ Bitmask x = sqlite3WhereGetMask(pMaskSet, pExpr->w.iJoin); if( ExprHasProperty(pExpr, EP_OuterON) ){ prereqAll |= x; extraRight = x-1; /* ON clause terms may not be used with an index ** on left table of a LEFT JOIN. Ticket #3015 */ if( (prereqAll>>1)>=x ){ sqlite3ErrorMsg(pParse, "ON clause references tables to its right"); return; } }else if( (prereqAll>>1)>=x ){ /* The ON clause of an INNER JOIN references a table to its right. ** Most other SQL database engines raise an error. But all versions ** of SQLite going back to 3.0.0 have just put the ON clause constraint ** into the WHERE clause and carried on. */ ExprClearProperty(pExpr, EP_InnerON); } } pTerm->prereqAll = prereqAll; pTerm->leftCursor = -1; pTerm->iParent = -1; pTerm->eOperator = 0; if( allowedOp(op) ){ |
︙ | ︙ | |||
1180 1181 1182 1183 1184 1185 1186 | pNew->u.x.leftColumn = aiCurCol[1]; testcase( (prereqLeft | extraRight) != prereqLeft ); pNew->prereqRight = prereqLeft | extraRight; pNew->prereqAll = prereqAll; pNew->eOperator = (operatorMask(pDup->op) + eExtraOp) & opMask; }else if( op==TK_ISNULL | | | 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 | pNew->u.x.leftColumn = aiCurCol[1]; testcase( (prereqLeft | extraRight) != prereqLeft ); pNew->prereqRight = prereqLeft | extraRight; pNew->prereqAll = prereqAll; pNew->eOperator = (operatorMask(pDup->op) + eExtraOp) & opMask; }else if( op==TK_ISNULL && !ExprHasProperty(pExpr,EP_OuterON) && 0==sqlite3ExprCanBeNull(pLeft) ){ assert( !ExprHasProperty(pExpr, EP_IntValue) ); pExpr->op = TK_TRUEFALSE; pExpr->u.zToken = "false"; ExprSetProperty(pExpr, EP_IsFalse); pTerm->prereqAll = 0; |
︙ | ︙ | |||
1251 1252 1253 1254 1255 1256 1257 | ** virtual term of that form. ** ** The virtual term must be tagged with TERM_VNULL. */ else if( pExpr->op==TK_NOTNULL ){ if( pExpr->pLeft->op==TK_COLUMN && pExpr->pLeft->iColumn>=0 | | | 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 | ** virtual term of that form. ** ** The virtual term must be tagged with TERM_VNULL. */ else if( pExpr->op==TK_NOTNULL ){ if( pExpr->pLeft->op==TK_COLUMN && pExpr->pLeft->iColumn>=0 && !ExprHasProperty(pExpr, EP_OuterON) ){ Expr *pNewExpr; Expr *pLeft = pExpr->pLeft; int idxNew; WhereTerm *pNewTerm; pNewExpr = sqlite3PExpr(pParse, TK_GT, |
︙ | ︙ | |||
1455 1456 1457 1458 1459 1460 1461 | prereqExpr = sqlite3WhereExprUsage(pMaskSet, pRight); prereqColumn = sqlite3WhereExprUsage(pMaskSet, pLeft); if( (prereqExpr & prereqColumn)==0 ){ Expr *pNewExpr; pNewExpr = sqlite3PExpr(pParse, TK_MATCH, 0, sqlite3ExprDup(db, pRight, 0)); | | | | 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 | prereqExpr = sqlite3WhereExprUsage(pMaskSet, pRight); prereqColumn = sqlite3WhereExprUsage(pMaskSet, pLeft); if( (prereqExpr & prereqColumn)==0 ){ Expr *pNewExpr; pNewExpr = sqlite3PExpr(pParse, TK_MATCH, 0, sqlite3ExprDup(db, pRight, 0)); if( ExprHasProperty(pExpr, EP_OuterON) && pNewExpr ){ ExprSetProperty(pNewExpr, EP_OuterON); pNewExpr->w.iJoin = pExpr->w.iJoin; } idxNew = whereClauseInsert(pWC, pNewExpr, TERM_VIRTUAL|TERM_DYNAMIC); testcase( idxNew==0 ); pNewTerm = &pWC->a[idxNew]; pNewTerm->prereqRight = prereqExpr; pNewTerm->leftCursor = pLeft->iTable; |
︙ | ︙ | |||
1823 1824 1825 1826 1827 1828 1829 | assert( ExprUseYTab(pColRef) ); pColRef->y.pTab = pTab; pItem->colUsed |= sqlite3ExprColUsed(pColRef); pRhs = sqlite3PExpr(pParse, TK_UPLUS, sqlite3ExprDup(pParse->db, pArgs->a[j].pExpr, 0), 0); pTerm = sqlite3PExpr(pParse, TK_EQ, pColRef, pRhs); if( pItem->fg.jointype & (JT_LEFT|JT_LTORJ) ){ | | | | 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 | assert( ExprUseYTab(pColRef) ); pColRef->y.pTab = pTab; pItem->colUsed |= sqlite3ExprColUsed(pColRef); pRhs = sqlite3PExpr(pParse, TK_UPLUS, sqlite3ExprDup(pParse->db, pArgs->a[j].pExpr, 0), 0); pTerm = sqlite3PExpr(pParse, TK_EQ, pColRef, pRhs); if( pItem->fg.jointype & (JT_LEFT|JT_LTORJ) ){ joinType = EP_OuterON; }else{ joinType = EP_InnerON; } sqlite3SetJoinExpr(pTerm, pItem->iCursor, joinType); whereClauseInsert(pWC, pTerm, TERM_DYNAMIC); } } |
Changes to test/altermalloc3.test.
︙ | ︙ | |||
16 17 18 19 20 21 22 23 24 25 26 27 28 29 | set testprefix altermalloc3 # If SQLITE_OMIT_ALTERTABLE is defined, omit this file. ifcapable !altertable { finish_test return } set ::TMPDBERROR [list 1 \ {unable to open a temporary database file for storing temporary tables} ] do_execsql_test 1.0 { CREATE TABLE x1( | > | 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | set testprefix altermalloc3 # If SQLITE_OMIT_ALTERTABLE is defined, omit this file. ifcapable !altertable { finish_test return } set ::TMPDBERROR [list 1 \ {unable to open a temporary database file for storing temporary tables} ] do_execsql_test 1.0 { CREATE TABLE x1( |
︙ | ︙ | |||
42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 | do_faultsim_test 1 -prep { faultsim_restore_and_reopen } -body { execsql { ALTER TABLE t1 DROP COLUMN c } } -test { faultsim_test_result {0 {}} $::TMPDBERROR } #------------------------------------------------------------------------- # dbsqlfuzz e3dd84cda3848016a6a6024c7249d09bc2ef2615 # reset_db do_execsql_test 2.0 { CREATE TABLE t2(k,v); CREATE TRIGGER r2 AFTER INSERT ON t2 BEGIN UPDATE t2 SET (k,v)= ( (WITH cte1(a) AS ( SELECT 1 FROM ( SELECT * FROM t2 ) ) SELECT a FROM cte1 ), 1); END; } faultsim_save_and_close faultsim_restore_and_reopen do_execsql_test 2.1 { ALTER TABLE t2 RENAME TO t2x; | > > > > > > | 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 | do_faultsim_test 1 -prep { faultsim_restore_and_reopen } -body { execsql { ALTER TABLE t1 DROP COLUMN c } } -test { faultsim_test_result {0 {}} $::TMPDBERROR } #------------------------------------------------------------------------- # dbsqlfuzz e3dd84cda3848016a6a6024c7249d09bc2ef2615 # reset_db do_execsql_test 2.0 { CREATE TABLE t2(k,v); CREATE TRIGGER r2 AFTER INSERT ON t2 BEGIN UPDATE t2 SET (k,v)= ( (WITH cte1(a) AS ( SELECT 1 FROM ( SELECT * FROM t2 ) ) SELECT a FROM cte1 ), 1); END; CREATE TRIGGER r1 AFTER INSERT ON t2 BEGIN UPDATE t2 SET k=1 FROM t2 AS one, t2 AS two NATURAL JOIN t2 AS three WHERE one.k=two.v; END; } faultsim_save_and_close faultsim_restore_and_reopen do_execsql_test 2.1 { ALTER TABLE t2 RENAME TO t2x; |
︙ | ︙ |
Changes to test/altertab3.test.
︙ | ︙ | |||
641 642 643 644 645 646 647 | CREATE TABLE t1(xx); CREATE TRIGGER xx INSERT ON t1 BEGIN UPDATE t1 SET xx=xx FROM(SELECT xx); END; } {} do_catchsql_test 26.6 { ALTER TABLE t1 RENAME TO t2; | | | 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 | CREATE TABLE t1(xx); CREATE TRIGGER xx INSERT ON t1 BEGIN UPDATE t1 SET xx=xx FROM(SELECT xx); END; } {} do_catchsql_test 26.6 { ALTER TABLE t1 RENAME TO t2; } {1 {error in trigger xx: no such column: xx}} #------------------------------------------------------------------------- reset_db do_execsql_test 27.1 { CREATE TABLE t1(a, b AS ((WITH w1 (xyz) AS ( SELECT t1.b FROM t1 ) SELECT 123) IN ()), c); |
︙ | ︙ |
Added test/altertrig.test.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 | # 2022 May 27 # # 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 altertrig # If SQLITE_OMIT_ALTERTABLE is defined, omit this file. ifcapable !altertable { finish_test return } proc collapse_whitespace {in} { regsub -all {[ \t\n]+} [string trim $in] { } } proc do_whitespace_sql_test {tn sql res} { set got [execsql $sql] set wgot [list] set wres [list] foreach g $got { lappend wgot [collapse_whitespace $g] } foreach r $res { lappend wres [collapse_whitespace $r] } uplevel [list do_test $tn [list set {} $wgot] $wres] } do_execsql_test 1.0 { CREATE TABLE t1(x); CREATE TABLE t2(y); CREATE TABLE t3(z); CREATE TABLE t4(a); CREATE TRIGGER r1 INSERT ON t1 BEGIN UPDATE t1 SET d='xyz' FROM t2, t3; END; } do_whitespace_sql_test 1.1 { ALTER TABLE t3 RENAME TO t5; SELECT sql FROM sqlite_schema WHERE type='trigger'; } {{ CREATE TRIGGER r1 INSERT ON t1 BEGIN UPDATE t1 SET d='xyz' FROM t2, "t5"; END }} do_execsql_test 1.2 { DROP TRIGGER r1; CREATE TRIGGER r1 INSERT ON t1 BEGIN UPDATE t1 SET d='xyz' FROM t2, (SELECT * FROM t5); END; } do_whitespace_sql_test 1.3 { ALTER TABLE t5 RENAME TO t3; SELECT sql FROM sqlite_schema WHERE type='trigger'; } {{ CREATE TRIGGER r1 INSERT ON t1 BEGIN UPDATE t1 SET d='xyz' FROM t2, (SELECT * FROM "t3"); END }} foreach {tn alter update final} { 1 { ALTER TABLE t3 RENAME TO t10 } { UPDATE t1 SET d='xyz' FROM t2, (SELECT * FROM t3) } { UPDATE t1 SET d='xyz' FROM t2, (SELECT * FROM "t10") } 2 { ALTER TABLE t3 RENAME TO t10 } { UPDATE t1 SET a='xyz' FROM t3, (SELECT * FROM (SELECT e FROM t3)) } { UPDATE t1 SET a='xyz' FROM "t10", (SELECT * FROM (SELECT e FROM "t10")) } 3 { ALTER TABLE t3 RENAME e TO abc } { UPDATE t1 SET a='xyz' FROM t3, (SELECT * FROM (SELECT e FROM t3)) } { UPDATE t1 SET a='xyz' FROM t3, (SELECT * FROM (SELECT abc FROM t3)) } 4 { ALTER TABLE t2 RENAME c TO abc } { UPDATE t1 SET a='xyz' FROM t3, (SELECT 1 FROM t2 WHERE c) } { UPDATE t1 SET a='xyz' FROM t3, (SELECT 1 FROM t2 WHERE abc) } 5 { ALTER TABLE t2 RENAME c TO abc } { UPDATE t1 SET a=t2.c FROM t2 } { UPDATE t1 SET a=t2.abc FROM t2 } 6 { ALTER TABLE t2 RENAME c TO abc } { UPDATE t1 SET a=t2.c FROM t2, t3 } { UPDATE t1 SET a=t2.abc FROM t2, t3 } 7 { ALTER TABLE t4 RENAME e TO abc } { UPDATE t1 SET a=1 FROM t3 NATURAL JOIN t4 WHERE t4.e=a } { UPDATE t1 SET a=1 FROM t3 NATURAL JOIN t4 WHERE t4.abc=a } 8 { ALTER TABLE t4 RENAME TO abc } { UPDATE t1 SET a=1 FROM t3 NATURAL JOIN t4 WHERE t4.e=a } { UPDATE t1 SET a=1 FROM t3 NATURAL JOIN "abc" WHERE "abc".e=a } } { reset_db do_execsql_test 2.$tn.1 { CREATE TABLE t1(a,b); CREATE TABLE t2(c,d); CREATE TABLE t3(e,f); CREATE TABLE t4(e,f); } do_execsql_test 2.$tn.2 " CREATE TRIGGER r1 INSERT ON t1 BEGIN $update; END " do_execsql_test 2.$tn.3 $alter do_whitespace_sql_test 2.$tn.4 { SELECT sqL FROM sqlite_schema WHERE type='trigger' } "{ CREATE TRIGGER r1 INSERT ON t1 BEGIN $final; END }" } finish_test |
Changes to test/e_select.test.
︙ | ︙ | |||
614 615 616 617 618 619 620 | {aa cc cc bb DD dd} 4b { SELECT * FROM (SELECT a COLLATE nocase, b FROM t6) AS x %JOIN% t5 ON (x.a=t5.a) } {aa cc AA cc bb DD BB dd} } { do_join_test e_select-1.7.$tn $select $res } | > | | | | 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 | {aa cc cc bb DD dd} 4b { SELECT * FROM (SELECT a COLLATE nocase, b FROM t6) AS x %JOIN% t5 ON (x.a=t5.a) } {aa cc AA cc bb DD BB dd} } { do_join_test e_select-1.7.$tn $select $res } # EVIDENCE-OF: R-24610-05866 If the join-operator is a "LEFT JOIN" or # "LEFT OUTER JOIN", then after the ON or USING filtering clauses have # been applied, an extra row is added to the output for each row in the # original left-hand input dataset that does not match any row in the # right-hand dataset. # do_execsql_test e_select-1.8.0 { CREATE TABLE t7(a, b, c); CREATE TABLE t8(a, d, e); INSERT INTO t7 VALUES('x', 'ex', 24); INSERT INTO t7 VALUES('y', 'why', 25); |
︙ | ︙ |
Changes to test/expr.test.
︙ | ︙ | |||
177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 | test_expr expr-1.109 {i1=0} {1/0} {{}} if {[working_64bit_int]} { test_expr expr-1.110 {i1=0} {-9223372036854775807/-1} 9223372036854775807 } test_expr expr-1.111 {i1=NULL, i2=8} {i1 IS i2} 0 test_expr expr-1.112 {i1=NULL, i2=NULL} {i1 IS i2} 1 test_expr expr-1.113 {i1=6, i2=NULL} {i1 IS i2} 0 test_expr expr-1.114 {i1=6, i2=6} {i1 IS i2} 1 test_expr expr-1.115 {i1=NULL, i2=8} \ {CASE WHEN i1 IS i2 THEN 'yes' ELSE 'no' END} no test_expr expr-1.116 {i1=NULL, i2=NULL} \ {CASE WHEN i1 IS i2 THEN 'yes' ELSE 'no' END} yes test_expr expr-1.117 {i1=6, i2=NULL} \ {CASE WHEN i1 IS i2 THEN 'yes' ELSE 'no' END} no test_expr expr-1.118 {i1=8, i2=8} \ {CASE WHEN i1 IS i2 THEN 'yes' ELSE 'no' END} yes test_expr expr-1.119 {i1=NULL, i2=8} {i1 IS NOT i2} 1 test_expr expr-1.120 {i1=NULL, i2=NULL} {i1 IS NOT i2} 0 test_expr expr-1.121 {i1=6, i2=NULL} {i1 IS NOT i2} 1 test_expr expr-1.122 {i1=6, i2=6} {i1 IS NOT i2} 0 test_expr expr-1.123 {i1=NULL, i2=8} \ {CASE WHEN i1 IS NOT i2 THEN 'yes' ELSE 'no' END} yes test_expr expr-1.124 {i1=NULL, i2=NULL} \ {CASE WHEN i1 IS NOT i2 THEN 'yes' ELSE 'no' END} no test_expr expr-1.125 {i1=6, i2=NULL} \ {CASE WHEN i1 IS NOT i2 THEN 'yes' ELSE 'no' END} yes test_expr expr-1.126 {i1=8, i2=8} \ {CASE WHEN i1 IS NOT i2 THEN 'yes' ELSE 'no' END} no do_catchsql_test expr-1.127 { SELECT 1 IS #1; } {1 {near "#1": syntax error}} ifcapable floatingpoint {if {[working_64bit_int]} { test_expr expr-1.200\ | > > > > > > > > > > > > > > > > > > > > > > > > | 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 | test_expr expr-1.109 {i1=0} {1/0} {{}} if {[working_64bit_int]} { test_expr expr-1.110 {i1=0} {-9223372036854775807/-1} 9223372036854775807 } test_expr expr-1.111 {i1=NULL, i2=8} {i1 IS i2} 0 test_expr expr-1.111b {i1=NULL, i2=8} {i1 IS NOT DISTINCT FROM i2} 0 test_expr expr-1.112 {i1=NULL, i2=NULL} {i1 IS i2} 1 test_expr expr-1.112b {i1=NULL, i2=NULL} {i1 IS NOT DISTINCT FROM i2} 1 test_expr expr-1.113 {i1=6, i2=NULL} {i1 IS i2} 0 test_expr expr-1.113b {i1=6, i2=NULL} {i1 IS NOT DISTINCT FROM i2} 0 test_expr expr-1.114 {i1=6, i2=6} {i1 IS i2} 1 test_expr expr-1.114b {i1=6, i2=6} {i1 IS NOT DISTINCT FROM i2} 1 test_expr expr-1.115 {i1=NULL, i2=8} \ {CASE WHEN i1 IS i2 THEN 'yes' ELSE 'no' END} no test_expr expr-1.115b {i1=NULL, i2=8} \ {CASE WHEN i1 IS NOT DISTINCT FROM i2 THEN 'yes' ELSE 'no' END} no test_expr expr-1.116 {i1=NULL, i2=NULL} \ {CASE WHEN i1 IS i2 THEN 'yes' ELSE 'no' END} yes test_expr expr-1.116b {i1=NULL, i2=NULL} \ {CASE WHEN i1 IS NOT DISTINCT FROM i2 THEN 'yes' ELSE 'no' END} yes test_expr expr-1.117 {i1=6, i2=NULL} \ {CASE WHEN i1 IS i2 THEN 'yes' ELSE 'no' END} no test_expr expr-1.117b {i1=6, i2=NULL} \ {CASE WHEN i1 IS NOT DISTINCT FROM i2 THEN 'yes' ELSE 'no' END} no test_expr expr-1.118 {i1=8, i2=8} \ {CASE WHEN i1 IS i2 THEN 'yes' ELSE 'no' END} yes test_expr expr-1.118b {i1=8, i2=8} \ {CASE WHEN i1 IS NOT DISTINCT FROM i2 THEN 'yes' ELSE 'no' END} yes test_expr expr-1.119 {i1=NULL, i2=8} {i1 IS NOT i2} 1 test_expr expr-1.119b {i1=NULL, i2=8} {i1 IS DISTINCT FROM i2} 1 test_expr expr-1.120 {i1=NULL, i2=NULL} {i1 IS NOT i2} 0 test_expr expr-1.120b {i1=NULL, i2=NULL} {i1 IS DISTINCT FROM i2} 0 test_expr expr-1.121 {i1=6, i2=NULL} {i1 IS NOT i2} 1 test_expr expr-1.121b {i1=6, i2=NULL} {i1 IS DISTINCT FROM i2} 1 test_expr expr-1.122 {i1=6, i2=6} {i1 IS NOT i2} 0 test_expr expr-1.122b {i1=6, i2=6} {i1 IS DISTINCT FROM i2} 0 test_expr expr-1.123 {i1=NULL, i2=8} \ {CASE WHEN i1 IS NOT i2 THEN 'yes' ELSE 'no' END} yes test_expr expr-1.123b {i1=NULL, i2=8} \ {CASE WHEN i1 IS DISTINCT FROM i2 THEN 'yes' ELSE 'no' END} yes test_expr expr-1.124 {i1=NULL, i2=NULL} \ {CASE WHEN i1 IS NOT i2 THEN 'yes' ELSE 'no' END} no test_expr expr-1.124b {i1=NULL, i2=NULL} \ {CASE WHEN i1 IS DISTINCT FROM i2 THEN 'yes' ELSE 'no' END} no test_expr expr-1.125 {i1=6, i2=NULL} \ {CASE WHEN i1 IS NOT i2 THEN 'yes' ELSE 'no' END} yes test_expr expr-1.125b {i1=6, i2=NULL} \ {CASE WHEN i1 IS DISTINCT FROM i2 THEN 'yes' ELSE 'no' END} yes test_expr expr-1.126 {i1=8, i2=8} \ {CASE WHEN i1 IS NOT i2 THEN 'yes' ELSE 'no' END} no test_expr expr-1.126b {i1=8, i2=8} \ {CASE WHEN i1 IS DISTINCT FROM i2 THEN 'yes' ELSE 'no' END} no do_catchsql_test expr-1.127 { SELECT 1 IS #1; } {1 {near "#1": syntax error}} ifcapable floatingpoint {if {[working_64bit_int]} { test_expr expr-1.200\ |
︙ | ︙ |
Changes to test/func7.test.
︙ | ︙ | |||
56 57 58 59 60 61 62 63 64 65 66 67 68 69 | } {0.6931472} do_execsql_test func7-pg-170 { SELECT log(100.0) } {2.0} do_execsql_test func7-pg-180 { SELECT log10(1000.0) } {3.0} do_execsql_test func7-pg-190 { SELECT log(2.0, 64.0) } {6.0} do_execsql_test func7-pg-200 { SELECT mod(9,4); } {1.0} do_execsql_test func7-pg-210 { | > > > > > > | 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 | } {0.6931472} do_execsql_test func7-pg-170 { SELECT log(100.0) } {2.0} do_execsql_test func7-pg-180 { SELECT log10(1000.0) } {3.0} do_execsql_test func7-pg-181 { SELECT format('%.30f', log10(100.0) ); } {2.000000000000000000000000000000} do_execsql_test func7-pg-182 { SELECT format('%.30f', ln(exp(2.0)) ); } {2.000000000000000000000000000000} do_execsql_test func7-pg-190 { SELECT log(2.0, 64.0) } {6.0} do_execsql_test func7-pg-200 { SELECT mod(9,4); } {1.0} do_execsql_test func7-pg-210 { |
︙ | ︙ |
Changes to test/in.test.
︙ | ︙ | |||
793 794 795 796 797 798 799 800 801 | } {ok} # Ticket f3ff1472887 # do_execsql_test in-20.1 { SELECT (1 IN (2 IS TRUE)); } {1} finish_test | > > > > > > > > > | 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 | } {ok} # Ticket f3ff1472887 # do_execsql_test in-20.1 { SELECT (1 IN (2 IS TRUE)); } {1} # Forum post: https://sqlite.org/forum/forumpost/5782619992. # reset_db do_execsql_test in-21.1 { CREATE TABLE t0(c0); SELECT COUNT(*) FROM t0 ORDER BY (t0.c0 IN ()); } {0} finish_test |
Changes to test/join.test.
︙ | ︙ | |||
246 247 248 249 250 251 252 253 254 255 256 257 258 259 | do_test join-2.1 { execsql { SELECT * FROM t1 NATURAL LEFT JOIN t2; } } {1 2 3 4 2 3 4 5 3 4 5 {}} # ticket #3522 do_test join-2.1.1 { execsql2 { SELECT * FROM t1 NATURAL LEFT JOIN t2; } } {a 1 b 2 c 3 d 4 a 2 b 3 c 4 d 5 a 3 b 4 c 5 d {}} do_test join-2.1.2 { | > > > > > > > > > > > > > | 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 | do_test join-2.1 { execsql { SELECT * FROM t1 NATURAL LEFT JOIN t2; } } {1 2 3 4 2 3 4 5 3 4 5 {}} # EVIDENCE-OF: R-52129-05406 you can say things like "OUTER LEFT NATURAL # JOIN" which means the same as "NATURAL LEFT OUTER JOIN". do_test join-2.1b { execsql { SELECT * FROM t1 OUTER LEFT NATURAL JOIN t2; } } {1 2 3 4 2 3 4 5 3 4 5 {}} do_test join-2.1c { execsql { SELECT * FROM t1 NATURAL LEFT OUTER JOIN t2; } } {1 2 3 4 2 3 4 5 3 4 5 {}} # ticket #3522 do_test join-2.1.1 { execsql2 { SELECT * FROM t1 NATURAL LEFT JOIN t2; } } {a 1 b 2 c 3 d 4 a 2 b 3 c 4 d 5 a 3 b 4 c 5 d {}} do_test join-2.1.2 { |
︙ | ︙ | |||
324 325 326 327 328 329 330 331 332 333 334 335 336 337 | catchsql { SELECT * FROM t1 USING(a) } } {1 {a JOIN clause is required before USING}} do_test join-3.6 { catchsql { SELECT * FROM t1 JOIN t2 ON t3.a=t2.b; } } {1 {no such column: t3.a}} do_test join-3.7 { catchsql { SELECT * FROM t1 INNER OUTER JOIN t2; } } {1 {unknown join type: INNER OUTER}} do_test join-3.8 { catchsql { | > > > | 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 | catchsql { SELECT * FROM t1 USING(a) } } {1 {a JOIN clause is required before USING}} do_test join-3.6 { catchsql { SELECT * FROM t1 JOIN t2 ON t3.a=t2.b; } } {1 {no such column: t3.a}} # EVIDENCE-OF: R-47973-48020 you cannot say "INNER OUTER JOIN", because # that would be contradictory. do_test join-3.7 { catchsql { SELECT * FROM t1 INNER OUTER JOIN t2; } } {1 {unknown join type: INNER OUTER}} do_test join-3.8 { catchsql { |
︙ | ︙ |
Changes to test/join8.test.
︙ | ︙ | |||
16 17 18 19 20 21 22 23 24 25 26 27 28 29 | ifcapable !vtab { finish_test return } db null NULL do_execsql_test join8-10 { CREATE TABLE t1(a,b,c); CREATE TABLE t2(x,y); CREATE INDEX t2x ON t2(x); SELECT avg(DISTINCT b) FROM (SELECT * FROM t2 LEFT RIGHT JOIN t1 ON c); } {NULL} | > > | 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | ifcapable !vtab { finish_test return } db null NULL # EVIDENCE-OF: R-33754-02880 you can say "LEFT RIGHT JOIN" which is the # same as "FULL JOIN". do_execsql_test join8-10 { CREATE TABLE t1(a,b,c); CREATE TABLE t2(x,y); CREATE INDEX t2x ON t2(x); SELECT avg(DISTINCT b) FROM (SELECT * FROM t2 LEFT RIGHT JOIN t1 ON c); } {NULL} |
︙ | ︙ | |||
222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 | - - - - - - 200 0 - - - - - - - - 203 3 - - - - - - - - 209 9 - - - - - - - - - - 300 0 - - - - - - - - 305 5 - - - - - - - - 310 10 } do_execsql_test join8-7020 { EXPLAIN QUERY PLAN WITH t0 AS MATERIALIZED ( SELECT t1.*, t2.*, t3.* FROM t1 INNER JOIN t2 ON t1.b=t2.b AND t2.x>0 RIGHT JOIN t3 ON t1.c=t3.c AND t3.y>0 ) SELECT * FROM t0 FULL JOIN t4 ON t0.a=t4.d AND t4.z>0 ORDER BY coalesce(t0.a, t0.y+200, t4.d); } {/.*BLOOM FILTER ON t2.*BLOOM FILTER ON t3.*BLOOM FILTER ON t4.*/} finish_test | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 | - - - - - - 200 0 - - - - - - - - 203 3 - - - - - - - - 209 9 - - - - - - - - - - 300 0 - - - - - - - - 305 5 - - - - - - - - 310 10 } # EVIDENCE-OF: R-33754-02880 you can say "LEFT RIGHT JOIN" which is the # same as "FULL JOIN". do_execsql_test join8-7011 { WITH t0 AS MATERIALIZED ( SELECT t1.*, t2.*, t3.* FROM t1 INNER JOIN t2 ON t1.b=t2.b AND t2.x>0 RIGHT JOIN t3 ON t1.c=t3.c AND t3.y>0 ) SELECT * FROM t0 LEFT RIGHT JOIN t4 ON t0.a=t4.d AND t4.z>0 ORDER BY coalesce(t0.a, t0.y+200, t4.d); } { 6 106 206 306 106 6 206 6 - - - - - - - - 200 0 - - - - - - - - 203 3 - - - - - - - - 209 9 - - - - - - - - - - 300 0 - - - - - - - - 305 5 - - - - - - - - 310 10 } do_execsql_test join8-7020 { EXPLAIN QUERY PLAN WITH t0 AS MATERIALIZED ( SELECT t1.*, t2.*, t3.* FROM t1 INNER JOIN t2 ON t1.b=t2.b AND t2.x>0 RIGHT JOIN t3 ON t1.c=t3.c AND t3.y>0 ) SELECT * FROM t0 FULL JOIN t4 ON t0.a=t4.d AND t4.z>0 ORDER BY coalesce(t0.a, t0.y+200, t4.d); } {/.*BLOOM FILTER ON t2.*BLOOM FILTER ON t3.*BLOOM FILTER ON t4.*/} # 2022-05-12 Difference with PG found (by Dan) while exploring # https://sqlite.org/forum/forumpost/677a0ab93fcd9ccd # reset_db do_execsql_test join8-8000 { CREATE TABLE t1(a INT, b INT); CREATE TABLE t2(c INT, d INT); CREATE TABLE t3(e INT, f INT); INSERT INTO t1 VALUES(1, 2); INSERT INTO t2 VALUES(3, 4); INSERT INTO t3 VALUES(5, 6); } {} do_execsql_test join8-8010 { SELECT * FROM t3 LEFT JOIN t2 ON true JOIN t1 ON (t3.e IS t2.c); } {} do_execsql_test join8-8020 { SELECT * FROM t3 LEFT JOIN t2 ON true JOIN t1 ON (t3.e IS NOT DISTINCT FROM t2.c); } {} # 2022-05-13 The idea of reusing subquery cursors does not # work, if the cursors are used both for scanning and lookups. # reset_db db null - do_execsql_test join8-9000 { CREATE TABLE t1(a INTEGER PRIMARY KEY, b TEXT, c TEXT, d REAL); INSERT INTO t1 VALUES(1,'E','bb',NULL),(2,NULL,NULL,NULL); SELECT * FROM t1 NATURAL RIGHT JOIN t1 AS t2 WHERE (a,b) IN (SELECT a+0, b FROM t1); } {1 E bb -} # 2022-05-14 https://sqlite.org/forum/forumpost/c06b10ad7e # reset_db db null - do_execsql_test join8-10000 { CREATE TABLE t1(c0 INT UNIQUE); CREATE TABLE t2(c0); CREATE TABLE t2i(c0 INT); CREATE TABLE t3(c0 INT); INSERT INTO t1 VALUES(1); INSERT INTO t2 VALUES(2); INSERT INTO t2i VALUES(2); INSERT INTO t3 VALUES(3); } {} do_execsql_test join8-10010 { SELECT DISTINCT t1.c0, t3.c0 FROM t2 NATURAL JOIN t1 RIGHT JOIN t3 ON t1.c0; } {- 3} do_execsql_test join8-10020 { SELECT t1.c0, t3.c0 FROM t2 NATURAL JOIN t1 RIGHT JOIN t3 ON t1.c0; } {- 3} do_execsql_test join8-10030 { SELECT DISTINCT t1.c0, t3.c0 FROM t2 NATURAL CROSS JOIN t1 RIGHT JOIN t3 ON t1.c0; } {- 3} do_execsql_test join8-10040 { SELECT t1.c0, t3.c0 FROM t1 NATURAL CROSS JOIN t2 RIGHT JOIN t3 ON t1.c0; } {- 3} do_execsql_test join8-10050 { SELECT DISTINCT t1.c0, t3.c0 FROM t2i NATURAL JOIN t1 RIGHT JOIN t3 ON t1.c0; } {- 3} do_execsql_test join8-10060 { SELECT DISTINCT +t1.c0, t3.c0 FROM t2 NATURAL JOIN t1 RIGHT JOIN t3 ON t1.c0; } {- 3} do_execsql_test join8-10070 { SELECT DISTINCT +t1.c0, t3.c0 FROM t1 NATURAL CROSS JOIN t2 RIGHT JOIN t3 ON t1.c0; } {- 3} do_execsql_test join8-10080 { SELECT DISTINCT t1.c0, t3.c0 FROM t2 NATURAL JOIN t1 RIGHT JOIN t3 ON t1.c0<>0; } {- 3} # 2022-05-14 # index-on-expr scan on a RIGHT JOIN # dbsqlfuzz 39ee60004ff027a9e2846cf76e02cd5ac0953739 # reset_db db null - do_execsql_test join8-11000 { CREATE TABLE t1(a); CREATE TABLE t2(b); INSERT INTO t2 VALUES(0),(1),(2); SELECT * FROM t1 RIGHT JOIN t2 ON (a=b) WHERE 99+(b+1)!=99; } {- 0 - 1 - 2} do_execsql_test join8-11010 { CREATE INDEX t2b ON t2(b+1) WHERE b IS NOT NULL; SELECT * FROM t1 RIGHT JOIN t2 ON (a=b) WHERE 99+(b+1)!=99; } {- 0 - 1 - 2} do_execsql_test join8-11020 { DROP TABLE t1; DROP TABLE t2; CREATE TABLE t1(a); CREATE TABLE t2(b, c, d); INSERT INTO t2 VALUES(1, 3, 'not-4'); SELECT b, d FROM t1 RIGHT JOIN t2 WHERE (b+0)=1 AND d!=4; } {1 not-4} do_execsql_test join8-11030 { CREATE INDEX i2 ON t2((b+0), d); SELECT b, d FROM t1 RIGHT JOIN t2 WHERE (b+0)=1 AND d!=4; } {1 not-4} do_execsql_test join8-11040 { DROP INDEX i2; CREATE INDEX i2 ON t2((b+0), d) WHERE d IS NOT NULL; SELECT b, d FROM t1 RIGHT JOIN t2 WHERE (b+0)=1 AND d!=4; } {1 not-4} # 2022-05-23 # NATURAL JOIN name resolution is more forgiving with LEFT JOIN # https://sqlite.org/forum/forumpost/e90a8e6e6f # reset_db db null - do_execsql_test join8-12000 { CREATE TABLE t1(a INT); INSERT INTO t1 VALUES(0),(1); CREATE TABLE t2(a INT); INSERT INTO t2 VALUES(0),(2); CREATE TABLE t3(a INT); INSERT INTO t3 VALUES(0),(3); } {} do_catchsql_test join8-12010 { SELECT * FROM t1 RIGHT JOIN t2 ON t2.a<>0 NATURAL RIGHT JOIN t3; } {1 {ambiguous reference to a in USING()}} do_catchsql_test join8-12020 { SELECT * FROM t1 RIGHT JOIN t2 ON t2.a<>0 NATURAL LEFT JOIN t3; } {1 {ambiguous reference to a in USING()}} do_catchsql_test join8-12030 { SELECT * FROM t1 LEFT JOIN t2 ON t2.a<>0 NATURAL RIGHT JOIN t3; } {1 {ambiguous reference to a in USING()}} # The following query should probably also return the same error as the # previous three cases. However, historical versions of SQLite have always # let it pass. We will not "fix" this, since to do so might break legacy # applications. # do_catchsql_test join8-12040 { SELECT * FROM t1 LEFT JOIN t2 ON t2.a<>0 NATURAL LEFT JOIN t3; } {0 {0 2 1 2}} # 2022-05-24 # https://sqlite.org/forum/forumpost/687b0bf563a1d4f1 # reset_db do_execsql_test join8-13000 { CREATE TABLE t0(t TEXT, u TEXT); INSERT INTO t0 VALUES('t', 'u'); CREATE TABLE t1(v TEXT, w TEXT); INSERT INTO t1 VALUES('v', 'w'); CREATE TABLE t2(x TEXT, y TEXT); INSERT INTO t2 VALUES('x', 'y'); SELECT * FROM t0 JOIN t1 ON (t2.x NOTNULL) LEFT JOIN t2 ON false; SELECT * FROM t0 JOIN t1 ON (t2.x NOTNULL) LEFT JOIN t2 ON false WHERE t2.y ISNULL; } {} # 2022-05-25 # https://sqlite.org/forum/forumpost/5cfe08eed6 # reset_db do_execsql_test join8-14000 { CREATE TABLE t0(a TEXT, b TEXT, c TEXT); CREATE TABLE t1(a TEXT); INSERT INTO t1 VALUES('1'); CREATE VIEW v0 AS SELECT 'xyz' AS d; SELECT * FROM v0 RIGHT JOIN t1 ON t1.a<>'' INNER JOIN t0 ON t0.c<>''; SELECT * FROM v0 RIGHT JOIN t1 ON t1.a<>'' INNER JOIN t0 ON t0.c<>'' WHERE b ISNULL; } {} do_execsql_test join8-14010 { CREATE TABLE y0(a INT); CREATE TABLE y1(b INT); INSERT INTO y1 VALUES(1), (2); CREATE TABLE y2(c INT); INSERT INTO y2 VALUES(3), (4); } {} db null - do_execsql_test join8-14020 { SELECT * FROM y0 RIGHT JOIN y1 ON true INNER JOIN y2 ON true WHERE y2.c!=99 AND y2.c!=98; } { - 1 3 - 1 4 - 2 3 - 2 4 } finish_test |
Added test/joinE.test.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 | # 2022-05-13 # # The author disclaims copyright to this source code. In place of # a legal notice, here is a blessing: # # May you do good and not evil. # May you find forgiveness for yourself and forgive others. # May you share freely, never taking more than you give. # #*********************************************************************** # # This file implements tests for JOINs that use Bloom filters. # # The test case output is (mostly) all generated by PostgreSQL 14. This # test module was created as follows: # # 1. Run a TCL script (included at the bottom of this file) that # generates an input script for "psql" that will run man # diverse tests on joins. # # 2. Run the script from step (1) through psql and collect the # output. # # 3. Make a few minor global search-and-replace operations to convert # the psql output into a form suitable for this test module. # # 4. Add this header, and the script content at the footer. # set testdir [file dirname $argv0] source $testdir/tester.tcl db nullvalue - db eval { CREATE TABLE t1(a INT); INSERT INTO t1 VALUES(1),(NULL); CREATE TABLE t2(b INT); INSERT INTO t2 VALUES(2),(NULL); } do_execsql_test joinE-1 { SELECT a, b FROM t1 INNER JOIN t2 ON true ORDER BY coalesce(a,b,3); } { 1 2 1 - - 2 - - } do_execsql_test joinE-2 { SELECT a, b FROM t1 INNER JOIN t2 ON true WHERE a IS NULL ORDER BY coalesce(a,b,3); } { - 2 - - } do_execsql_test joinE-3 { SELECT a, b FROM t1 INNER JOIN t2 ON a IS NULL ORDER BY coalesce(a,b,3); } { - 2 - - } do_execsql_test joinE-4 { SELECT a, b FROM t1 INNER JOIN t2 ON true WHERE b IS NULL ORDER BY coalesce(a,b,3); } { 1 - - - } do_execsql_test joinE-5 { SELECT a, b FROM t1 INNER JOIN t2 ON b IS NULL ORDER BY coalesce(a,b,3); } { 1 - - - } do_execsql_test joinE-6 { SELECT a, b FROM t1 LEFT JOIN t2 ON true ORDER BY coalesce(a,b,3); } { 1 2 1 - - 2 - - } do_execsql_test joinE-7 { SELECT a, b FROM t1 LEFT JOIN t2 ON true WHERE a IS NULL ORDER BY coalesce(a,b,3); } { - 2 - - } do_execsql_test joinE-8 { SELECT a, b FROM t1 LEFT JOIN t2 ON a IS NULL ORDER BY coalesce(a,b,3); } { 1 - - 2 - - } do_execsql_test joinE-9 { SELECT a, b FROM t1 LEFT JOIN t2 ON true WHERE b IS NULL ORDER BY coalesce(a,b,3); } { 1 - - - } do_execsql_test joinE-10 { SELECT a, b FROM t1 LEFT JOIN t2 ON b IS NULL ORDER BY coalesce(a,b,3); } { 1 - - - } do_execsql_test joinE-11 { SELECT a, b FROM t1 RIGHT JOIN t2 ON true ORDER BY coalesce(a,b,3); } { 1 2 1 - - 2 - - } do_execsql_test joinE-12 { SELECT a, b FROM t1 RIGHT JOIN t2 ON true WHERE a IS NULL ORDER BY coalesce(a,b,3); } { - 2 - - } do_execsql_test joinE-13 { SELECT a, b FROM t1 RIGHT JOIN t2 ON a IS NULL ORDER BY coalesce(a,b,3); } { - 2 - - } do_execsql_test joinE-14 { SELECT a, b FROM t1 RIGHT JOIN t2 ON true WHERE b IS NULL ORDER BY coalesce(a,b,3); } { 1 - - - } do_execsql_test joinE-15 { SELECT a, b FROM t1 RIGHT JOIN t2 ON b IS NULL ORDER BY coalesce(a,b,3); } { 1 - - 2 - - } do_execsql_test joinE-16 { SELECT a, b FROM t1 FULL JOIN t2 ON true ORDER BY coalesce(a,b,3); } { 1 2 1 - - 2 - - } do_execsql_test joinE-17 { SELECT a, b FROM t1 FULL JOIN t2 ON true WHERE a IS NULL ORDER BY coalesce(a,b,3); } { - 2 - - } # PG-14 is unable to perform this join. It says: FULL JOIN is only # supported with merge-joinable or hash-joinable join conditions # # do_execsql_test joinE-18 { # SELECT a, b # FROM t1 FULL JOIN t2 ON a IS NULL # ORDER BY coalesce(a,b,3); # } { # } do_execsql_test joinE-19 { SELECT a, b FROM t1 FULL JOIN t2 ON true WHERE b IS NULL ORDER BY coalesce(a,b,3); } { 1 - - - } # PG-14 is unable to perform this join. It says: FULL JOIN is only # supported with merge-joinable or hash-joinable join conditions # # do_execsql_test joinE-20 { # SELECT a, b # FROM t1 FULL JOIN t2 ON b IS NULL # ORDER BY coalesce(a,b,3); # } { # } db eval { DELETE FROM t1; INSERT INTO t1 VALUES(1); DELETE FROM t2; INSERT INTO t2 VALUES(NULL); } do_execsql_test joinE-21 { SELECT a, b FROM t1 INNER JOIN t2 ON true ORDER BY coalesce(a,b,3); } { 1 - } do_execsql_test joinE-22 { SELECT a, b FROM t1 INNER JOIN t2 ON true WHERE a IS NULL ORDER BY coalesce(a,b,3); } { } do_execsql_test joinE-23 { SELECT a, b FROM t1 INNER JOIN t2 ON a IS NULL ORDER BY coalesce(a,b,3); } { } do_execsql_test joinE-24 { SELECT a, b FROM t1 INNER JOIN t2 ON true WHERE b IS NULL ORDER BY coalesce(a,b,3); } { 1 - } do_execsql_test joinE-25 { SELECT a, b FROM t1 INNER JOIN t2 ON b IS NULL ORDER BY coalesce(a,b,3); } { 1 - } do_execsql_test joinE-26 { SELECT a, b FROM t1 LEFT JOIN t2 ON true ORDER BY coalesce(a,b,3); } { 1 - } do_execsql_test joinE-27 { SELECT a, b FROM t1 LEFT JOIN t2 ON true WHERE a IS NULL ORDER BY coalesce(a,b,3); } { } do_execsql_test joinE-28 { SELECT a, b FROM t1 LEFT JOIN t2 ON a IS NULL ORDER BY coalesce(a,b,3); } { 1 - } do_execsql_test joinE-29 { SELECT a, b FROM t1 LEFT JOIN t2 ON true WHERE b IS NULL ORDER BY coalesce(a,b,3); } { 1 - } do_execsql_test joinE-30 { SELECT a, b FROM t1 LEFT JOIN t2 ON b IS NULL ORDER BY coalesce(a,b,3); } { 1 - } do_execsql_test joinE-31 { SELECT a, b FROM t1 RIGHT JOIN t2 ON true ORDER BY coalesce(a,b,3); } { 1 - } do_execsql_test joinE-32 { SELECT a, b FROM t1 RIGHT JOIN t2 ON true WHERE a IS NULL ORDER BY coalesce(a,b,3); } { } do_execsql_test joinE-33 { SELECT a, b FROM t1 RIGHT JOIN t2 ON a IS NULL ORDER BY coalesce(a,b,3); } { - - } do_execsql_test joinE-34 { SELECT a, b FROM t1 RIGHT JOIN t2 ON true WHERE b IS NULL ORDER BY coalesce(a,b,3); } { 1 - } do_execsql_test joinE-35 { SELECT a, b FROM t1 RIGHT JOIN t2 ON b IS NULL ORDER BY coalesce(a,b,3); } { 1 - } do_execsql_test joinE-36 { SELECT a, b FROM t1 FULL JOIN t2 ON true ORDER BY coalesce(a,b,3); } { 1 - } do_execsql_test joinE-37 { SELECT a, b FROM t1 FULL JOIN t2 ON true WHERE a IS NULL ORDER BY coalesce(a,b,3); } { } # PG-14 is unable # # do_execsql_test joinE-38 { # SELECT a, b # FROM t1 FULL JOIN t2 ON a IS NULL # ORDER BY coalesce(a,b,3); # } { # } do_execsql_test joinE-39 { SELECT a, b FROM t1 FULL JOIN t2 ON true WHERE b IS NULL ORDER BY coalesce(a,b,3); } { 1 - } # PG-14 is unable # do_execsql_test joinE-40 { # SELECT a, b # FROM t1 FULL JOIN t2 ON b IS NULL # ORDER BY coalesce(a,b,3); # } { # } finish_test ############################################################################## # This is the PG-14 test script generator # # puts " # \\pset border off # \\pset tuples_only on # \\pset null - # # DROP TABLE IF EXISTS t1; # DROP TABLE IF EXISTS t2; # CREATE TABLE t1(a INT); # INSERT INTO t1 VALUES(1),(NULL); # CREATE TABLE t2(b INT); # INSERT INTO t2 VALUES(2),(NULL); # " # # proc echo {prefix txt} { # regsub -all {\n} $txt \n$prefix txt # puts "$prefix$txt" # } # # set n 0 # set k 0 # foreach j1 {INNER LEFT RIGHT FULL} { # foreach on1 { # true # {true WHERE a IS NULL} # {a IS NULL} # {true WHERE b IS NULL} # {b IS NULL} # } { # # incr n # incr k # set q1 "" # append q1 "SELECT a, b\n" # append q1 " FROM t1 $j1 JOIN t2 ON $on1\n" # append q1 " ORDER BY coalesce(a,b,3);" # # echo "\\qecho " "do_execsql_test joinE-$n \{" # echo "\\qecho X " $q1 # echo "\\qecho " "\} \{" # puts $q1 # echo "\\qecho " "\}" # # } # } # # puts " # DELETE FROM t1; # INSERT INTO t1 VALUES(1); # DELETE FROM t2; # INSERT INTO t2 VALUES(NULL); # " # # foreach j1 {INNER LEFT RIGHT FULL} { # foreach on1 { # true # {true WHERE a IS NULL} # {a IS NULL} # {true WHERE b IS NULL} # {b IS NULL} # } { # # incr n # incr k # set q1 "" # append q1 "SELECT a, b\n" # append q1 " FROM t1 $j1 JOIN t2 ON $on1\n" # append q1 " ORDER BY coalesce(a,b,3);" # # echo "\\qecho " "do_execsql_test joinE-$n \{" # echo "\\qecho X " $q1 # echo "\\qecho " "\} \{" # puts $q1 # echo "\\qecho " "\}" # # } # } |
Changes to test/permutations.test.
︙ | ︙ | |||
213 214 215 216 217 218 219 | ] test_suite "valgrind" -prefix "" -description { Run the "veryquick" test suite with a couple of multi-process tests (that fail under valgrind) omitted. } -files [ test_set $allquicktests -exclude *malloc* *ioerr* *fault* *_err* wal.test \ | > | | 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 | ] test_suite "valgrind" -prefix "" -description { Run the "veryquick" test suite with a couple of multi-process tests (that fail under valgrind) omitted. } -files [ test_set $allquicktests -exclude *malloc* *ioerr* *fault* *_err* wal.test \ shell2.test shell6.test shell7.test \ crash8.test atof1.test selectG.test \ tkt-fc62af4523.test numindex1.test corruptK.test ] -initialize { set ::G(valgrind) 1 } -shutdown { unset -nocomplain ::G(valgrind) } |
︙ | ︙ |
Changes to test/shell1.test.
︙ | ︙ | |||
19 20 21 22 23 24 25 | # shell1-2.*: Basic "dot" command token parsing. # shell1-3.*: Basic test that "dot" command can be called. # shell1-{4-8}.*: Test various "dot" commands's functionality. # shell1-9.*: Basic test that "dot" commands and SQL intermix ok. # set testdir [file dirname $argv0] source $testdir/tester.tcl | | | 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | # shell1-2.*: Basic "dot" command token parsing. # shell1-3.*: Basic test that "dot" command can be called. # shell1-{4-8}.*: Test various "dot" commands's functionality. # shell1-9.*: Basic test that "dot" commands and SQL intermix ok. # set testdir [file dirname $argv0] source $testdir/tester.tcl set CLI [test_cli_invocation] db close forcedelete test.db test.db-journal test.db-wal sqlite3 db test.db #---------------------------------------------------------------------------- # Test cases shell1-1.*: Basic command line option handling. # |
︙ | ︙ |
Changes to test/shell2.test.
︙ | ︙ | |||
132 133 134 135 136 137 138 | INSERT INTO foo1(a) VALUES(2); INSERT INTO foo2(b) VALUES(2); SELECT * FROM foo1; SELECT * FROM foo2; } } {0 {CREATE TABLE foo1(a); INSERT INTO foo1(a) VALUES(1); CREATE TABLE foo2(b); INSERT INTO foo2(b) VALUES(1); | | < | < | < | 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 | INSERT INTO foo1(a) VALUES(2); INSERT INTO foo2(b) VALUES(2); SELECT * FROM foo1; SELECT * FROM foo2; } } {0 {CREATE TABLE foo1(a); INSERT INTO foo1(a) VALUES(1); CREATE TABLE foo2(b); INSERT INTO foo2(b) VALUES(1); SELECT * FROM foo1; SELECT * FROM foo2; 1 1 INSERT INTO foo1(a) VALUES(2); INSERT INTO foo2(b) VALUES(2); SELECT * FROM foo1; SELECT * FROM foo2; 1 2 1 2 }} # Test with echo on and headers on using dot command and # multiple commands per line. # NB. whitespace is important |
︙ | ︙ | |||
166 167 168 169 170 171 172 | SELECT * FROM foo1; SELECT * FROM foo2; } } {0 {.headers ON CREATE TABLE foo1(a); INSERT INTO foo1(a) VALUES(1); CREATE TABLE foo2(b); INSERT INTO foo2(b) VALUES(1); | | < | < | < | 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 | SELECT * FROM foo1; SELECT * FROM foo2; } } {0 {.headers ON CREATE TABLE foo1(a); INSERT INTO foo1(a) VALUES(1); CREATE TABLE foo2(b); INSERT INTO foo2(b) VALUES(1); SELECT * FROM foo1; SELECT * FROM foo2; a 1 b 1 INSERT INTO foo1(a) VALUES(2); INSERT INTO foo2(b) VALUES(2); SELECT * FROM foo1; SELECT * FROM foo2; a 1 2 b 1 2 }} # Test for rejection of incomplete input at EOF. # Reported at https://sqlite.org/forum/forumpost/718f489a43be3197 |
︙ | ︙ |
Changes to test/shell3.test.
︙ | ︙ | |||
18 19 20 21 22 23 24 | # # shell3-1.*: Basic tests for running SQL statments from command line. # shell3-2.*: Basic tests for running SQL file from command line. # shell3-3.*: Basic tests for processing odd SQL constructs. # set testdir [file dirname $argv0] source $testdir/tester.tcl | | | 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | # # shell3-1.*: Basic tests for running SQL statments from command line. # shell3-2.*: Basic tests for running SQL file from command line. # shell3-3.*: Basic tests for processing odd SQL constructs. # set testdir [file dirname $argv0] source $testdir/tester.tcl set CLI [test_cli_invocation] db close forcedelete test.db test.db-journal test.db-wal sqlite3 db test.db # There are inconsistencies in command-line argument quoting on Windows. # In particular, individual applications are responsible for command-line |
︙ | ︙ |
Changes to test/shell4.test.
︙ | ︙ | |||
19 20 21 22 23 24 25 | # shell4-1.*: Basic tests specific to the "stats" command. # shell4-2.*: Basic tests for ".trace" # shell4-3.*: The ".read" command takes the shell out of interactive mode # shell4-4.*: Input redirects cannot recurse too much # set testdir [file dirname $argv0] source $testdir/tester.tcl | | > | 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | # shell4-1.*: Basic tests specific to the "stats" command. # shell4-2.*: Basic tests for ".trace" # shell4-3.*: The ".read" command takes the shell out of interactive mode # shell4-4.*: Input redirects cannot recurse too much # set testdir [file dirname $argv0] source $testdir/tester.tcl set CLI [test_cli_invocation] set CLI_ONLY [test_find_cli] db close forcedelete test.db test.db-journal test.db-wal sqlite3 db test.db #---------------------------------------------------------------------------- # Test cases shell4-1.*: Tests specific to the "stats" command. # |
︙ | ︙ | |||
126 127 128 129 130 131 132 | } {0 {SELECT * FROM t1;}} } do_test shell4-3.1 { set fd [open t1.txt wb] puts $fd "SELECT 'squirrel';" close $fd | | | | 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 | } {0 {SELECT * FROM t1;}} } do_test shell4-3.1 { set fd [open t1.txt wb] puts $fd "SELECT 'squirrel';" close $fd exec $::CLI_ONLY :memory: --interactive ".read t1.txt" } {squirrel} do_test shell4-3.2 { set fd [open t1.txt wb] puts $fd "SELECT 'pound: \302\243';" close $fd exec $::CLI_ONLY :memory: --interactive ".read t1.txt" } {pound: £} do_test shell4-4.1 { set fd [open t1.txt wb] puts $fd ".read t1.txt" close $fd catchcmd ":memory:" ".read t1.txt" } {1 {Input nesting limit (25) reached at line 1. Check recursion.}} finish_test |
Changes to test/shell5.test.
︙ | ︙ | |||
17 18 19 20 21 22 23 | # Test plan: # # shell5-1.*: Basic tests specific to the ".import" command. # set testdir [file dirname $argv0] source $testdir/tester.tcl | | | 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | # Test plan: # # shell5-1.*: Basic tests specific to the ".import" command. # set testdir [file dirname $argv0] source $testdir/tester.tcl set CLI [test_cli_invocation] db close forcedelete test.db test.db-journal test.db-wal #---------------------------------------------------------------------------- # Test cases shell5-1.*: Basic handling of the .import and .separator commands. # |
︙ | ︙ |
Changes to test/shell8.test.
︙ | ︙ | |||
15 16 17 18 19 20 21 | set testdir [file dirname $argv0] source $testdir/tester.tcl set testprefix shell8 ifcapable !vtab { finish_test; return } | | | 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | set testdir [file dirname $argv0] source $testdir/tester.tcl set testprefix shell8 ifcapable !vtab { finish_test; return } set CLI [test_cli_invocation] # Check to make sure the shell has been compiled with ".archive" support. # if {[string match {*unknown command*} [catchcmd :memory: .archive]]} { finish_test; return } |
︙ | ︙ |
Changes to test/swarmvtab3.test.
︙ | ︙ | |||
144 145 146 147 148 149 150 151 152 153 | catch { array unset ::dbcache } # Set up 100 databases with filenames "remote_test.dbN", where N is a # random integer between 0 and 1,000,000 # 0 and 99. do_test 2.1 { for {set i 0} {$i < 100} {incr i} { while 1 { set ctx [expr abs(int(rand() *1000000))] | > | > | 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 | catch { array unset ::dbcache } # Set up 100 databases with filenames "remote_test.dbN", where N is a # random integer between 0 and 1,000,000 # 0 and 99. do_test 2.1 { catch { array unset ctx_used } for {set i 0} {$i < 100} {incr i} { while 1 { set ctx [expr abs(int(rand() *1000000))] if {[info exists ctx_used($ctx)]==0} break } set ctx_used($ctx) 1 set file test_remote.db$ctx forcedelete $file forcedelete test.db$i sqlite3 rrr $file rrr eval { CREATE TABLE t1(a INTEGER PRIMARY KEY, b); |
︙ | ︙ |
Changes to test/tester.tcl.
︙ | ︙ | |||
180 181 182 183 184 185 186 | if {[info exists ::env(ComSpec)]} { set comSpec $::env(ComSpec) } else { # NOTE: Hard-code the typical default value. set comSpec {C:\Windows\system32\cmd.exe} } return [string map [list \\ /] \ | | | 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 | if {[info exists ::env(ComSpec)]} { set comSpec $::env(ComSpec) } else { # NOTE: Hard-code the typical default value. set comSpec {C:\Windows\system32\cmd.exe} } return [string map [list \\ /] \ [string trim [exec -- $comSpec /c CD]]] } else { return [pwd] } } # Copy file $from into $to. This is used because some versions of # TCL for windows (notably the 8.4.1 binary package shipped with the |
︙ | ︙ | |||
2474 2475 2476 2477 2478 2479 2480 | finish_test return "" } return $ret } # Find the name of the 'shell' executable (e.g. "sqlite3.exe") to use for | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 2474 2475 2476 2477 2478 2479 2480 2481 2482 2483 2484 2485 2486 2487 2488 2489 2490 2491 2492 2493 2494 2495 2496 2497 2498 2499 2500 2501 2502 2503 2504 2505 2506 2507 2508 2509 2510 2511 2512 2513 2514 2515 2516 2517 2518 2519 2520 2521 2522 2523 2524 2525 2526 | finish_test return "" } return $ret } # Find the name of the 'shell' executable (e.g. "sqlite3.exe") to use for # the tests in shell*.test. If no such executable can be found, invoke # [finish_test ; return] in the callers context. # proc test_find_cli {} { set prog [test_find_binary sqlite3] if {$prog==""} { return -code return } return $prog } # Find invocation of the 'shell' executable (e.g. "sqlite3.exe") to use # for the tests in shell*.test with optional valgrind prefix when the # environment variable SQLITE_CLI_VALGRIND_OPT is set. The set value # operates as follows: # empty or 0 => no valgrind prefix; # 1 => valgrind options for memory leak check; # other => use value as valgrind options. # If shell not found, invoke [finish_test ; return] in callers context. # proc test_cli_invocation {} { set prog [test_find_binary sqlite3] if {$prog==""} { return -code return } set vgrun [expr {[permutation]=="valgrind"}] if {$vgrun || [info exists ::env(SQLITE_CLI_VALGRIND_OPT)]} { if {$vgrun} { set vgo "--quiet" } else { set vgo $::env(SQLITE_CLI_VALGRIND_OPT) } if {$vgo == 0 || $vgo eq ""} { return $prog } elseif {$vgo == 1} { return "valgrind --quiet --leak-check=yes $prog" } else { return "valgrind $vgo $prog" } } else { return $prog } } # Find the name of the 'sqldiff' executable (e.g. "sqlite3.exe") to use for # the tests in sqldiff tests. If no such executable can be found, invoke # [finish_test ; return] in the callers context. # proc test_find_sqldiff {} { set prog [test_find_binary sqldiff] |
︙ | ︙ |
Added test/upfrom4.test.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 | # 2022-05-24 # # 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 upfrom4 do_execsql_test 100 { DROP TABLE IF EXISTS t5; DROP TABLE IF EXISTS m1; DROP TABLE IF EXISTS m2; CREATE TABLE t5(a INTEGER PRIMARY KEY, b TEXT, c TEXT); CREATE TABLE m1(x INTEGER PRIMARY KEY, y TEXT); CREATE TABLE m2(u INTEGER PRIMARY KEY, v TEXT); INSERT INTO t5 VALUES(1, 'one', 'ONE'); INSERT INTO t5 VALUES(2, 'two', 'TWO'); INSERT INTO t5 VALUES(3, 'three', 'THREE'); INSERT INTO t5 VALUES(4, 'four', 'FOUR'); INSERT INTO m1 VALUES(1, 'i'); INSERT INTO m1 VALUES(2, 'ii'); INSERT INTO m1 VALUES(3, 'iii'); INSERT INTO m2 VALUES(1, 'I'); INSERT INTO m2 VALUES(3, 'II'); INSERT INTO m2 VALUES(4, 'III'); SELECT * FROM t5; } {1 one ONE 2 two TWO 3 three THREE 4 four FOUR} do_execsql_test 110 { BEGIN; UPDATE t5 SET b=y, c=v FROM m1 LEFT JOIN m2 ON (x=u) WHERE x=a; SELECT * FROM t5 ORDER BY a; ROLLBACK; } {1 i I 2 ii {} 3 iii II 4 four FOUR} do_execsql_test 120 { BEGIN; UPDATE t5 SET b=y, c=v FROM m2 RIGHT JOIN m1 ON (x=u) WHERE x=a; SELECT * FROM t5 ORDER BY a; ROLLBACK; } {1 i I 2 ii {} 3 iii II 4 four FOUR} reset_db db null - do_execsql_test 200 { CREATE TABLE t1(a INT PRIMARY KEY, b INT, c INT); INSERT INTO t1(a) VALUES(1),(2),(8),(19); CREATE TABLE c1(x INTEGER PRIMARY KEY, b INT); INSERT INTO c1(x,b) VALUES(1,1),(8,8),(17,17),(NULL,NULL); CREATE TABLE c2(x INT,c INT); INSERT INTO c2(x,c) VALUES(2,2),(8,8),(NULL,NULL); CREATE TABLE dual(dummy TEXT); INSERT INTO dual VALUES('X'); } {} do_execsql_test 210 { BEGIN; SELECT * FROM t1 ORDER BY a; UPDATE t1 SET b=c1.b, c=c2.c FROM dual, c1 NATURAL RIGHT JOIN c2 WHERE x=a; SELECT * FROM t1 ORDER BY a; ROLLBACK; } { 1 - - 2 - - 8 - - 19 - - 1 - - 2 - 2 8 8 8 19 - - } do_execsql_test 300 { CREATE TABLE t2(x); CREATE TRIGGER AFTER INSERT ON t2 BEGIN UPDATE t1 SET b=c1.b, c=c2.c FROM dual, c1 NATURAL RIGHT JOIN c2 WHERE x=a; END; } {} do_execsql_test 310 { BEGIN; SELECT * FROM t1 ORDER BY a; INSERT INTO t2(x) VALUES(1); SELECT * FROM t1 ORDER BY a; ROLLBACK; } { 1 - - 2 - - 8 - - 19 - - 1 - - 2 - 2 8 8 8 19 - - } # 2022-05-26 dbsqlfuzz crash-9401d6ba699f1257d352a657de236286bf2b14da # reset_db db null - do_execsql_test 400 { CREATE TABLE t2(x,y,z PRIMARY KEY) WITHOUT ROWID; INSERT INTO t2 VALUES(89,-89,6); CREATE TABLE t1(a INT,b TEXT,c TEXT,d REAL) STRICT; INSERT INTO t1 VALUES(1,'xyz','def',4.5); CREATE TRIGGER t1tr BEFORE UPDATE ON t1 BEGIN INSERT INTO t1(a,b) VALUES(1000,'uvw'); UPDATE t1 SET b=NULL FROM (SELECT CAST(a AS varchar) FROM t1 ORDER BY b) NATURAL LEFT FULL JOIN t1 AS text; END; UPDATE t1 SET b=b|100; SELECT * FROM t1 ORDER BY a; } { 1 100 def 4.5 1000 - - - } finish_test |
Changes to test/vtab6.test.
︙ | ︙ | |||
273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 | catchsql { SELECT * FROM t1 USING(a) } } {1 {a JOIN clause is required before USING}} do_test vtab6-3.6 { catchsql { SELECT * FROM t1 JOIN t2 ON t3.a=t2.b; } } {1 {no such column: t3.a}} do_test vtab6-3.7 { catchsql { SELECT * FROM t1 INNER OUTER JOIN t2; } } {1 {unknown join type: INNER OUTER}} do_test vtab6-3.7 { catchsql { SELECT * FROM t1 LEFT BOGUS JOIN t2; } } {1 {unknown join type: LEFT BOGUS}} do_test vtab6-4.1 { | > > > > | 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 | catchsql { SELECT * FROM t1 USING(a) } } {1 {a JOIN clause is required before USING}} do_test vtab6-3.6 { catchsql { SELECT * FROM t1 JOIN t2 ON t3.a=t2.b; } } {1 {no such column: t3.a}} # EVIDENCE-OF: R-47973-48020 you cannot say "INNER OUTER JOIN", because # that would be contradictory. do_test vtab6-3.7 { catchsql { SELECT * FROM t1 INNER OUTER JOIN t2; } } {1 {unknown join type: INNER OUTER}} do_test vtab6-3.7 { catchsql { SELECT * FROM t1 LEFT BOGUS JOIN t2; } } {1 {unknown join type: LEFT BOGUS}} do_test vtab6-4.1 { |
︙ | ︙ |
Changes to test/where.test.
1 2 3 4 5 6 7 8 9 10 11 12 13 | # 2001 September 15 # # 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 regression tests for SQLite library. The # focus of this file is testing the use of indices in WHERE clases. # | < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | # 2001 September 15 # # 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 regression tests for SQLite library. The # focus of this file is testing the use of indices in WHERE clases. # set testdir [file dirname $argv0] source $testdir/tester.tcl # Build some test data # do_test where-1.0 { |
︙ | ︙ | |||
1598 1599 1600 1601 1602 1603 1604 1605 1606 | INSERT INTO t1(a) VALUES(9223372036854775807); SELECT 1 FROM t1 WHERE a>=(9223372036854775807+1); } {} do_execsql_test where-27.2 { SELECT a>=9223372036854775807+1 FROM t1; } {0} } finish_test | > > > > > > > > > > > > > > > | 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 | INSERT INTO t1(a) VALUES(9223372036854775807); SELECT 1 FROM t1 WHERE a>=(9223372036854775807+1); } {} do_execsql_test where-27.2 { SELECT a>=9223372036854775807+1 FROM t1; } {0} } # 2022-05-10 dbsqlfuzz 4c5e3e89bc251d28378be88233f531b84ec66901 # reset_db do_execsql_test where-28.1 { CREATE TABLE t1(a INTEGER PRIMARY KEY, b INT); CREATE INDEX t1b ON t1(b,b,b,b,b,b,b,b,b,b,b,b,b); INSERT INTO t1(a,b) VALUES(1,1),(15,2),(19,5); UPDATE t1 SET b=999 WHERE a IN (SELECT 15) AND b IN (1,2); SELECT * FROM t1; } { 1 1 15 999 19 5 } finish_test |